Return aligned blocks on the smallest boundaries that are suitably
[yaz-moved-to-github.git] / src / nmem.c
1 /*
2  * Copyright (c) 1995-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: nmem.c,v 1.10 2005-01-04 21:27:54 mike Exp $
6  */
7
8 /**
9  * \file nmem.c
10  * \brief Implements Nibble Memory
11  *
12  * This is a simple and fairly wasteful little module for nibble memory
13  * allocation. Evemtually we'll put in something better.
14  *
15  * FIXME - it also has some semaphore stuff, and stuff to handle errno.
16  *         These should be moved to some other place!
17  */
18 #if HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <assert.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stddef.h>
26 #include <yaz/xmalloc.h>
27 #include <yaz/nmem.h>
28 #include <yaz/log.h>
29 #include <yaz/oid.h>
30
31 #ifdef WIN32
32 #include <windows.h>
33 #endif
34
35 #if YAZ_POSIX_THREADS
36 #include <pthread.h>
37 #endif
38
39 #if YAZ_GNU_THREADS
40 #include <pth.h>
41 #endif
42
43 #define NMEM_CHUNK (4*1024)
44
45 struct align {
46     char x;
47     union {
48         char c;
49         short s;
50         int i;
51         long l;
52         long long ll;
53         float f;
54         double d;
55     } u;
56 };
57
58 #define NMEM_ALIGN (offsetof(struct align, u))
59
60 static int log_level=0;
61 static int log_level_initialized=0;
62
63 #ifdef WIN32
64 static CRITICAL_SECTION critical_section;
65 #define NMEM_ENTER EnterCriticalSection(&critical_section)
66 #define NMEM_LEAVE LeaveCriticalSection(&critical_section)
67 struct nmem_mutex {
68     CRITICAL_SECTION m_handle;
69 };
70 #elif YAZ_POSIX_THREADS
71 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
72 #define NMEM_ENTER pthread_mutex_lock(&nmem_mutex);
73 #define NMEM_LEAVE pthread_mutex_unlock(&nmem_mutex);
74 struct nmem_mutex {
75     pthread_mutex_t m_handle;
76 };
77 #elif YAZ_GNU_THREADS
78 static pth_mutex_t nmem_mutex = PTH_MUTEX_INIT;
79 #define NMEM_ENTER pth_mutex_acquire(&nmem_mutex, 0, 0)
80 #define NMEM_LEAVE pth_mutex_release(&nmem_mutex)
81 struct nmem_mutex {
82     pth_mutex_t m_handle;
83 };
84 #else
85 #define NMEM_ENTER
86 #define NMEM_LEAVE
87 struct nmem_mutex {
88     int dummy;
89 };
90 #endif
91
92 YAZ_EXPORT void nmem_mutex_create(NMEM_MUTEX *p)
93 {
94     if (!log_level_initialized)
95     {
96         log_level=yaz_log_module_level("nmem");
97         log_level_initialized=1;
98     }
99
100     NMEM_ENTER;
101     if (!*p)
102     {
103         *p = (NMEM_MUTEX) malloc (sizeof(**p));
104 #ifdef WIN32
105         InitializeCriticalSection(&(*p)->m_handle);
106 #elif YAZ_POSIX_THREADS
107         pthread_mutex_init (&(*p)->m_handle, 0);
108 #elif YAZ_GNU_THREADS
109         pth_mutex_init (&(*p)->m_handle);
110 #endif
111     }
112     NMEM_LEAVE;
113 }
114
115 YAZ_EXPORT void nmem_mutex_enter(NMEM_MUTEX p)
116 {
117     if (p)
118     {
119 #ifdef WIN32
120         EnterCriticalSection(&p->m_handle);
121 #elif YAZ_POSIX_THREADS
122         pthread_mutex_lock(&p->m_handle);
123 #endif
124     }
125 }
126
127 YAZ_EXPORT void nmem_mutex_leave(NMEM_MUTEX p)
128 {
129     if (p)
130     {
131 #ifdef WIN32
132         LeaveCriticalSection(&p->m_handle);
133 #elif YAZ_POSIX_THREADS
134         pthread_mutex_unlock(&p->m_handle);
135 #endif
136     }
137 }
138
139 YAZ_EXPORT void nmem_mutex_destroy(NMEM_MUTEX *p)
140 {
141     NMEM_ENTER;
142     if (*p)
143     {
144 #ifdef WIN32
145         DeleteCriticalSection(&(*p)->m_handle);
146 #endif
147         free (*p);
148         *p = 0;
149     }
150     NMEM_LEAVE;
151 }
152
153 static nmem_block *freelist = NULL;        /* "global" freelists */
154 static nmem_control *cfreelist = NULL;
155 static int nmem_active_no = 0;
156 static int nmem_init_flag = 0;
157
158 #if NMEM_DEBUG
159 struct nmem_debug_info {
160     void *p;
161     char file[40];
162     int line;
163     struct nmem_debug_info *next;
164 };
165   
166 struct nmem_debug_info *nmem_debug_list = 0;  
167 #endif
168
169 static void free_block(nmem_block *p)
170 {  
171     memset(p->buf, 'Y', p->size);
172     p->next = freelist;
173     freelist = p;
174     if (log_level)
175         yaz_log (log_level, "nmem free_block p=%p", p);
176 }
177
178 #if NMEM_DEBUG
179 void nmem_print_list (void)
180 {
181     if(log_level)
182         nmem_print_list_l(log_level);
183 }
184
185 void nmem_print_list_l (int level)
186 {
187     struct nmem_debug_info *p;
188
189     yaz_log (level, "nmem print list");
190     NMEM_ENTER;
191     for (p = nmem_debug_list; p; p = p->next)
192         yaz_log (level, " %s:%d p=%p size=%d", p->file, p->line, p->p,
193                  nmem_total(p->p));
194     NMEM_LEAVE;
195 }
196 #endif
197 /*
198  * acquire a block with a minimum of size free bytes.
199  */
200 static nmem_block *get_block(int size)
201 {
202     nmem_block *r, *l;
203
204     if (log_level)
205         yaz_log (log_level, "nmem get_block size=%d", size);
206
207     for (r = freelist, l = 0; r; l = r, r = r->next)
208         if (r->size >= size)
209             break;
210     if (r)
211     {
212         if (log_level)
213             yaz_log (log_level, "nmem get_block found free block p=%p", r);
214         if (l)
215             l->next = r->next;
216         else
217             freelist = r->next;
218     }
219     else
220     {
221         int get = NMEM_CHUNK;
222
223         if (get < size)
224             get = size;
225         if(log_level)
226             yaz_log (log_level, "nmem get_block alloc new block size=%d", get);
227
228         r = (nmem_block *)xmalloc(sizeof(*r));
229         r->buf = (char *)xmalloc(r->size = get);
230     }
231     r->top = 0;
232     return r;
233 }
234
235 void nmem_reset(NMEM n)
236 {
237     nmem_block *t;
238     
239     yaz_log (log_level, "nmem_reset p=%p", n);
240     if (!n)
241         return;
242     NMEM_ENTER;
243     while (n->blocks)
244     {
245         t = n->blocks;
246         n->blocks = n->blocks->next;
247         free_block(t);
248     }
249     n->total = 0;
250     NMEM_LEAVE;
251 }
252
253 #if NMEM_DEBUG
254 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
255 #else
256 void *nmem_malloc(NMEM n, int size)
257 #endif
258 {
259     struct nmem_block *p;
260     char *r;
261
262 #if NMEM_DEBUG
263     if (log_level)
264         yaz_log (log_level, "%s:%d: nmem_malloc p=%p size=%d", 
265                 file, line, n, size);
266 #endif
267     if (!n)
268     {
269         yaz_log (YLOG_FATAL, "calling nmem_malloc with an null pointer");
270         abort ();
271     }
272 #ifdef WIN32
273     assert (nmem_init_flag);
274 #endif
275     NMEM_ENTER;
276     p = n->blocks;
277     if (!p || p->size - p->top < size)
278     {
279         p = get_block(size);
280         p->next = n->blocks;
281         n->blocks = p;
282     }
283     r = p->buf + p->top;
284     /* align size */
285     p->top += (size + (NMEM_ALIGN - 1)) & ~(NMEM_ALIGN - 1);
286     n->total += size;
287     NMEM_LEAVE;
288     return r;
289 }
290
291 int nmem_total(NMEM n)
292 {
293     return n->total;
294 }
295
296 #if NMEM_DEBUG
297 NMEM nmem_create_f(const char *file, int line)
298 #else
299 NMEM nmem_create(void)
300 #endif
301 {
302     NMEM r;
303 #if NMEM_DEBUG
304     struct nmem_debug_info *debug_p;
305 #endif
306     if (!log_level_initialized)
307     {
308         log_level=yaz_log_module_level("nmem");
309         log_level_initialized=1;
310     }
311     
312     NMEM_ENTER;
313     nmem_active_no++;
314     r = cfreelist;
315     if (r)
316         cfreelist = cfreelist->next;
317     else
318         r = (nmem_control *)xmalloc(sizeof(*r));
319     NMEM_LEAVE;
320
321 #if NMEM_DEBUG
322     yaz_log (YLOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
323                      nmem_active_no, r);
324 #endif
325     r->blocks = 0;
326     r->total = 0;
327     r->next = 0;
328
329 #if NMEM_DEBUG
330     for (debug_p = nmem_debug_list; debug_p; debug_p = debug_p->next)
331         if (debug_p->p == r)
332         {
333             yaz_log (YLOG_FATAL, "multi used block in nmem");
334             abort ();
335         }
336     debug_p = xmalloc (sizeof(*debug_p));
337     strncpy (debug_p->file, file, sizeof(debug_p->file)-1);
338     debug_p->file[sizeof(debug_p->file)-1] = '\0';
339     debug_p->line = line;
340     debug_p->p = r;
341     debug_p->next = nmem_debug_list;
342     nmem_debug_list = debug_p;
343
344     nmem_print_list();
345 #endif
346     return r;
347 }
348
349 #if NMEM_DEBUG
350 void nmem_destroy_f(const char *file, int line, NMEM n)
351 #else
352 void nmem_destroy(NMEM n)
353 #endif
354 {
355 #if NMEM_DEBUG
356     struct nmem_debug_info **debug_p;
357     int ok = 0;
358 #endif
359     if (!n)
360         return;
361     
362 #if NMEM_DEBUG
363     yaz_log (log_level, "%s:%d: nmem_destroy %d p=%p", file, line,
364                      nmem_active_no-1, n);
365     NMEM_ENTER;
366     for (debug_p = &nmem_debug_list; *debug_p; debug_p = &(*debug_p)->next)
367         if ((*debug_p)->p == n)
368         {
369             struct nmem_debug_info *debug_save = *debug_p;
370             *debug_p = (*debug_p)->next;
371             xfree (debug_save);
372             ok = 1;
373             break;
374         }
375     NMEM_LEAVE;
376     nmem_print_list();
377     if (!ok)
378     {
379         yaz_log (YLOG_WARN, "%s:%d destroying unallocated nmem block p=%p",
380                  file, line, n);
381         return;
382     }
383 #endif
384     nmem_reset(n);
385     NMEM_ENTER;
386     nmem_active_no--;
387     n->next = cfreelist;
388     cfreelist = n;
389     NMEM_LEAVE;
390 }
391
392 void nmem_transfer (NMEM dst, NMEM src)
393 {
394     nmem_block *t;
395     while ((t = src->blocks))
396     {
397         src->blocks = t->next;
398         t->next = dst->blocks;
399         dst->blocks = t;
400     }
401     dst->total += src->total;
402     src->total = 0;
403 }
404
405 void nmem_critical_enter (void)
406 {
407     NMEM_ENTER;
408 }
409
410 void nmem_critical_leave (void)
411 {
412     NMEM_LEAVE;
413 }
414
415 void nmem_init (void)
416 {
417     if (!log_level_initialized)
418     {
419         log_level=yaz_log_module_level("nmem");
420         log_level_initialized=1;
421     }
422     
423     if (++nmem_init_flag == 1)
424     {
425 #ifdef WIN32
426         InitializeCriticalSection(&critical_section);
427 #elif YAZ_GNU_THREADS
428         yaz_log (log_level, "pth_init"); /* ??? */
429         pth_init ();
430 #endif
431         nmem_active_no = 0;
432         freelist = NULL;
433         cfreelist = NULL;
434     }
435 }
436
437 void nmem_exit (void)
438 {
439     if (--nmem_init_flag == 0)
440     {
441         oid_exit();
442         while (freelist)
443         {
444             struct nmem_block *fl = freelist;
445             freelist = freelist->next;
446             xfree (fl->buf);
447             xfree (fl);
448         }
449         while (cfreelist)
450         {
451             struct nmem_control *cfl = cfreelist;
452             cfreelist = cfreelist->next;
453             xfree (cfl);
454         }
455 #ifdef WIN32
456         DeleteCriticalSection(&critical_section);
457 #endif
458     }
459 }
460
461
462 #ifdef WIN32
463 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
464                      DWORD reason,
465                      LPVOID reserved)
466 {
467     switch (reason)
468     {
469     case DLL_PROCESS_ATTACH:
470         nmem_init ();
471         break;
472     case DLL_PROCESS_DETACH:
473         nmem_exit ();
474     }
475     return TRUE;
476 }
477 #endif
478
479 int yaz_errno(void)
480 {
481     return errno;
482 }
483
484 void yaz_set_errno(int v)
485 {
486     errno = v;
487 }
488
489 void yaz_strerror(char *buf, int max)
490 {
491 #ifdef WIN32
492     DWORD err;
493 #endif
494     char *cp;
495     if (!log_level_initialized)
496     {
497         log_level = yaz_log_module_level("nmem");
498         log_level_initialized = 1;
499     }
500     
501 #ifdef WIN32
502     err = GetLastError();
503     if (err)
504     {
505         FormatMessage(
506                 FORMAT_MESSAGE_FROM_SYSTEM,
507                 NULL,
508                 err,
509                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */
510                 (LPTSTR) buf,
511                 max-1,
512                 NULL);
513     }
514     else
515         *buf = '\0';
516 #else
517 /* UNIX */
518 #if HAVE_STRERROR_R
519 #if YAZ_POSIX_THREADS
520     *buf = '\0';
521     strerror_r(errno, buf, max);
522     /* if buffer is unset - use strerror anyway (GLIBC bug) */
523     if (*buf == '\0')
524         strcpy(buf, strerror(yaz_errno()));
525 #else
526     strcpy(buf, strerror(yaz_errno()));
527 #endif
528 #else
529     strcpy(buf, strerror(yaz_errno()));
530 #endif
531 /* UNIX */
532 #endif
533     if ((cp = strrchr(buf, '\n')))
534         *cp = '\0';
535     if ((cp = strrchr(buf, '\r')))
536         *cp = '\0';
537 }