Separate malloc debug library. Removal of ASN_COMPILED-#ifdefs.
[yaz-moved-to-github.git] / util / nmem.c
1 /*
2  * Copyright (c) 1995-2001, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: nmem.c,v $
7  * Revision 1.32  2001-11-13 23:00:43  adam
8  * Separate malloc debug library. Removal of ASN_COMPILED-#ifdefs.
9  *
10  * Revision 1.31  2001/10/24 12:24:43  adam
11  * WIN32 updates: ZOOM runs, nmem_init/nmem_exit called in DllMain.
12  *
13  * Revision 1.30  2001/10/05 13:55:17  adam
14  * Added defines YAZ_GNU_THREADS, YAZ_POSIX_THREADS in code and yaz-config
15  *
16  * Revision 1.29  2001/10/04 00:37:58  adam
17  * Fixes for GNU threads (not working yet).
18  *
19  * Revision 1.28  2001/10/03 23:55:18  adam
20  * GNU threads support.
21  *
22  * Revision 1.27  2001/09/27 12:09:18  adam
23  * Function nmem_exit calls oid_exit (when reference is 0).
24  *
25  * Revision 1.26  2001/07/19 19:51:42  adam
26  * Added typecasts to make C++ happy.
27  *
28  * Revision 1.25  2001/06/26 14:11:27  adam
29  * Added MUTEX functions for NMEM module (used by OID utility).
30  *
31  * Revision 1.24  2000/05/11 14:37:55  adam
32  * Minor changes.
33  *
34  * Revision 1.23  2000/05/09 10:55:05  adam
35  * Public nmem_print_list (for debugging).
36  *
37  * Revision 1.22  2000/05/03 22:00:00  adam
38  * Reference counter (if multiple modules are init/freeing nmem).
39  *
40  * Revision 1.21  2000/02/29 13:44:55  adam
41  * Check for config.h (currently not generated).
42  *
43  * Revision 1.20  2000/01/06 14:59:13  adam
44  * Added oid_init/oid_exit. Changed oid_exit.
45  *
46  * Revision 1.19  1999/11/30 13:47:12  adam
47  * Improved installation. Moved header files to include/yaz.
48  *
49  * Revision 1.18  1999/08/27 09:40:32  adam
50  * Renamed logf function to yaz_log. Removed VC++ project files.
51  *
52  * Revision 1.17  1999/07/13 13:28:25  adam
53  * Better debugging for NMEM routines.
54  *
55  * Revision 1.16  1999/03/31 11:18:25  adam
56  * Implemented odr_strdup. Added Reference ID to backend server API.
57  *
58  * Revision 1.15  1999/02/11 09:10:26  adam
59  * Function nmem_init only mandatory on Windows.
60  *
61  * Revision 1.14  1999/02/02 13:57:40  adam
62  * Uses preprocessor define WIN32 instead of WINDOWS to build code
63  * for Microsoft WIN32.
64  *
65  * Revision 1.13  1998/10/19 15:24:21  adam
66  * New nmem utility, nmem_transfer, that transfer blocks from one
67  * NMEM to another.
68  *
69  * Revision 1.12  1998/10/13 16:00:18  adam
70  * Implemented nmem_critical_{enter,leave}.
71  *
72  * Revision 1.11  1998/08/21 14:13:36  adam
73  * Added GNU Configure script to build Makefiles.
74  *
75  * Revision 1.10  1998/07/20 12:35:57  adam
76  * Added more memory diagnostics (when NMEM_DEBUG is 1).
77  *
78  * Revision 1.9  1998/07/07 15:49:01  adam
79  * Reduced chunk size.
80  *
81  * Revision 1.8  1998/07/03 14:21:27  adam
82  * Added critical sections for pthreads-library. Thanks to Ian Ibbotson,
83  * Fretwell Downing Informatics.
84  *
85  * Revision 1.7  1998/02/11 11:53:36  adam
86  * Changed code so that it compiles as C++.
87  *
88  * Revision 1.6  1997/10/31 12:20:09  adam
89  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
90  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
91  * Bug fix: missing fclose in data1_read_espec1.
92  *
93  * Revision 1.5  1997/10/06 09:09:52  adam
94  * Function mmem_exit releases memory used by the freelists.
95  *
96  * Revision 1.4  1997/09/29 07:12:50  adam
97  * NMEM thread safe. NMEM must be initialized before use (sigh) -
98  * routine nmem_init/nmem_exit implemented.
99  *
100  * Revision 1.3  1997/07/21 12:47:38  adam
101  * Moved definition of nmem_control and nmem_block.
102  *
103  * Revision 1.2  1995/12/13 13:44:37  quinn
104  * Modified Data1-system to use nmem
105  *
106  * Revision 1.1  1995/11/13  09:27:52  quinn
107  * Fiddling with the variant stuff.
108  *
109  *
110  */
111
112 /*
113  * This is a simple and fairly wasteful little module for nibble memory
114  * allocation. Evemtually we'll put in something better.
115  */
116 #if HAVE_CONFIG_H
117 #include <config.h>
118 #endif
119
120 #include <assert.h>
121 #include <string.h>
122 #include <yaz/xmalloc.h>
123 #include <yaz/nmem.h>
124 #include <yaz/log.h>
125 #include <yaz/oid.h>
126
127 #ifdef WIN32
128 #include <windows.h>
129 #endif
130
131 #if YAZ_POSIX_THREADS
132 #include <pthread.h>
133 #endif
134
135 #if YAZ_GNU_THREADS
136 #include <pth.h>
137 #endif
138
139 #define NMEM_CHUNK (4*1024)
140
141 #ifdef WIN32
142 static CRITICAL_SECTION critical_section;
143 #define NMEM_ENTER EnterCriticalSection(&critical_section)
144 #define NMEM_LEAVE LeaveCriticalSection(&critical_section)
145 struct nmem_mutex {
146     CRITICAL_SECTION m_handle;
147 };
148 #elif YAZ_POSIX_THREADS
149 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
150 #define NMEM_ENTER pthread_mutex_lock(&nmem_mutex);
151 #define NMEM_LEAVE pthread_mutex_unlock(&nmem_mutex);
152 struct nmem_mutex {
153     pthread_mutex_t m_handle;
154 };
155 #elif YAZ_GNU_THREADS
156 static pth_mutex_t nmem_mutex = PTH_MUTEX_INIT;
157 #define NMEM_ENTER pth_mutex_acquire(&nmem_mutex, 0, 0)
158 #define NMEM_LEAVE pth_mutex_release(&nmem_mutex)
159 struct nmem_mutex {
160     pth_mutex_t m_handle;
161 };
162 #else
163 #define NMEM_ENTER
164 #define NMEM_LEAVE
165 struct nmem_mutex {
166     int dummy;
167 };
168 #endif
169
170 YAZ_EXPORT void nmem_mutex_create(NMEM_MUTEX *p)
171 {
172     NMEM_ENTER;
173     if (!*p)
174     {
175         *p = (NMEM_MUTEX) malloc (sizeof(**p));
176 #ifdef WIN32
177         InitializeCriticalSection(&(*p)->m_handle);
178 #elif YAZ_POSIX_THREADS
179         pthread_mutex_init (&(*p)->m_handle, 0);
180 #elif YAZ_GNU_THREADS
181         pth_mutex_init (&(*p)->m_handle);
182 #endif
183     }
184     NMEM_LEAVE;
185 }
186
187 YAZ_EXPORT void nmem_mutex_enter(NMEM_MUTEX p)
188 {
189     if (p)
190     {
191 #ifdef WIN32
192         EnterCriticalSection(&p->m_handle);
193 #elif YAZ_POSIX_THREADS
194         pthread_mutex_lock(&p->m_handle);
195 #endif
196     }
197 }
198
199 YAZ_EXPORT void nmem_mutex_leave(NMEM_MUTEX p)
200 {
201     if (p)
202     {
203 #ifdef WIN32
204         LeaveCriticalSection(&p->m_handle);
205 #elif YAZ_POSIX_THREADS
206         pthread_mutex_unlock(&p->m_handle);
207 #endif
208     }
209 }
210
211 YAZ_EXPORT void nmem_mutex_destroy(NMEM_MUTEX *p)
212 {
213     NMEM_ENTER;
214     if (*p)
215     {
216 #ifdef WIN32
217         DeleteCriticalSection(&(*p)->m_handle);
218 #endif
219         free (*p);
220         *p = 0;
221     }
222     NMEM_LEAVE;
223 }
224
225 static nmem_block *freelist = NULL;        /* "global" freelists */
226 static nmem_control *cfreelist = NULL;
227 static int nmem_active_no = 0;
228 #ifdef WIN32
229 static int nmem_init_flag = 0;
230 #else
231 static int nmem_init_flag = 1;
232 #endif
233
234 #if NMEM_DEBUG
235 struct nmem_debug_info {
236     void *p;
237     char file[40];
238     int line;
239     struct nmem_debug_info *next;
240 };
241   
242 struct nmem_debug_info *nmem_debug_list = 0;  
243 #endif
244
245 static void free_block(nmem_block *p)
246 {  
247     p->next = freelist;
248     freelist = p;
249 #if NMEM_DEBUG
250     yaz_log (LOG_DEBUG, "nmem free_block p=%p", p);
251 #endif
252 }
253
254 #if NMEM_DEBUG
255 void nmem_print_list (void)
256 {
257     struct nmem_debug_info *p;
258
259     yaz_log (LOG_DEBUG, "nmem print list");
260     NMEM_ENTER;
261     for (p = nmem_debug_list; p; p = p->next)
262         yaz_log (LOG_DEBUG, " %s:%d p=%p size=%d", p->file, p->line, p->p,
263                  nmem_total(p->p));
264     NMEM_LEAVE;
265 }
266 #endif
267 /*
268  * acquire a block with a minimum of size free bytes.
269  */
270 static nmem_block *get_block(int size)
271 {
272     nmem_block *r, *l;
273
274 #if NMEM_DEBUG
275     yaz_log (LOG_DEBUG, "nmem get_block size=%d", size);
276 #endif
277     for (r = freelist, l = 0; r; l = r, r = r->next)
278         if (r->size >= size)
279             break;
280     if (r)
281     {
282 #if NMEM_DEBUG
283         yaz_log (LOG_DEBUG, "nmem get_block found free block p=%p", r);
284 #endif
285         if (l)
286             l->next = r->next;
287         else
288             freelist = r->next;
289     }
290     else
291     {
292         int get = NMEM_CHUNK;
293
294         if (get < size)
295             get = size;
296 #if NMEM_DEBUG
297         yaz_log (LOG_DEBUG, "nmem get_block alloc new block size=%d", get);
298 #endif
299         r = (nmem_block *)xmalloc(sizeof(*r));
300         r->buf = (char *)xmalloc(r->size = get);
301     }
302     r->top = 0;
303     return r;
304 }
305
306 void nmem_reset(NMEM n)
307 {
308     nmem_block *t;
309
310 #if NMEM_DEBUG
311     yaz_log (LOG_DEBUG, "nmem_reset p=%p", n);
312 #endif
313     if (!n)
314         return;
315     NMEM_ENTER;
316     while (n->blocks)
317     {
318         t = n->blocks;
319         n->blocks = n->blocks->next;
320         free_block(t);
321     }
322     n->total = 0;
323     NMEM_LEAVE;
324 }
325
326 #if NMEM_DEBUG
327 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
328 #else
329 void *nmem_malloc(NMEM n, int size)
330 #endif
331 {
332     struct nmem_block *p;
333     char *r;
334
335 #if NMEM_DEBUG
336     yaz_log (LOG_DEBUG, "%s:%d: nmem_malloc p=%p size=%d", file, line,
337                      n, size);
338 #endif
339     if (!n)
340     {
341         abort ();
342         return xmalloc(size);
343     }
344 #ifdef WIN32
345     assert (nmem_init_flag);
346 #endif
347     NMEM_ENTER;
348     p = n->blocks;
349     if (!p || p->size - p->top < size)
350     {
351         p = get_block(size);
352         p->next = n->blocks;
353         n->blocks = p;
354     }
355     r = p->buf + p->top;
356     /* align size */
357     p->top += (size + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
358     n->total += size;
359     NMEM_LEAVE;
360     return r;
361 }
362
363 int nmem_total(NMEM n)
364 {
365     return n->total;
366 }
367
368 #if NMEM_DEBUG
369 NMEM nmem_create_f(const char *file, int line)
370 #else
371 NMEM nmem_create(void)
372 #endif
373 {
374     NMEM r;
375 #if NMEM_DEBUG
376     struct nmem_debug_info *debug_p;
377 #endif
378     
379     NMEM_ENTER;
380     nmem_active_no++;
381     r = cfreelist;
382     if (r)
383         cfreelist = cfreelist->next;
384     else
385         r = (nmem_control *)xmalloc(sizeof(*r));
386     NMEM_LEAVE;
387
388 #if NMEM_DEBUG
389     yaz_log (LOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
390                      nmem_active_no, r);
391 #endif
392     r->blocks = 0;
393     r->total = 0;
394     r->next = 0;
395
396 #if NMEM_DEBUG
397     for (debug_p = nmem_debug_list; debug_p; debug_p = debug_p->next)
398         if (debug_p->p == r)
399         {
400             yaz_log (LOG_FATAL, "multi used block in nmem");
401             abort ();
402         }
403     debug_p = xmalloc (sizeof(*debug_p));
404     strncpy (debug_p->file, file, sizeof(debug_p->file)-1);
405     debug_p->file[sizeof(debug_p->file)-1] = '\0';
406     debug_p->line = line;
407     debug_p->p = r;
408     debug_p->next = nmem_debug_list;
409     nmem_debug_list = debug_p;
410
411     nmem_print_list();
412 #endif
413     return r;
414 }
415
416 #if NMEM_DEBUG
417 void nmem_destroy_f(const char *file, int line, NMEM n)
418 #else
419 void nmem_destroy(NMEM n)
420 #endif
421 {
422 #if NMEM_DEBUG
423     struct nmem_debug_info **debug_p;
424     int ok = 0;
425 #endif
426     if (!n)
427         return;
428     
429 #if NMEM_DEBUG
430     yaz_log (LOG_DEBUG, "%s:%d: nmem_destroy %d p=%p", file, line,
431                      nmem_active_no-1, n);
432     NMEM_ENTER;
433     for (debug_p = &nmem_debug_list; *debug_p; debug_p = &(*debug_p)->next)
434         if ((*debug_p)->p == n)
435         {
436             struct nmem_debug_info *debug_save = *debug_p;
437             *debug_p = (*debug_p)->next;
438             xfree (debug_save);
439             ok = 1;
440             break;
441         }
442     NMEM_LEAVE;
443     nmem_print_list();
444     if (!ok)
445     {
446         yaz_log (LOG_WARN, "%s:%d destroying unallocated nmem block p=%p",
447                  file, line, n);
448         return;
449     }
450 #endif
451     nmem_reset(n);
452     NMEM_ENTER;
453     nmem_active_no--;
454     n->next = cfreelist;
455     cfreelist = n;
456     NMEM_LEAVE;
457 }
458
459 void nmem_transfer (NMEM dst, NMEM src)
460 {
461     nmem_block *t;
462     while ((t=src->blocks))
463     {
464         src->blocks = t->next;
465         t->next = dst->blocks;
466         dst->blocks = t;
467     }
468     dst->total += src->total;
469     src->total = 0;
470 }
471
472 void nmem_critical_enter (void)
473 {
474     NMEM_ENTER;
475 }
476
477 void nmem_critical_leave (void)
478 {
479     NMEM_LEAVE;
480 }
481
482 void nmem_init (void)
483 {
484     if (++nmem_init_flag == 1)
485     {
486 #ifdef WIN32
487         InitializeCriticalSection(&critical_section);
488 #elif YAZ_GNU_THREADS
489         yaz_log (LOG_LOG, "pth_init");
490         pth_init ();
491 #endif
492         nmem_active_no = 0;
493         freelist = NULL;
494         cfreelist = NULL;
495     }
496 }
497
498 void nmem_exit (void)
499 {
500     if (--nmem_init_flag == 0)
501     {
502         yaz_log (LOG_LOG, "nmem_exit .............");
503         oid_exit();
504         while (freelist)
505         {
506             struct nmem_block *fl = freelist;
507             freelist = freelist->next;
508             xfree (fl->buf);
509             xfree (fl);
510         }
511         while (cfreelist)
512         {
513             struct nmem_control *cfl = cfreelist;
514             cfreelist = cfreelist->next;
515             xfree (cfl);
516         }
517 #ifdef WIN32
518         DeleteCriticalSection(&critical_section);
519 #endif
520     }
521 }
522
523
524 #ifdef WIN32
525 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
526                      DWORD reason,
527                      LPVOID reserved)
528 {
529     switch (reason)
530     {
531     case DLL_PROCESS_ATTACH:
532         nmem_init ();
533         break;
534     case DLL_PROCESS_DETACH:
535         nmem_exit ();
536     }
537     return TRUE;
538 }
539 #endif