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