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