-
Notifications
You must be signed in to change notification settings - Fork 0
/
gx_pool.h
259 lines (249 loc) · 20.7 KB
/
gx_pool.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#ifndef _GX_POOL_H
#define _GX_POOL_H
#include "./gx_error.h"
// This macros is provided to help defining the intrusive fields required for objects
// allocated by a memory pool.
#define GX_POOL_OBJECT(TYPE) \
struct { \
TYPE * _prev; \
TYPE * _next; \
}
// This macros is provided to help defining the intrusive fields required for objects
// allocated by a reference counted memory pool.
#define GX_POOL_REFC_OBJECT(TYPE) \
struct { \
TYPE * _prev; \
TYPE * _next; \
void * _pool; \
size_t _refc; \
}
// This macros is intended to be used as EXTRA parameter to gx_pool_init_full when the
// memory pool allocates reference counted objects.
// Reference counted objects have to provide a "void *_pool" and "size_t _refc" fields
#define GX_POOL_REFC(TYPE, CONSTRUCT) \
\
static inline TYPE *acquire_ ## TYPE(TYPE ## _pool *pool) { \
TYPE *res = NULL; \
pthread_mutex_lock(&(pool->mutex)); \
if(rare(!pool->available_head)) \
if(TYPE ## _pool_extend(pool, pool->total_items) == -1) goto fin; \
res = pool->available_head; \
pool->available_head = res->_next; \
_prepend_ ## TYPE(pool, res); \
fin: \
pthread_mutex_unlock(&(pool->mutex)); \
if(freq(res != NULL)) { \
if(rare(CONSTRUCT(res) != 0)) { \
release_ ## TYPE(pool, res); \
res = NULL; \
} else { \
res->_refc = 1; \
res->_pool = pool; \
} \
} \
return res; \
} \
\
static inline void TYPE ## _incr_refc(TYPE *entry) { \
++(entry->_refc); \
} \
\
static inline void TYPE ## _decr_refc(TYPE *entry) { \
if((--(entry->_refc)) == 0) { \
release_ ## TYPE(entry->_pool, entry); \
} \
}
// This is macro is intended to be used as EXTRA parameter to gx_pool_init_full when the
// memory pool allocates simple objects.
#define GX_POOL_SIMPLE(TYPE, CONSTRUCT) \
\
static inline TYPE *acquire_ ## TYPE(TYPE ## _pool *pool) { \
TYPE *res = NULL; \
pthread_mutex_lock(&(pool->mutex)); \
if(rare(!pool->available_head)) \
if(TYPE ## _pool_extend(pool, pool->total_items) == -1) goto fin; \
res = pool->available_head; \
pool->available_head = res->_next; \
_prepend_ ## TYPE(pool, res); \
fin: \
pthread_mutex_unlock(&(pool->mutex)); \
if(freq(res != NULL)) { \
if(rare(CONSTRUCT(res) != 0)) { \
release_ ## TYPE(pool, res); \
res = NULL; \
} \
} \
return res; \
}
// TYPE must be a type that has a "_next" and "_prev" member that is a pointer to the same
// type. Also, the struct should be aligned (?).
#define gx_pool_init_full(TYPE, ALLOCATE, DEALLOCATE, CONSTRUCT, DESTROY, EXTRA) \
\
typedef struct pool_memory_segment ## TYPE { \
struct pool_memory_segment ## TYPE *next; \
TYPE *segment; \
} pool_memory_segment ## TYPE; \
\
typedef struct TYPE ## _pool { \
pthread_mutex_t mutex; \
size_t total_items; \
TYPE *available_head; \
TYPE *active_head; \
TYPE *active_tail; \
TYPE *prereleased[0x10000]; \
pool_memory_segment ## TYPE *memseg_head; \
} TYPE ## _pool; \
\
static int TYPE ## _pool_extend(TYPE ## _pool *pool, size_t by_number); \
static inline TYPE ## _pool *new_ ## TYPE ## _pool (size_t initial_number) { \
TYPE ## _pool *res; \
_N(res = (TYPE ## _pool *)malloc(sizeof(TYPE ## _pool))) _raise(NULL); \
memset(res, 0, sizeof(TYPE ## _pool)); \
_ (pthread_mutex_init(&(res->mutex), NULL)) { \
free(res); \
_raise(NULL); \
} \
TYPE ## _pool_extend(res, initial_number); \
return res; \
} \
\
static inline void destroy_ ## TYPE ## _pool(TYPE ## _pool *pool) { \
pool_memory_segment ## TYPE * seg1; \
pool_memory_segment ## TYPE * seg2; \
TYPE *ptr; \
if(pool != NULL) { \
pthread_mutex_lock(&(pool->mutex)); \
for(ptr = pool->available_head; ptr != NULL; ptr = ptr->_next) { \
DEALLOCATE(ptr); \
} \
seg1 = pool->memseg_head; \
while(seg1 != NULL) { \
seg2 = seg1; \
seg1 = seg1->next; \
free(seg2->segment); \
free(seg2); \
} \
pthread_mutex_unlock(&(pool->mutex)); \
} \
} \
\
static int TYPE ## _pool_extend(TYPE ## _pool *pool, size_t by_number) { \
TYPE *new_seg; \
TYPE *prev_seg = NULL; \
size_t curr; \
\
pool_memory_segment ## TYPE *memseg_entry; \
_N(new_seg = (TYPE *)malloc(sizeof(TYPE) * by_number)) _raise(-1); \
\
/* Link to memory-segments for freeing later */ \
_N(memseg_entry = (pool_memory_segment ## TYPE *) \
malloc(sizeof(pool_memory_segment ## TYPE))) _raise(-1); \
memseg_entry->segment = new_seg; \
memseg_entry->next = pool->memseg_head; \
\
/* Link them up */ \
for(curr = 0; curr < by_number; ++curr) { \
if(rare(ALLOCATE(new_seg + curr) != 0)) { \
while(curr-- != 0) { \
DEALLOCATE(new_seg + curr); \
} \
free(memseg_entry); \
free(new_seg); \
_raise(-1); \
} \
new_seg[curr]._prev = prev_seg; \
new_seg[curr]._next = new_seg + (curr + 1); \
prev_seg = new_seg + curr; \
} \
\
if(pool->available_head != NULL) { \
pool->available_head->_prev = new_seg + (by_number - 1); \
} \
\
new_seg[by_number - 1]._next = pool->available_head; \
pool->available_head = new_seg; \
pool->total_items += by_number; \
pool->memseg_head = memseg_entry; \
return 0; \
} \
\
static inline void _prepend_ ## TYPE (TYPE ## _pool *pool, TYPE *entry) { \
/* put an object at the front of the active list */ \
entry->_next = pool->active_head; \
if (freq(pool->active_head != NULL)) { pool->active_head->_prev = entry; } \
pool->active_head = entry; \
if (rare(pool->active_tail == NULL)) { pool->active_tail = entry; } \
} \
\
static inline void _remove_ ## TYPE(TYPE ## _pool *pool, TYPE *entry) { \
/* remove an object from the active list */ \
if (freq(entry->_prev != NULL)) { entry->_prev->_next = entry->_next; } \
else { pool->active_head = entry->_next; } \
if (freq(entry->_next != NULL)) { entry->_next->_prev = entry->_prev; } \
else { pool->active_tail = entry->_prev; } \
} \
\
static inline void prerelease_ ## TYPE(TYPE ## _pool *pool, TYPE *entry) { \
pthread_mutex_lock(&(pool->mutex)); \
pid_t cpid; \
cpid = syscall(SYS_getpid); \
unsigned int idx = (unsigned int)cpid & 0xffff; \
if(rare(pool->prereleased[idx])) \
log_error("Snap, prerelease snafu: %d", idx); \
pool->prereleased[idx] = entry; \
pthread_mutex_unlock(&(pool->mutex)); \
} \
\
static inline void finrelease_ ## TYPE(TYPE ## _pool *pool, pid_t cpid) { \
pthread_mutex_lock(&(pool->mutex)); \
unsigned int idx = (unsigned int)cpid & 0xffff; \
TYPE *entry = pool->prereleased[idx]; \
if(rare(!entry)) log_error("Snap, prerelease snafu: %d", idx); \
_remove_ ## TYPE(pool, entry); \
entry->_next = pool->available_head; \
pool->available_head = entry; \
pool->prereleased[idx] = NULL; \
pthread_mutex_unlock(&(pool->mutex)); \
} \
\
static inline void release_ ## TYPE(TYPE ## _pool *pool, TYPE *entry) { \
DESTROY(entry); \
pthread_mutex_lock(&(pool->mutex)); \
_remove_ ## TYPE(pool, entry); \
entry->_next = pool->available_head; \
pool->available_head = entry; \
pthread_mutex_unlock(&(pool->mutex)); \
} \
\
static inline void move_to_front_ ## TYPE(TYPE ## _pool *pool, TYPE *entry) { \
/* move an object to the front of the active list */ \
_remove_ ## TYPE(pool, entry); \
_prepend_ ## TYPE(pool, entry); \
} \
\
EXTRA
#define gx_pool_init_refc(TYPE, ALLOCATE, DEALLOCATE, CONSTRUCT, DESTROY) \
gx_pool_init_full(TYPE, \
ALLOCATE, \
DEALLOCATE, \
CONSTRUCT, \
DESTROY, \
GX_POOL_REFC(TYPE, CONSTRUCT))
#define gx_pool_init_simple(TYPE, ALLOCATE, DEALLOCATE, CONSTRUCT, DESTROY) \
gx_pool_init_full(TYPE, \
ALLOCATE, \
DEALLOCATE, \
CONSTRUCT, \
DESTROY, \
GX_POOL_SIMPLE(TYPE, CONSTRUCT))
#define gx_pool_init(TYPE) \
static inline int _allocate_ ## TYPE(TYPE *object) { (void)object; return 0; } \
static inline int _deallocate_ ## TYPE(TYPE *object) { (void)object; return 0; } \
static inline int _construct_ ## TYPE(TYPE *object) { (void)object; return 0; } \
static inline int _destroy_ ## TYPE(TYPE *object) { (void)object; return 0; } \
gx_pool_init_simple(TYPE, \
_allocate_ ## TYPE, \
_deallocate_ ## TYPE, \
_construct_ ## TYPE, \
_destroy_ ## TYPE)
#endif