xref: /freebsd/contrib/jemalloc/include/jemalloc/internal/rtree.h (revision 38f0b757fd84d17d0fc24739a7cda160c4516d81)
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 
11 typedef 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 #define	RTREE_NODESIZE (1U << 16)
18 
19 typedef void *(rtree_alloc_t)(size_t);
20 typedef void (rtree_dalloc_t)(void *);
21 
22 #endif /* JEMALLOC_H_TYPES */
23 /******************************************************************************/
24 #ifdef JEMALLOC_H_STRUCTS
25 
26 struct rtree_s {
27 	rtree_alloc_t	*alloc;
28 	rtree_dalloc_t	*dalloc;
29 	malloc_mutex_t	mutex;
30 	void		**root;
31 	unsigned	height;
32 	unsigned	level2bits[1]; /* Dynamically sized. */
33 };
34 
35 #endif /* JEMALLOC_H_STRUCTS */
36 /******************************************************************************/
37 #ifdef JEMALLOC_H_EXTERNS
38 
39 rtree_t	*rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc);
40 void	rtree_delete(rtree_t *rtree);
41 void	rtree_prefork(rtree_t *rtree);
42 void	rtree_postfork_parent(rtree_t *rtree);
43 void	rtree_postfork_child(rtree_t *rtree);
44 
45 #endif /* JEMALLOC_H_EXTERNS */
46 /******************************************************************************/
47 #ifdef JEMALLOC_H_INLINES
48 
49 #ifndef JEMALLOC_ENABLE_INLINE
50 #ifdef JEMALLOC_DEBUG
51 uint8_t rtree_get_locked(rtree_t *rtree, uintptr_t key);
52 #endif
53 uint8_t	rtree_get(rtree_t *rtree, uintptr_t key);
54 bool	rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val);
55 #endif
56 
57 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_))
58 #define	RTREE_GET_GENERATE(f)						\
59 /* The least significant bits of the key are ignored. */		\
60 JEMALLOC_INLINE uint8_t							\
61 f(rtree_t *rtree, uintptr_t key)					\
62 {									\
63 	uint8_t ret;							\
64 	uintptr_t subkey;						\
65 	unsigned i, lshift, height, bits;				\
66 	void **node, **child;						\
67 									\
68 	RTREE_LOCK(&rtree->mutex);					\
69 	for (i = lshift = 0, height = rtree->height, node = rtree->root;\
70 	    i < height - 1;						\
71 	    i++, lshift += bits, node = child) {			\
72 		bits = rtree->level2bits[i];				\
73 		subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR +	\
74 		    3)) - bits);					\
75 		child = (void**)node[subkey];				\
76 		if (child == NULL) {					\
77 			RTREE_UNLOCK(&rtree->mutex);			\
78 			return (0);					\
79 		}							\
80 	}								\
81 									\
82 	/*								\
83 	 * node is a leaf, so it contains values rather than node	\
84 	 * pointers.							\
85 	 */								\
86 	bits = rtree->level2bits[i];					\
87 	subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -	\
88 	    bits);							\
89 	{								\
90 		uint8_t *leaf = (uint8_t *)node;			\
91 		ret = leaf[subkey];					\
92 	}								\
93 	RTREE_UNLOCK(&rtree->mutex);					\
94 									\
95 	RTREE_GET_VALIDATE						\
96 	return (ret);							\
97 }
98 
99 #ifdef JEMALLOC_DEBUG
100 #  define RTREE_LOCK(l)		malloc_mutex_lock(l)
101 #  define RTREE_UNLOCK(l)	malloc_mutex_unlock(l)
102 #  define RTREE_GET_VALIDATE
103 RTREE_GET_GENERATE(rtree_get_locked)
104 #  undef RTREE_LOCK
105 #  undef RTREE_UNLOCK
106 #  undef RTREE_GET_VALIDATE
107 #endif
108 
109 #define	RTREE_LOCK(l)
110 #define	RTREE_UNLOCK(l)
111 #ifdef JEMALLOC_DEBUG
112    /*
113     * Suppose that it were possible for a jemalloc-allocated chunk to be
114     * munmap()ped, followed by a different allocator in another thread re-using
115     * overlapping virtual memory, all without invalidating the cached rtree
116     * value.  The result would be a false positive (the rtree would claim that
117     * jemalloc owns memory that it had actually discarded).  This scenario
118     * seems impossible, but the following assertion is a prudent sanity check.
119     */
120 #  define RTREE_GET_VALIDATE						\
121 	assert(rtree_get_locked(rtree, key) == ret);
122 #else
123 #  define RTREE_GET_VALIDATE
124 #endif
125 RTREE_GET_GENERATE(rtree_get)
126 #undef RTREE_LOCK
127 #undef RTREE_UNLOCK
128 #undef RTREE_GET_VALIDATE
129 
130 JEMALLOC_INLINE bool
131 rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val)
132 {
133 	uintptr_t subkey;
134 	unsigned i, lshift, height, bits;
135 	void **node, **child;
136 
137 	malloc_mutex_lock(&rtree->mutex);
138 	for (i = lshift = 0, height = rtree->height, node = rtree->root;
139 	    i < height - 1;
140 	    i++, lshift += bits, node = child) {
141 		bits = rtree->level2bits[i];
142 		subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -
143 		    bits);
144 		child = (void**)node[subkey];
145 		if (child == NULL) {
146 			size_t size = ((i + 1 < height - 1) ? sizeof(void *)
147 			    : (sizeof(uint8_t))) << rtree->level2bits[i+1];
148 			child = (void**)rtree->alloc(size);
149 			if (child == NULL) {
150 				malloc_mutex_unlock(&rtree->mutex);
151 				return (true);
152 			}
153 			memset(child, 0, size);
154 			node[subkey] = child;
155 		}
156 	}
157 
158 	/* node is a leaf, so it contains values rather than node pointers. */
159 	bits = rtree->level2bits[i];
160 	subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits);
161 	{
162 		uint8_t *leaf = (uint8_t *)node;
163 		leaf[subkey] = val;
164 	}
165 	malloc_mutex_unlock(&rtree->mutex);
166 
167 	return (false);
168 }
169 #endif
170 
171 #endif /* JEMALLOC_H_INLINES */
172 /******************************************************************************/
173