1 /*
2 * aprsc
3 *
4 * (c) Matti Aarnio, OH2MQK, <oh2mqk@sral.fi>
5 *
6 * This program is licensed under the BSD license, which can be found
7 * in the file LICENSE.
8 *
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <pthread.h>
16 #include <sys/mman.h>
17 #include <fcntl.h>
18
19 #include "cellmalloc.h"
20 #include "hmalloc.h"
21 #include "hlog.h"
22
23 /*
24 * cellmalloc() -- manages arrays of cells of data
25 *
26 */
27
28 #ifndef _FOR_VALGRIND_
29 struct cellhead;
30
31 struct cellarena_t {
32 int cellsize;
33 int alignment;
34 int increment; /* alignment overhead applied.. */
35 int lifo_policy;
36 int minfree;
37 int use_mutex;
38
39 const char *arenaname;
40
41 pthread_mutex_t mutex;
42
43 struct cellhead *free_head;
44 struct cellhead *free_tail;
45
46 int freecount;
47 int createsize;
48
49 int cellblocks_count;
50 #define CELLBLOCKS_MAX 200 /* track client cell allocator limit! */
51 char *cellblocks[CELLBLOCKS_MAX]; /* ref as 'char pointer' for pointer arithmetics... */
52 };
53
54 #define CELLHEAD_DEBUG 0
55
56 struct cellhead {
57 #if CELLHEAD_DEBUG == 1
58 struct cellarena_t *ca;
59 #endif
60 struct cellhead *next;
61 };
62
63
64 /*
65 * new_cellblock() -- must be called MUTEX PROTECTED
66 *
67 */
68
new_cellblock(cellarena_t * ca)69 int new_cellblock(cellarena_t *ca)
70 {
71 int i;
72 char *cb;
73
74 #ifdef MEMDEBUG /* External backing-store files, unique ones for each cellblock,
75 which at Linux names memory blocks in /proc/nnn/smaps "file"
76 with this filename.. */
77 int fd;
78 char name[2048];
79
80 sprintf(name, "/tmp/.-%d-%s-%d.mmap", getpid(), ca->arenaname, ca->cellblocks_count );
81 unlink(name);
82 fd = open(name, O_RDWR|O_CREAT, 644);
83 unlink(name);
84 if (fd >= 0) {
85 memset(name, 0, sizeof(name));
86 i = 0;
87 while (i < ca->createsize) {
88 int rc = write(fd, name, sizeof(name));
89 if (rc < 0) break;
90 i += rc;
91 }
92 }
93
94 cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
95 close(fd);
96 #else
97
98 #ifndef MAP_ANON
99 # define MAP_ANON 0
100 #endif
101 cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
102 #endif
103 if (cb == NULL || cb == (char*)-1)
104 return -1;
105
106 if (ca->cellblocks_count >= CELLBLOCKS_MAX) return -1;
107
108 ca->cellblocks[ca->cellblocks_count++] = cb;
109
110 for (i = 0; i <= ca->createsize-ca->increment; i += ca->increment) {
111 struct cellhead *ch = (struct cellhead *)(cb + i); /* pointer arithmentic! */
112 if (!ca->free_head) {
113 ca->free_head = ch;
114 } else {
115 ca->free_tail->next = ch;
116 }
117 ca->free_tail = ch;
118 ch->next = NULL;
119 #if CELLHEAD_DEBUG == 1
120 ch->ca = ca; // cellhead pointer space
121 #endif
122
123 ca->freecount += 1;
124 }
125
126 // hlog( LOG_DEBUG, "new_cellblock(%p) of %dB freecount %d returns to %p/%p",
127 // ca, ca->cellsize, ca->freecount,
128 // __builtin_return_address(1), __builtin_return_address(2) );
129
130 return 0;
131 }
132
133
134
135 /*
136 * cellinit() -- the main program calls this once for each used cell type/size
137 *
138 */
139
140
cellinit(const char * arenaname,const int cellsize,const int alignment,const int policy,const int createkb,const int minfree)141 cellarena_t *cellinit( const char *arenaname, const int cellsize, const int alignment, const int policy, const int createkb, const int minfree )
142 {
143 cellarena_t *ca = hmalloc(sizeof(*ca));
144 int n;
145
146 memset(ca, 0, sizeof(*ca));
147
148 ca->arenaname = arenaname;
149
150 #if CELLHEAD_DEBUG == 1
151 if (alignment < __alignof__(void*))
152 alignment = __alignof__(void*); // cellhead pointer space
153 #endif
154
155 ca->cellsize = cellsize;
156 ca->alignment = alignment;
157 ca->minfree = minfree;
158 #if CELLHEAD_DEBUG == 1
159 ca->increment = cellsize + sizeof(void*); // cellhead pointer space
160 #else
161 ca->increment = cellsize;
162 #endif
163 if ((cellsize % alignment) != 0) {
164 ca->increment += alignment - cellsize % alignment;
165 }
166 ca->lifo_policy = policy & CELLMALLOC_POLICY_LIFO;
167 ca->use_mutex = (policy & CELLMALLOC_POLICY_NOMUTEX) ? 0 : 1;
168
169 ca->createsize = createkb * 1024;
170
171 n = ca->createsize / ca->increment;
172 hlog( LOG_DEBUG, "cellinit: %-12s block size %4d kB, cells/block: %d, %s", arenaname, createkb, n, ca->use_mutex ? "mutex" : "no mutex" );
173
174 pthread_mutex_init(&ca->mutex, NULL);
175
176 while (ca->freecount < ca->minfree)
177 new_cellblock(ca); /* more until minfree is full */
178
179 #if CELLHEAD_DEBUG == 1
180 hlog(LOG_DEBUG, "cellinit() cellhead=%p", ca);
181 #endif
182 return ca;
183 }
184
185
cellhead_to_clientptr(struct cellhead * ch)186 static void *cellhead_to_clientptr(struct cellhead *ch)
187 {
188 char *p = (char*)ch;
189 #if CELLHEAD_DEBUG == 1
190 p += sizeof(void*);
191 #endif
192 return p;
193 }
194
clientptr_to_cellhead(void * v)195 static struct cellhead *clientptr_to_cellhead(void *v)
196 {
197 #if CELLHEAD_DEBUG == 1
198 struct cellhead *ch = (struct cellhead *)(((char*)v) - sizeof(void*));
199 #else
200 struct cellhead *ch = (struct cellhead*)v;
201 #endif
202 return ch;
203 }
204
205
cellmalloc(cellarena_t * ca)206 void *cellmalloc(cellarena_t *ca)
207 {
208 void *cp;
209 struct cellhead *ch;
210 int me;
211
212 if (ca->use_mutex) {
213 if ((me = pthread_mutex_lock(&ca->mutex))) {
214 hlog(LOG_ERR, "cellmalloc: could not lock mutex: %s", strerror(me));
215 return NULL;
216 }
217 }
218
219 while (!ca->free_head || (ca->freecount < ca->minfree))
220 if (new_cellblock(ca)) {
221 if (ca->use_mutex) {
222 if ((me = pthread_mutex_unlock(&ca->mutex))) {
223 hlog(LOG_ERR, "cellmalloc: could not unlock mutex: %s", strerror(me));
224 }
225 }
226
227 return NULL;
228 }
229
230 /* Pick new one off the free-head ! */
231 ch = ca->free_head;
232 ca->free_head = ch->next;
233 ch->next = NULL;
234 cp = ch;
235 if (ca->free_head == NULL)
236 ca->free_tail = NULL;
237
238 ca->freecount -= 1;
239
240 if (ca->use_mutex) {
241 if ((me = pthread_mutex_unlock(&ca->mutex))) {
242 hlog(LOG_ERR, "cellmalloc: could not unlock mutex: %s", strerror(me));
243 }
244 }
245
246 // hlog(LOG_DEBUG, "cellmalloc(%p at %p) freecount %d", cellhead_to_clientptr(cp), ca, ca->freecount);
247
248 return cellhead_to_clientptr(cp);
249 }
250
251 /*
252 * cellmallocmany() -- give many cells in single lock region
253 *
254 */
255
cellmallocmany(cellarena_t * ca,void ** array,int numcells)256 int cellmallocmany(cellarena_t *ca, void **array, int numcells)
257 {
258 int count;
259 struct cellhead *ch;
260 int me;
261
262 if (ca->use_mutex) {
263 if ((me = pthread_mutex_lock(&ca->mutex))) {
264 hlog(LOG_ERR, "cellmallocmany: could not lock mutex: %s", strerror(me));
265 return 0;
266 }
267 }
268
269 for (count = 0; count < numcells; ++count) {
270
271 while (!ca->free_head ||
272 ca->freecount < ca->minfree) {
273 /* Out of free cells ? alloc new set */
274 if (new_cellblock(ca)) {
275 /* Failed ! */
276 hlog(LOG_ERR, "cellmallocmany: failed to allocate new block!");
277 break;
278 }
279 }
280
281 /* Pick new one off the free-head ! */
282
283 ch = ca->free_head;
284
285 // hlog( LOG_DEBUG, "cellmallocmany(%d of %d); freecount %d; %p at %p",
286 // count, numcells, ca->freecount, cellhead_to_clientptr(ch), ca );
287
288 if (!ch)
289 break; // Should not happen...
290
291 ca->free_head = ch->next;
292 ch->next = NULL;
293
294 if (ca->free_head == NULL)
295 ca->free_tail = NULL;
296
297 array[count] = cellhead_to_clientptr(ch);
298
299 ca->freecount -= 1;
300
301 }
302
303 if (ca->use_mutex) {
304 if ((me = pthread_mutex_unlock(&ca->mutex))) {
305 hlog(LOG_ERR, "cellmallocmany: could not unlock mutex: %s", strerror(me));
306 return count;
307 }
308 }
309
310 return count;
311 }
312
313
314
cellfree(cellarena_t * ca,void * p)315 void cellfree(cellarena_t *ca, void *p)
316 {
317 int me;
318 struct cellhead *ch = clientptr_to_cellhead(p);
319 ch->next = NULL;
320 #if CELLHEAD_DEBUG == 1
321 if (ch->ca != ca) {
322 hlog(LOG_ERR, "cellfree(%p to %p) wrong cellhead->ca pointer %p", p, ca, ch->ca);
323 }
324 #endif
325
326 // hlog(LOG_DEBUG, "cellfree() %p to %p", p, ca);
327
328 if (ca->use_mutex) {
329 if ((me = pthread_mutex_lock(&ca->mutex))) {
330 hlog(LOG_ERR, "cellfree: could not lock mutex: %s", strerror(me));
331 return;
332 }
333 }
334
335 if (ca->lifo_policy) {
336 /* Put the cell on free-head */
337 ch->next = ca->free_head;
338 ca->free_head = ch;
339
340 } else {
341 /* Put the cell on free-tail */
342 if (ca->free_tail)
343 ca->free_tail->next = ch;
344 ca->free_tail = ch;
345 if (!ca->free_head)
346 ca->free_head = ch;
347 ch->next = NULL;
348 }
349
350 ca->freecount += 1;
351
352 if (ca->use_mutex) {
353 if ((me = pthread_mutex_unlock(&ca->mutex))) {
354 hlog(LOG_ERR, "cellfree: could not unlock mutex: %s", strerror(me));
355 }
356 }
357 }
358
359 /*
360 * cellfreemany() -- release many cells in single lock region
361 *
362 */
363
cellfreemany(cellarena_t * ca,void ** array,int numcells)364 void cellfreemany(cellarena_t *ca, void **array, int numcells)
365 {
366 int count;
367 int me;
368
369 if (ca->use_mutex) {
370 if ((me = pthread_mutex_lock(&ca->mutex))) {
371 hlog(LOG_ERR, "cellfreemany: could not lock mutex: %s", strerror(me));
372 return;
373 }
374 }
375
376 for (count = 0; count < numcells; ++count) {
377
378 struct cellhead *ch = clientptr_to_cellhead(array[count]);
379
380 #if CELLHEAD_DEBUG == 1
381 if (ch->ca != ca) {
382 hlog(LOG_ERR, "cellfreemany(%p to %p) wrong cellhead->ca pointer %p", array[count], ca, ch->ca);
383 }
384 #endif
385
386 // hlog(LOG_DEBUG, "cellfreemany() %p to %p", ch, ca);
387
388 if (ca->lifo_policy) {
389 /* Put the cell on free-head */
390 ch->next = ca->free_head;
391 ca->free_head = ch;
392
393 } else {
394 /* Put the cell on free-tail */
395 if (ca->free_tail)
396 ca->free_tail->next = ch;
397 ca->free_tail = ch;
398 if (!ca->free_head)
399 ca->free_head = ch;
400 ch->next = NULL;
401 }
402
403 ca->freecount += 1;
404
405 }
406
407 if (ca->use_mutex) {
408 if ((me = pthread_mutex_unlock(&ca->mutex))) {
409 hlog(LOG_ERR, "cellfreemany: could not unlock mutex: %s", strerror(me));
410 }
411 }
412 }
413
cellstatus(cellarena_t * cellarena,struct cellstatus_t * status)414 void cellstatus(cellarena_t *cellarena, struct cellstatus_t *status)
415 {
416 /* TODO: try this for atomic cellstatus collection:
417 if (ca->use_mutex)
418 pthread_mutex_lock(&ca->mutex);
419 */
420 status->cellsize = cellarena->cellsize;
421 status->cellsize_aligned = cellarena->increment;
422 status->alignment = cellarena->alignment;
423 status->freecount = cellarena->freecount;
424 status->cellcount = (cellarena->createsize / cellarena->increment) * cellarena->cellblocks_count;
425 status->blocks = cellarena->cellblocks_count;
426 status->blocks_max = CELLBLOCKS_MAX;
427 status->block_size = cellarena->createsize;
428 /* and this:
429 if (ca->use_mutex)
430 pthread_mutex_unlock(&ca->mutex);
431 */
432 }
433
434 #endif /* (NOT) _FOR_VALGRIND_ */
435
436