Do not use acquire locks (NMEM_{ENTER,LEAVE}) in
[yaz-moved-to-github.git] / src / nmem.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: nmem.c,v 1.14 2005-01-21 09:23:27 adam Exp $
6  */
7
8 /**
9  * \file nmem.c
10  * \brief Implements Nibble Memory
11  *
12  * This is a simple and fairly wasteful little module for nibble memory
13  * allocation. Evemtually we'll put in something better.
14  *
15  * FIXME - it also has some semaphore stuff, and stuff to handle errno.
16  *         These should be moved to some other place!
17  */
18 #if HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stddef.h>
27 #include <yaz/xmalloc.h>
28 #include <yaz/nmem.h>
29 #include <yaz/log.h>
30 #include <yaz/oid.h>
31
32 #ifdef WIN32
33 #include <windows.h>
34 #endif
35
36 #if YAZ_POSIX_THREADS
37 #include <pthread.h>
38 #endif
39
40 #if YAZ_GNU_THREADS
41 #include <pth.h>
42 #endif
43
44 #define NMEM_CHUNK (4*1024)
45
46 struct align {
47     char x;
48     union {
49         char c;
50         short s;
51         int i;
52         long l;
53 #if HAVE_LONG_LONG
54         long long ll;
55 #endif
56         float f;
57         double d;
58     } u;
59 };
60
61 #define NMEM_ALIGN (offsetof(struct align, u))
62
63 static int log_level = 0;
64 static int log_level_initialized = 0;
65
66 #ifdef WIN32
67 static CRITICAL_SECTION critical_section;
68 #define NMEM_ENTER EnterCriticalSection(&critical_section)
69 #define NMEM_LEAVE LeaveCriticalSection(&critical_section)
70 struct nmem_mutex {
71     CRITICAL_SECTION m_handle;
72 };
73 #elif YAZ_POSIX_THREADS
74 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
75 #define NMEM_ENTER pthread_mutex_lock(&nmem_mutex);
76 #define NMEM_LEAVE pthread_mutex_unlock(&nmem_mutex);
77 struct nmem_mutex {
78     pthread_mutex_t m_handle;
79 };
80 #elif YAZ_GNU_THREADS
81 static pth_mutex_t nmem_mutex = PTH_MUTEX_INIT;
82 #define NMEM_ENTER pth_mutex_acquire(&nmem_mutex, 0, 0)
83 #define NMEM_LEAVE pth_mutex_release(&nmem_mutex)
84 struct nmem_mutex {
85     pth_mutex_t m_handle;
86 };
87 #else
88 #define NMEM_ENTER
89 #define NMEM_LEAVE
90 struct nmem_mutex {
91     int dummy;
92 };
93 #endif
94
95 YAZ_EXPORT void nmem_mutex_create(NMEM_MUTEX *p)
96 {
97     if (!log_level_initialized)
98     {
99         log_level = yaz_log_module_level("nmem");
100         log_level_initialized = 1;
101     }
102
103     if (!*p)
104     {
105         *p = (NMEM_MUTEX) malloc (sizeof(**p));
106 #ifdef WIN32
107         InitializeCriticalSection(&(*p)->m_handle);
108 #elif YAZ_POSIX_THREADS
109         pthread_mutex_init (&(*p)->m_handle, 0);
110 #elif YAZ_GNU_THREADS
111         pth_mutex_init (&(*p)->m_handle);
112 #endif
113     }
114 }
115
116 YAZ_EXPORT void nmem_mutex_enter(NMEM_MUTEX p)
117 {
118     if (p)
119     {
120 #ifdef WIN32
121         EnterCriticalSection(&p->m_handle);
122 #elif YAZ_POSIX_THREADS
123         pthread_mutex_lock(&p->m_handle);
124 #endif
125     }
126 }
127
128 YAZ_EXPORT void nmem_mutex_leave(NMEM_MUTEX p)
129 {
130     if (p)
131     {
132 #ifdef WIN32
133         LeaveCriticalSection(&p->m_handle);
134 #elif YAZ_POSIX_THREADS
135         pthread_mutex_unlock(&p->m_handle);
136 #endif
137     }
138 }
139
140 YAZ_EXPORT void nmem_mutex_destroy(NMEM_MUTEX *p)
141 {
142     if (*p)
143     {
144 #ifdef WIN32
145         DeleteCriticalSection(&(*p)->m_handle);
146 #endif
147         free (*p);
148         *p = 0;
149     }
150 }
151
152 static nmem_block *freelist = NULL;        /* "global" freelists */
153 static nmem_control *cfreelist = NULL;
154 static int nmem_active_no = 0;
155 static int nmem_init_flag = 0;
156
157 #if NMEM_DEBUG
158 struct nmem_debug_info {
159     void *p;
160     char file[40];
161     int line;
162     struct nmem_debug_info *next;
163 };
164   
165 struct nmem_debug_info *nmem_debug_list = 0;  
166 #endif
167
168 static void free_block(nmem_block *p)
169 {  
170     memset(p->buf, 'Y', p->size);
171     p->next = freelist;
172     freelist = p;
173     if (log_level)
174         yaz_log (log_level, "nmem free_block p=%p", p);
175 }
176
177 #if NMEM_DEBUG
178 void nmem_print_list (void)
179 {
180     if(log_level)
181         nmem_print_list_l(log_level);
182 }
183
184 void nmem_print_list_l (int level)
185 {
186     struct nmem_debug_info *p;
187
188     yaz_log (level, "nmem print list");
189     NMEM_ENTER;
190     for (p = nmem_debug_list; p; p = p->next)
191         yaz_log (level, " %s:%d p=%p size=%d", p->file, p->line, p->p,
192                  nmem_total(p->p));
193     NMEM_LEAVE;
194 }
195 #endif
196 /*
197  * acquire a block with a minimum of size free bytes.
198  */
199 static nmem_block *get_block(int size)
200 {
201     nmem_block *r, *l;
202
203     if (log_level)
204         yaz_log (log_level, "nmem get_block size=%d", size);
205
206     for (r = freelist, l = 0; r; l = r, r = r->next)
207         if (r->size >= size)
208             break;
209     if (r)
210     {
211         if (log_level)
212             yaz_log (log_level, "nmem get_block found free block p=%p", r);
213         if (l)
214             l->next = r->next;
215         else
216             freelist = r->next;
217     }
218     else
219     {
220         int get = NMEM_CHUNK;
221
222         if (get < size)
223             get = size;
224         if(log_level)
225             yaz_log (log_level, "nmem get_block alloc new block size=%d", get);
226
227         r = (nmem_block *)xmalloc(sizeof(*r));
228         r->buf = (char *)xmalloc(r->size = get);
229     }
230     r->top = 0;
231     return r;
232 }
233
234 void nmem_reset(NMEM n)
235 {
236     nmem_block *t;
237     
238     yaz_log (log_level, "nmem_reset p=%p", n);
239     if (!n)
240         return;
241     NMEM_ENTER;
242     while (n->blocks)
243     {
244         t = n->blocks;
245         n->blocks = n->blocks->next;
246         free_block(t);
247     }
248     n->total = 0;
249     NMEM_LEAVE;
250 }
251
252 #if NMEM_DEBUG
253 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
254 #else
255 void *nmem_malloc(NMEM n, int size)
256 #endif
257 {
258     struct nmem_block *p;
259     char *r;
260
261 #if NMEM_DEBUG
262     if (log_level)
263         yaz_log (log_level, "%s:%d: nmem_malloc p=%p size=%d", 
264                 file, line, n, size);
265 #endif
266     if (!n)
267     {
268         yaz_log (YLOG_FATAL, "calling nmem_malloc with an null pointer");
269         abort ();
270     }
271 #ifdef WIN32
272     assert (nmem_init_flag);
273 #endif
274     NMEM_ENTER;
275     p = n->blocks;
276     if (!p || p->size - p->top < size)
277     {
278         p = get_block(size);
279         p->next = n->blocks;
280         n->blocks = p;
281     }
282     r = p->buf + p->top;
283     /* align size */
284     p->top += (size + (NMEM_ALIGN - 1)) & ~(NMEM_ALIGN - 1);
285     n->total += size;
286     NMEM_LEAVE;
287     return r;
288 }
289
290 int nmem_total(NMEM n)
291 {
292     return n->total;
293 }
294
295 #if NMEM_DEBUG
296 NMEM nmem_create_f(const char *file, int line)
297 #else
298 NMEM nmem_create(void)
299 #endif
300 {
301     NMEM r;
302 #if NMEM_DEBUG
303     struct nmem_debug_info *debug_p;
304 #endif
305     if (!log_level_initialized)
306     {
307         log_level = yaz_log_module_level("nmem");
308         log_level_initialized = 1;
309     }
310     
311     NMEM_ENTER;
312     nmem_active_no++;
313     r = cfreelist;
314     if (r)
315         cfreelist = cfreelist->next;
316     else
317         r = (nmem_control *)xmalloc(sizeof(*r));
318     NMEM_LEAVE;
319
320 #if NMEM_DEBUG
321     yaz_log (YLOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
322                      nmem_active_no, r);
323 #endif
324     r->blocks = 0;
325     r->total = 0;
326     r->next = 0;
327
328 #if NMEM_DEBUG
329     for (debug_p = nmem_debug_list; debug_p; debug_p = debug_p->next)
330         if (debug_p->p == r)
331         {
332             yaz_log (YLOG_FATAL, "multi used block in nmem");
333             abort ();
334         }
335     debug_p = xmalloc (sizeof(*debug_p));
336     strncpy (debug_p->file, file, sizeof(debug_p->file)-1);
337     debug_p->file[sizeof(debug_p->file)-1] = '\0';
338     debug_p->line = line;
339     debug_p->p = r;
340     debug_p->next = nmem_debug_list;
341     nmem_debug_list = debug_p;
342
343     nmem_print_list();
344 #endif
345     return r;
346 }
347
348 #if NMEM_DEBUG
349 void nmem_destroy_f(const char *file, int line, NMEM n)
350 #else
351 void nmem_destroy(NMEM n)
352 #endif
353 {
354 #if NMEM_DEBUG
355     struct nmem_debug_info **debug_p;
356     int ok = 0;
357 #endif
358     if (!n)
359         return;
360     
361 #if NMEM_DEBUG
362     yaz_log (log_level, "%s:%d: nmem_destroy %d p=%p", file, line,
363                      nmem_active_no-1, n);
364     NMEM_ENTER;
365     for (debug_p = &nmem_debug_list; *debug_p; debug_p = &(*debug_p)->next)
366         if ((*debug_p)->p == n)
367         {
368             struct nmem_debug_info *debug_save = *debug_p;
369             *debug_p = (*debug_p)->next;
370             xfree (debug_save);
371             ok = 1;
372             break;
373         }
374     NMEM_LEAVE;
375     nmem_print_list();
376     if (!ok)
377     {
378         yaz_log (YLOG_WARN, "%s:%d destroying unallocated nmem block p=%p",
379                  file, line, n);
380         return;
381     }
382 #endif
383     nmem_reset(n);
384     NMEM_ENTER;
385     nmem_active_no--;
386     n->next = cfreelist;
387     cfreelist = n;
388     NMEM_LEAVE;
389 }
390
391 void nmem_transfer (NMEM dst, NMEM src)
392 {
393     nmem_block *t;
394     while ((t = src->blocks))
395     {
396         src->blocks = t->next;
397         t->next = dst->blocks;
398         dst->blocks = t;
399     }
400     dst->total += src->total;
401     src->total = 0;
402 }
403
404 void nmem_critical_enter (void)
405 {
406     NMEM_ENTER;
407 }
408
409 void nmem_critical_leave (void)
410 {
411     NMEM_LEAVE;
412 }
413
414 void nmem_init (void)
415 {
416     
417     if (++nmem_init_flag == 1)
418     {
419 #ifdef WIN32
420         InitializeCriticalSection(&critical_section);
421 #elif YAZ_GNU_THREADS
422         pth_init ();
423 #endif
424         nmem_active_no = 0;
425         freelist = NULL;
426         cfreelist = NULL;
427     }
428     if (!log_level_initialized)
429     {
430         log_level = yaz_log_module_level("nmem");
431         log_level_initialized = 1;
432     }
433 }
434
435 void nmem_exit (void)
436 {
437     if (--nmem_init_flag == 0)
438     {
439         oid_exit();
440         while (freelist)
441         {
442             struct nmem_block *fl = freelist;
443             freelist = freelist->next;
444             xfree (fl->buf);
445             xfree (fl);
446         }
447         while (cfreelist)
448         {
449             struct nmem_control *cfl = cfreelist;
450             cfreelist = cfreelist->next;
451             xfree (cfl);
452         }
453 #ifdef WIN32
454         DeleteCriticalSection(&critical_section);
455 #endif
456     }
457 }
458
459
460 #ifdef WIN32
461 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
462                      DWORD reason,
463                      LPVOID reserved)
464 {
465     switch (reason)
466     {
467     case DLL_PROCESS_ATTACH:
468         nmem_init ();
469         break;
470     case DLL_PROCESS_DETACH:
471         nmem_exit ();
472     }
473     return TRUE;
474 }
475 #endif
476
477 int yaz_errno(void)
478 {
479     return errno;
480 }
481
482 void yaz_set_errno(int v)
483 {
484     errno = v;
485 }
486
487 void yaz_strerror(char *buf, int max)
488 {
489 #ifdef WIN32
490     DWORD err;
491 #endif
492     char *cp;
493     if (!log_level_initialized)
494     {
495         log_level = yaz_log_module_level("nmem");
496         log_level_initialized = 1;
497     }
498     
499 #ifdef WIN32
500     err = GetLastError();
501     if (err)
502     {
503         FormatMessage(
504                 FORMAT_MESSAGE_FROM_SYSTEM,
505                 NULL,
506                 err,
507                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */
508                 (LPTSTR) buf,
509                 max-1,
510                 NULL);
511     }
512     else
513         *buf = '\0';
514 #else
515 /* UNIX */
516 #if HAVE_STRERROR_R
517 #if YAZ_POSIX_THREADS
518     *buf = '\0';
519     strerror_r(errno, buf, max);
520     /* if buffer is unset - use strerror anyway (GLIBC bug) */
521     if (*buf == '\0')
522         strcpy(buf, strerror(yaz_errno()));
523 #else
524     strcpy(buf, strerror(yaz_errno()));
525 #endif
526 #else
527     strcpy(buf, strerror(yaz_errno()));
528 #endif
529 /* UNIX */
530 #endif
531     if ((cp = strrchr(buf, '\n')))
532         *cp = '\0';
533     if ((cp = strrchr(buf, '\r')))
534         *cp = '\0';
535 }