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