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