src/mempool.c

changeset 1323
deccdb82f24e
parent 1319
aa1f580f8f59
child 1324
399f7bb81d11
equal deleted inserted replaced
1322:7be10b57f658 1323:deccdb82f24e
29 #include "cx/mempool.h" 29 #include "cx/mempool.h"
30 30
31 #include <string.h> 31 #include <string.h>
32 #include <errno.h> 32 #include <errno.h>
33 33
34 struct cx_mempool_memory_s {
35 /** The destructor. */
36 cx_destructor_func destructor;
37 /** The actual memory. */
38 char c[];
39 };
40
41 static int cx_mempool_ensure_capacity( 34 static int cx_mempool_ensure_capacity(
42 struct cx_mempool_s *pool, 35 struct cx_mempool_s *pool,
43 size_t needed_capacity 36 size_t needed_capacity
44 ) { 37 ) {
45 if (needed_capacity <= pool->capacity) return 0; 38 if (needed_capacity <= pool->capacity) return 0;
46 size_t newcap = pool->capacity >= 1000 ? 39 size_t newcap = pool->capacity >= 1000 ?
47 pool->capacity + 1000 : pool->capacity * 2; 40 pool->capacity + 1000 : pool->capacity * 2;
48 size_t newmsize; 41 size_t newmsize;
49 if (pool->capacity > newcap || cx_szmul(newcap, 42 if (pool->capacity > newcap
50 sizeof(struct cx_mempool_memory_s*), &newmsize)) { 43 || cx_szmul(newcap, sizeof(void*), &newmsize)) {
51 errno = EOVERFLOW; 44 errno = EOVERFLOW;
52 return 1; 45 return 1;
53 } 46 }
54 struct cx_mempool_memory_s **newdata = cxRealloc( 47 void **newdata = cxReallocDefault(pool->data, newmsize);
55 cxDefaultAllocator, pool->data, newmsize);
56 if (newdata == NULL) return 1; 48 if (newdata == NULL) return 1;
57 pool->data = newdata; 49 pool->data = newdata;
58 pool->capacity = newcap; 50 pool->capacity = newcap;
59 return 0; 51 return 0;
60 } 52 }
61 53
62 static void *cx_mempool_malloc( 54 static int cx_mempool_ensure_registered_capacity(
55 struct cx_mempool_s *pool,
56 size_t needed_capacity
57 ) {
58 if (needed_capacity <= pool->registered_capacity) return 0;
59 // we do not expect so many registrations
60 size_t newcap = pool->registered_capacity + 8;
61 size_t newmsize;
62 if (pool->registered_capacity > newcap || cx_szmul(newcap,
63 sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) {
64 errno = EOVERFLOW;
65 return 1;
66 }
67 void *newdata = cxReallocDefault(pool->registered, newmsize);
68 if (newdata == NULL) return 1;
69 pool->registered = newdata;
70 pool->registered_capacity = newcap;
71 return 0;
72 }
73
74 static void *cx_mempool_malloc_simple(
63 void *p, 75 void *p,
64 size_t n 76 size_t n
65 ) { 77 ) {
66 struct cx_mempool_s *pool = p; 78 struct cx_mempool_s *pool = p;
67 79
68 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { 80 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
69 return NULL; 81 return NULL;
70 } 82 }
71 83
72 struct cx_mempool_memory_s *mem = cxMalloc( 84 struct cx_mempool_memory_s *mem =
73 cxDefaultAllocator, sizeof(cx_destructor_func) + n); 85 cxMallocDefault(sizeof(struct cx_mempool_memory_s) + n);
74 if (mem == NULL) return NULL; 86 if (mem == NULL) return NULL;
75 87 mem->destructor = NULL;
76 mem->destructor = pool->auto_destr;
77 pool->data[pool->size] = mem; 88 pool->data[pool->size] = mem;
78 pool->size++; 89 pool->size++;
79 90
80 return mem->c; 91 return mem->c;
81 } 92 }
82 93
83 static void *cx_mempool_calloc( 94 static void *cx_mempool_calloc_simple(
84 void *p, 95 void *p,
85 size_t nelem, 96 size_t nelem,
86 size_t elsize 97 size_t elsize
87 ) { 98 ) {
88 size_t msz; 99 size_t msz;
89 if (cx_szmul(nelem, elsize, &msz)) { 100 if (cx_szmul(nelem, elsize, &msz)) {
90 errno = EOVERFLOW; 101 errno = EOVERFLOW;
91 return NULL; 102 return NULL;
92 } 103 }
93 void *ptr = cx_mempool_malloc(p, msz); 104 void *ptr = cx_mempool_malloc_simple(p, msz);
94 if (ptr == NULL) return NULL; 105 if (ptr == NULL) return NULL;
95 memset(ptr, 0, nelem * elsize); 106 memset(ptr, 0, nelem * elsize);
96 return ptr; 107 return ptr;
97 } 108 }
98 109
99 static void *cx_mempool_realloc( 110 static void *cx_mempool_realloc_simple(
100 void *p, 111 void *p,
101 void *ptr, 112 void *ptr,
102 size_t n 113 size_t n
103 ) { 114 ) {
104 struct cx_mempool_s *pool = p; 115 struct cx_mempool_s *pool = p;
105 116
106 struct cx_mempool_memory_s *mem, *newm; 117 const unsigned overhead = sizeof(struct cx_mempool_memory_s);
107 mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); 118 struct cx_mempool_memory_s *mem =
108 newm = cxReallocDefault(mem, n + sizeof(cx_destructor_func)); 119 (void *) (((char *) ptr) - overhead);
120 struct cx_mempool_memory_s *newm =
121 cxReallocDefault(mem, n + overhead);
109 122
110 if (newm == NULL) return NULL; 123 if (newm == NULL) return NULL;
111 if (mem != newm) { 124 if (mem != newm) {
112 for (size_t i = 0; i < pool->size; i++) { 125 for (size_t i = 0; i < pool->size; i++) {
113 if (pool->data[i] == mem) { 126 if (pool->data[i] == mem) {
114 pool->data[i] = newm; 127 pool->data[i] = newm;
115 return ((char*)newm) + sizeof(cx_destructor_func); 128 return ((char*)newm) + overhead;
116 } 129 }
117 } 130 }
118 abort(); // LCOV_EXCL_LINE 131 abort(); // LCOV_EXCL_LINE
119 } else { 132 } else {
120 return ptr; 133 return ptr;
121 } 134 }
122 } 135 }
123 136
124 static void cx_mempool_free( 137 static void cx_mempool_free_simple(
125 void *p, 138 void *p,
126 void *ptr 139 void *ptr
127 ) { 140 ) {
128 if (!ptr) return; 141 if (!ptr) return;
129 struct cx_mempool_s *pool = p; 142 struct cx_mempool_s *pool = p;
130 143
131 struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) 144 struct cx_mempool_memory_s *mem =
132 ((char *) ptr - sizeof(cx_destructor_func)); 145 (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s));
133 146
134 for (size_t i = 0; i < pool->size; i++) { 147 for (size_t i = 0; i < pool->size; i++) {
135 if (mem == pool->data[i]) { 148 if (mem == pool->data[i]) {
136 if (mem->destructor) { 149 if (mem->destructor) {
137 mem->destructor(mem->c); 150 mem->destructor(mem->c);
151 }
152 if (pool->destr) {
153 pool->destr(mem->c);
154 }
155 if (pool->destr2) {
156 pool->destr2(pool->destr2_data, mem->c);
138 } 157 }
139 cxFreeDefault(mem); 158 cxFreeDefault(mem);
140 size_t last_index = pool->size - 1; 159 size_t last_index = pool->size - 1;
141 if (i != last_index) { 160 if (i != last_index) {
142 pool->data[i] = pool->data[last_index]; 161 pool->data[i] = pool->data[last_index];
147 } 166 }
148 } 167 }
149 abort(); // LCOV_EXCL_LINE 168 abort(); // LCOV_EXCL_LINE
150 } 169 }
151 170
171 static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) {
172 const bool has_destr = pool->destr;
173 const bool has_destr2 = pool->destr2;
174 for (size_t i = 0; i < pool->size; i++) {
175 struct cx_mempool_memory_s *mem = pool->data[i];
176 if (mem->destructor) {
177 mem->destructor(mem->c);
178 }
179 if (has_destr) {
180 pool->destr(mem->c);
181 }
182 if (has_destr2) {
183 pool->destr2(pool->destr2_data, mem->c);
184 }
185 cxFreeDefault(mem);
186 }
187 }
188
189 static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) {
190 for (size_t i = 0; i < pool->registered_size; i++) {
191 struct cx_mempool_foreign_memory_s info = pool->registered[i];
192 if (info.destr2_data == NULL) {
193 if (info.destr) {
194 info.destr(info.mem);
195 }
196 } else {
197 info.destr2(info.destr2_data, info.mem);
198 }
199 }
200 }
201
202 static cx_allocator_class cx_mempool_simple_allocator_class = {
203 cx_mempool_malloc_simple,
204 cx_mempool_realloc_simple,
205 cx_mempool_calloc_simple,
206 cx_mempool_free_simple
207 };
208
152 void cxMempoolFree(CxMempool *pool) { 209 void cxMempoolFree(CxMempool *pool) {
153 if (pool == NULL) return; 210 if (pool == NULL) return;
154 struct cx_mempool_memory_s *mem; 211 if (pool->allocator->cl == &cx_mempool_simple_allocator_class) {
155 for (size_t i = 0; i < pool->size; i++) { 212 cx_mempool_free_all_simple(pool);
156 mem = pool->data[i]; 213 } else {
157 if (mem->destructor) { 214 // TODO: implement
158 mem->destructor(mem->c); 215 }
159 } 216 cx_mempool_free_foreign(pool);
160 cxFreeDefault(mem);
161 }
162 cxFreeDefault(pool->data); 217 cxFreeDefault(pool->data);
218 cxFreeDefault(pool->registered);
163 cxFreeDefault((void*) pool->allocator); 219 cxFreeDefault((void*) pool->allocator);
164 cxFreeDefault(pool); 220 cxFreeDefault(pool);
165 } 221 }
166 222
167 void cxMempoolSetDestructor( 223 void cxMempoolSetDestructor(
171 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; 227 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
172 } 228 }
173 229
174 void cxMempoolRemoveDestructor(void *ptr) { 230 void cxMempoolRemoveDestructor(void *ptr) {
175 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; 231 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL;
176 }
177
178 struct cx_mempool_foreign_mem_s {
179 cx_destructor_func destr;
180 void* mem;
181 };
182
183 static void cx_mempool_destr_foreign_mem(void* ptr) {
184 struct cx_mempool_foreign_mem_s *fm = ptr;
185 fm->destr(fm->mem);
186 } 232 }
187 233
188 int cxMempoolRegister( 234 int cxMempoolRegister(
189 CxMempool *pool, 235 CxMempool *pool,
190 void *memory, 236 void *memory,
191 cx_destructor_func destr 237 cx_destructor_func destr
192 ) { 238 ) {
193 struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( 239 if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
194 pool, 240 return 1; // LCOV_EXCL_LINE
195 sizeof(struct cx_mempool_foreign_mem_s) 241 }
196 ); 242
197 if (fm == NULL) return 1; 243 pool->registered[pool->registered_size++] =
198 244 (struct cx_mempool_foreign_memory_s) {
199 fm->mem = memory; 245 .mem = memory,
200 fm->destr = destr; 246 .destr = destr,
201 *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; 247 .destr2_data = NULL
248 };
202 249
203 return 0; 250 return 0;
204 } 251 }
205
206 static cx_allocator_class cx_mempool_allocator_class = {
207 cx_mempool_malloc,
208 cx_mempool_realloc,
209 cx_mempool_calloc,
210 cx_mempool_free
211 };
212 252
213 CxMempool *cxMempoolCreate( 253 CxMempool *cxMempoolCreate(
214 size_t capacity, 254 size_t capacity,
215 cx_destructor_func destr 255 enum cx_mempool_type type
216 ) { 256 ) {
257 if (capacity == 0) capacity = 16;
217 size_t poolsize; 258 size_t poolsize;
218 if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { 259 if (cx_szmul(capacity, sizeof(void*), &poolsize)) {
219 errno = EOVERFLOW; 260 errno = EOVERFLOW;
220 return NULL; 261 return NULL;
221 } 262 }
222
223 struct cx_mempool_s *pool =
224 cxMallocDefault(sizeof(struct cx_mempool_s));
225 if (pool == NULL) return NULL;
226 263
227 CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator)); 264 CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator));
228 if (provided_allocator == NULL) { // LCOV_EXCL_START 265 if (provided_allocator == NULL) { // LCOV_EXCL_START
229 cxFreeDefault(pool);
230 return NULL; 266 return NULL;
231 } // LCOV_EXCL_STOP 267 } // LCOV_EXCL_STOP
232 provided_allocator->cl = &cx_mempool_allocator_class; 268
269 CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool));
270 if (pool == NULL) {
271 cxFreeDefault(provided_allocator);
272 return NULL;
273 }
274
233 provided_allocator->data = pool; 275 provided_allocator->data = pool;
234
235 pool->allocator = provided_allocator; 276 pool->allocator = provided_allocator;
277 if (type == CX_MEMPOOL_TYPE_SIMPLE) {
278 provided_allocator->cl = &cx_mempool_simple_allocator_class;
279 } else if (type == CX_MEMPOOL_TYPE_ADVANCED) {
280 // TODO: implement
281 } else {
282 // TODO: implement
283 }
236 284
237 pool->data = cxMallocDefault(poolsize); 285 pool->data = cxMallocDefault(poolsize);
238 if (pool->data == NULL) { // LCOV_EXCL_START 286 if (pool->data == NULL) { // LCOV_EXCL_START
239 cxFreeDefault(provided_allocator); 287 cxFreeDefault(provided_allocator);
240 cxFreeDefault(pool); 288 cxFreeDefault(pool);
241 return NULL; 289 return NULL;
242 } // LCOV_EXCL_STOP 290 } // LCOV_EXCL_STOP
243 291
244 pool->size = 0; 292 pool->size = 0;
245 pool->capacity = capacity; 293 pool->capacity = capacity;
246 pool->auto_destr = destr;
247 294
248 return pool; 295 return pool;
249 } 296 }
250 297
251 static void cx_mempool_free_transferred_allocator(void *al) { 298 static void cx_mempool_free_transferred_allocator(void *al) {
254 301
255 int cxMempoolTransfer( 302 int cxMempoolTransfer(
256 CxMempool *source, 303 CxMempool *source,
257 CxMempool *dest 304 CxMempool *dest
258 ) { 305 ) {
259 // safety check 306 // safety checks
260 if (source == dest) return 1; 307 if (source == dest) return 1;
308 if (source->allocator->cl != dest->allocator->cl) return 1;
261 309
262 // ensure enough capacity in the destination pool 310 // ensure enough capacity in the destination pool
263 if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { 311 if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) {
312 return 1; // LCOV_EXCL_LINE
313 }
314 if (cx_mempool_ensure_registered_capacity(dest,
315 dest->registered_size + source->registered_size)) {
264 return 1; // LCOV_EXCL_LINE 316 return 1; // LCOV_EXCL_LINE
265 } 317 }
266 318
267 // allocate a replacement allocator for the source pool 319 // allocate a replacement allocator for the source pool
268 CxAllocator *new_source_allocator = cxMallocDefault(sizeof(CxAllocator)); 320 CxAllocator *new_source_allocator = cxMallocDefault(sizeof(CxAllocator));
269 if (new_source_allocator == NULL) { // LCOV_EXCL_START 321 if (new_source_allocator == NULL) { // LCOV_EXCL_START
270 return 1; 322 return 1;
271 } // LCOV_EXCL_STOP 323 } // LCOV_EXCL_STOP
272 new_source_allocator->cl = &cx_mempool_allocator_class; 324 new_source_allocator->cl = source->allocator->cl;
273 new_source_allocator->data = source; 325 new_source_allocator->data = source;
274 326
275 // transfer all the data 327 // transfer all the data
276 memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size); 328 memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size);
277 dest->size += source->size; 329 dest->size += source->size;
330
331 // transfer all registered memory
332 memcpy(&dest->registered[dest->registered_size], source->registered,
333 sizeof(struct cx_mempool_foreign_memory_s) * source->size);
334 dest->registered_size += source->registered_size;
278 335
279 // register the old allocator with the new pool 336 // register the old allocator with the new pool
280 // we have to remove const-ness for this, but that's okay here 337 // we have to remove const-ness for this, but that's okay here
281 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; 338 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator;
282 transferred_allocator->data = dest; 339 transferred_allocator->data = dest;
283 cxMempoolRegister(dest, transferred_allocator, cx_mempool_free_transferred_allocator); 340 cxMempoolRegister(dest, transferred_allocator,
341 cx_mempool_free_transferred_allocator);
284 342
285 // prepare the source pool for re-use 343 // prepare the source pool for re-use
286 source->allocator = new_source_allocator; 344 source->allocator = new_source_allocator;
287 memset(source->data, 0, source->size * sizeof(source->data[0])); 345 memset(source->data, 0, source->size * sizeof(void*));
346 memset(source->registered, 0,
347 source->registered_size * sizeof(struct cx_mempool_foreign_memory_s));
288 source->size = 0; 348 source->size = 0;
349 source->registered_size = 0;
289 350
290 return 0; 351 return 0;
291 } 352 }
292 353
293 int cxMempoolTransferObject( 354 int cxMempoolTransferObject(

mercurial