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