rtree.h revision 7427525c28d58c423a68930160e3b0fe577fe953
1/*
2 * This radix tree implementation is tailored to the singular purpose of
3 * tracking which chunks are currently owned by jemalloc.  This functionality
4 * is mandatory for OS X, where jemalloc must be able to respond to object
5 * ownership queries.
6 *
7 *******************************************************************************
8 */
9#ifdef JEMALLOC_H_TYPES
10
11typedef struct rtree_s rtree_t;
12
13/*
14 * Size of each radix tree node (must be a power of 2).  This impacts tree
15 * depth.
16 */
17#if (LG_SIZEOF_PTR == 2)
18#  define RTREE_NODESIZE (1U << 14)
19#else
20#  define RTREE_NODESIZE CACHELINE
21#endif
22
23#endif /* JEMALLOC_H_TYPES */
24/******************************************************************************/
25#ifdef JEMALLOC_H_STRUCTS
26
27struct rtree_s {
28	malloc_mutex_t	mutex;
29	void		**root;
30	unsigned	height;
31	unsigned	level2bits[1]; /* Dynamically sized. */
32};
33
34#endif /* JEMALLOC_H_STRUCTS */
35/******************************************************************************/
36#ifdef JEMALLOC_H_EXTERNS
37
38rtree_t	*rtree_new(unsigned bits);
39
40#endif /* JEMALLOC_H_EXTERNS */
41/******************************************************************************/
42#ifdef JEMALLOC_H_INLINES
43
44#ifndef JEMALLOC_ENABLE_INLINE
45#ifndef JEMALLOC_DEBUG
46void	*rtree_get_locked(rtree_t *rtree, uintptr_t key);
47#endif
48void	*rtree_get(rtree_t *rtree, uintptr_t key);
49bool	rtree_set(rtree_t *rtree, uintptr_t key, void *val);
50#endif
51
52#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_))
53#define	RTREE_GET_GENERATE(f)						\
54/* The least significant bits of the key are ignored. */		\
55JEMALLOC_INLINE void *							\
56f(rtree_t *rtree, uintptr_t key)					\
57{									\
58	void *ret;							\
59	uintptr_t subkey;						\
60	unsigned i, lshift, height, bits;				\
61	void **node, **child;						\
62									\
63	RTREE_LOCK(&rtree->mutex);					\
64	for (i = lshift = 0, height = rtree->height, node = rtree->root;\
65	    i < height - 1;						\
66	    i++, lshift += bits, node = child) {			\
67		bits = rtree->level2bits[i];				\
68		subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \
69		    3)) - bits);					\
70		child = (void**)node[subkey];				\
71		if (child == NULL) {					\
72			RTREE_UNLOCK(&rtree->mutex);			\
73			return (NULL);					\
74		}							\
75	}								\
76									\
77	/*								\
78	 * node is a leaf, so it contains values rather than node	\
79	 * pointers.							\
80	 */								\
81	bits = rtree->level2bits[i];					\
82	subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -	\
83	    bits);							\
84	ret = node[subkey];						\
85	RTREE_UNLOCK(&rtree->mutex);					\
86									\
87	RTREE_GET_VALIDATE						\
88	return (ret);							\
89}
90
91#ifdef JEMALLOC_DEBUG
92#  define RTREE_LOCK(l)		malloc_mutex_lock(l)
93#  define RTREE_UNLOCK(l)	malloc_mutex_unlock(l)
94#  define RTREE_GET_VALIDATE
95RTREE_GET_GENERATE(rtree_get_locked)
96#  undef RTREE_LOCK
97#  undef RTREE_UNLOCK
98#  undef RTREE_GET_VALIDATE
99#endif
100
101#define	RTREE_LOCK(l)
102#define	RTREE_UNLOCK(l)
103#ifdef JEMALLOC_DEBUG
104   /*
105    * Suppose that it were possible for a jemalloc-allocated chunk to be
106    * munmap()ped, followed by a different allocator in another thread re-using
107    * overlapping virtual memory, all without invalidating the cached rtree
108    * value.  The result would be a false positive (the rtree would claim that
109    * jemalloc owns memory that it had actually discarded).  This scenario
110    * seems impossible, but the following assertion is a prudent sanity check.
111    */
112#  define RTREE_GET_VALIDATE						\
113	assert(rtree_get_locked(rtree, key) == ret);
114#else
115#  define RTREE_GET_VALIDATE
116#endif
117RTREE_GET_GENERATE(rtree_get)
118#undef RTREE_LOCK
119#undef RTREE_UNLOCK
120#undef RTREE_GET_VALIDATE
121
122JEMALLOC_INLINE bool
123rtree_set(rtree_t *rtree, uintptr_t key, void *val)
124{
125	uintptr_t subkey;
126	unsigned i, lshift, height, bits;
127	void **node, **child;
128
129	malloc_mutex_lock(&rtree->mutex);
130	for (i = lshift = 0, height = rtree->height, node = rtree->root;
131	    i < height - 1;
132	    i++, lshift += bits, node = child) {
133		bits = rtree->level2bits[i];
134		subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -
135		    bits);
136		child = (void**)node[subkey];
137		if (child == NULL) {
138			child = (void**)base_alloc(sizeof(void *) <<
139			    rtree->level2bits[i+1]);
140			if (child == NULL) {
141				malloc_mutex_unlock(&rtree->mutex);
142				return (true);
143			}
144			memset(child, 0, sizeof(void *) <<
145			    rtree->level2bits[i+1]);
146			node[subkey] = child;
147		}
148	}
149
150	/* node is a leaf, so it contains values rather than node pointers. */
151	bits = rtree->level2bits[i];
152	subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits);
153	node[subkey] = val;
154	malloc_mutex_unlock(&rtree->mutex);
155
156	return (false);
157}
158#endif
159
160#endif /* JEMALLOC_H_INLINES */
161/******************************************************************************/
162