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