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