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