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