Add odr_prepend()
[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.1 2003-10-27 12:21:32 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     p->next = freelist;
142     freelist = p;
143 #if NMEM_DEBUG
144     yaz_log (LOG_DEBUG, "nmem free_block p=%p", p);
145 #endif
146 }
147
148 #if NMEM_DEBUG
149 void nmem_print_list (void)
150 {
151     struct nmem_debug_info *p;
152
153     yaz_log (LOG_DEBUG, "nmem print list");
154     NMEM_ENTER;
155     for (p = nmem_debug_list; p; p = p->next)
156         yaz_log (LOG_DEBUG, " %s:%d p=%p size=%d", p->file, p->line, p->p,
157                  nmem_total(p->p));
158     NMEM_LEAVE;
159 }
160 #endif
161 /*
162  * acquire a block with a minimum of size free bytes.
163  */
164 static nmem_block *get_block(int size)
165 {
166     nmem_block *r, *l;
167
168 #if NMEM_DEBUG
169     yaz_log (LOG_DEBUG, "nmem get_block size=%d", size);
170 #endif
171     for (r = freelist, l = 0; r; l = r, r = r->next)
172         if (r->size >= size)
173             break;
174     if (r)
175     {
176 #if NMEM_DEBUG
177         yaz_log (LOG_DEBUG, "nmem get_block found free block p=%p", r);
178 #endif
179         if (l)
180             l->next = r->next;
181         else
182             freelist = r->next;
183     }
184     else
185     {
186         int get = NMEM_CHUNK;
187
188         if (get < size)
189             get = size;
190 #if NMEM_DEBUG
191         yaz_log (LOG_DEBUG, "nmem get_block alloc new block size=%d", get);
192 #endif
193         r = (nmem_block *)xmalloc(sizeof(*r));
194         r->buf = (char *)xmalloc(r->size = get);
195     }
196     r->top = 0;
197     return r;
198 }
199
200 void nmem_reset(NMEM n)
201 {
202     nmem_block *t;
203
204 #if NMEM_DEBUG
205     yaz_log (LOG_DEBUG, "nmem_reset p=%p", n);
206 #endif
207     if (!n)
208         return;
209     NMEM_ENTER;
210     while (n->blocks)
211     {
212         t = n->blocks;
213         n->blocks = n->blocks->next;
214         free_block(t);
215     }
216     n->total = 0;
217     NMEM_LEAVE;
218 }
219
220 #if NMEM_DEBUG
221 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
222 #else
223 void *nmem_malloc(NMEM n, int size)
224 #endif
225 {
226     struct nmem_block *p;
227     char *r;
228
229 #if NMEM_DEBUG
230     yaz_log (LOG_DEBUG, "%s:%d: nmem_malloc p=%p size=%d", file, line,
231                      n, size);
232 #endif
233     if (!n)
234     {
235         yaz_log (LOG_FATAL, "calling nmem_malloc with an null pointer");
236         abort ();
237     }
238 #ifdef WIN32
239     assert (nmem_init_flag);
240 #endif
241     NMEM_ENTER;
242     p = n->blocks;
243     if (!p || p->size - p->top < size)
244     {
245         p = get_block(size);
246         p->next = n->blocks;
247         n->blocks = p;
248     }
249     r = p->buf + p->top;
250     /* align size */
251     p->top += (size + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
252     n->total += size;
253     NMEM_LEAVE;
254     return r;
255 }
256
257 int nmem_total(NMEM n)
258 {
259     return n->total;
260 }
261
262 #if NMEM_DEBUG
263 NMEM nmem_create_f(const char *file, int line)
264 #else
265 NMEM nmem_create(void)
266 #endif
267 {
268     NMEM r;
269 #if NMEM_DEBUG
270     struct nmem_debug_info *debug_p;
271 #endif
272     
273     NMEM_ENTER;
274     nmem_active_no++;
275     r = cfreelist;
276     if (r)
277         cfreelist = cfreelist->next;
278     else
279         r = (nmem_control *)xmalloc(sizeof(*r));
280     NMEM_LEAVE;
281
282 #if NMEM_DEBUG
283     yaz_log (LOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
284                      nmem_active_no, r);
285 #endif
286     r->blocks = 0;
287     r->total = 0;
288     r->next = 0;
289
290 #if NMEM_DEBUG
291     for (debug_p = nmem_debug_list; debug_p; debug_p = debug_p->next)
292         if (debug_p->p == r)
293         {
294             yaz_log (LOG_FATAL, "multi used block in nmem");
295             abort ();
296         }
297     debug_p = xmalloc (sizeof(*debug_p));
298     strncpy (debug_p->file, file, sizeof(debug_p->file)-1);
299     debug_p->file[sizeof(debug_p->file)-1] = '\0';
300     debug_p->line = line;
301     debug_p->p = r;
302     debug_p->next = nmem_debug_list;
303     nmem_debug_list = debug_p;
304
305     nmem_print_list();
306 #endif
307     return r;
308 }
309
310 #if NMEM_DEBUG
311 void nmem_destroy_f(const char *file, int line, NMEM n)
312 #else
313 void nmem_destroy(NMEM n)
314 #endif
315 {
316 #if NMEM_DEBUG
317     struct nmem_debug_info **debug_p;
318     int ok = 0;
319 #endif
320     if (!n)
321         return;
322     
323 #if NMEM_DEBUG
324     yaz_log (LOG_DEBUG, "%s:%d: nmem_destroy %d p=%p", file, line,
325                      nmem_active_no-1, n);
326     NMEM_ENTER;
327     for (debug_p = &nmem_debug_list; *debug_p; debug_p = &(*debug_p)->next)
328         if ((*debug_p)->p == n)
329         {
330             struct nmem_debug_info *debug_save = *debug_p;
331             *debug_p = (*debug_p)->next;
332             xfree (debug_save);
333             ok = 1;
334             break;
335         }
336     NMEM_LEAVE;
337     nmem_print_list();
338     if (!ok)
339     {
340         yaz_log (LOG_WARN, "%s:%d destroying unallocated nmem block p=%p",
341                  file, line, n);
342         return;
343     }
344 #endif
345     nmem_reset(n);
346     NMEM_ENTER;
347     nmem_active_no--;
348     n->next = cfreelist;
349     cfreelist = n;
350     NMEM_LEAVE;
351 }
352
353 void nmem_transfer (NMEM dst, NMEM src)
354 {
355     nmem_block *t;
356     while ((t=src->blocks))
357     {
358         src->blocks = t->next;
359         t->next = dst->blocks;
360         dst->blocks = t;
361     }
362     dst->total += src->total;
363     src->total = 0;
364 }
365
366 void nmem_critical_enter (void)
367 {
368     NMEM_ENTER;
369 }
370
371 void nmem_critical_leave (void)
372 {
373     NMEM_LEAVE;
374 }
375
376 void nmem_init (void)
377 {
378     if (++nmem_init_flag == 1)
379     {
380 #ifdef WIN32
381         InitializeCriticalSection(&critical_section);
382 #elif YAZ_GNU_THREADS
383         yaz_log (LOG_LOG, "pth_init");
384         pth_init ();
385 #endif
386         nmem_active_no = 0;
387         freelist = NULL;
388         cfreelist = NULL;
389     }
390 }
391
392 void nmem_exit (void)
393 {
394     if (--nmem_init_flag == 0)
395     {
396         oid_exit();
397         while (freelist)
398         {
399             struct nmem_block *fl = freelist;
400             freelist = freelist->next;
401             xfree (fl->buf);
402             xfree (fl);
403         }
404         while (cfreelist)
405         {
406             struct nmem_control *cfl = cfreelist;
407             cfreelist = cfreelist->next;
408             xfree (cfl);
409         }
410 #ifdef WIN32
411         DeleteCriticalSection(&critical_section);
412 #endif
413     }
414 }
415
416
417 #ifdef WIN32
418 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
419                      DWORD reason,
420                      LPVOID reserved)
421 {
422     switch (reason)
423     {
424     case DLL_PROCESS_ATTACH:
425         nmem_init ();
426         break;
427     case DLL_PROCESS_DETACH:
428         nmem_exit ();
429     }
430     return TRUE;
431 }
432 #endif
433
434 int yaz_errno(void)
435 {
436     return errno;
437 }
438
439 void yaz_set_errno(int v)
440 {
441     errno = v;
442 }
443
444 void yaz_strerror(char *buf, int max)
445 {
446     char *cp;
447 #ifdef WIN32
448     DWORD err = GetLastError();
449     if (err)
450     {
451         FormatMessage(
452                 FORMAT_MESSAGE_FROM_SYSTEM,
453                 NULL,
454                 err,
455                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */
456                 (LPTSTR) buf,
457                 max-1,
458                 NULL);
459     }
460     else
461         *buf = '\0';
462 #else
463 /* UNIX */
464 #if HAVE_STRERROR_R
465 #if YAZ_POSIX_THREADS
466     *buf = '\0';
467     strerror_r(errno, buf, max);
468     /* if buffer is unset - use strerror anyway (GLIBC bug) */
469     if (*buf == '\0')
470         strcpy(buf, strerror(yaz_errno()));
471 #else
472     strcpy(buf, strerror(yaz_errno()));
473 #endif
474 #else
475     strcpy(buf, strerror(yaz_errno()));
476 #endif
477 /* UNIX */
478 #endif
479     if ((cp=strrchr(buf, '\n')))
480         *cp = '\0';
481     if ((cp=strrchr(buf, '\r')))
482         *cp = '\0';
483 }