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