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