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