Implemented nmem_critical_{enter,leave}.
[yaz-moved-to-github.git] / util / nmem.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: nmem.c,v $
7  * Revision 1.12  1998-10-13 16:00:18  adam
8  * Implemented nmem_critical_{enter,leave}.
9  *
10  * Revision 1.11  1998/08/21 14:13:36  adam
11  * Added GNU Configure script to build Makefiles.
12  *
13  * Revision 1.10  1998/07/20 12:35:57  adam
14  * Added more memory diagnostics (when NMEM_DEBUG is 1).
15  *
16  * Revision 1.9  1998/07/07 15:49:01  adam
17  * Reduced chunk size.
18  *
19  * Revision 1.8  1998/07/03 14:21:27  adam
20  * Added critical sections for pthreads-library. Thanks to Ian Ibbotson,
21  * Fretwell Downing Informatics.
22  *
23  * Revision 1.7  1998/02/11 11:53:36  adam
24  * Changed code so that it compiles as C++.
25  *
26  * Revision 1.6  1997/10/31 12:20:09  adam
27  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
28  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
29  * Bug fix: missing fclose in data1_read_espec1.
30  *
31  * Revision 1.5  1997/10/06 09:09:52  adam
32  * Function mmem_exit releases memory used by the freelists.
33  *
34  * Revision 1.4  1997/09/29 07:12:50  adam
35  * NMEM thread safe. NMEM must be initialized before use (sigh) -
36  * routine nmem_init/nmem_exit implemented.
37  *
38  * Revision 1.3  1997/07/21 12:47:38  adam
39  * Moved definition of nmem_control and nmem_block.
40  *
41  * Revision 1.2  1995/12/13 13:44:37  quinn
42  * Modified Data1-system to use nmem
43  *
44  * Revision 1.1  1995/11/13  09:27:52  quinn
45  * Fiddling with the variant stuff.
46  *
47  *
48  */
49
50 /*
51  * This is a simple and fairly wasteful little module for nibble memory
52  * allocation. Evemtually we'll put in something better.
53  */
54
55 #include <xmalloc.h>
56 #include <nmem.h>
57 #include <log.h>
58 #ifdef WINDOWS
59 #include <windows.h>
60 #elif _REENTRANT
61
62 #if HAVE_PTHREAD_H
63 #include <pthread.h>
64 #elif HAVE_THREAD_H
65 #include <thread.h>
66 #endif
67
68 #endif
69
70 #define NMEM_CHUNK (4*1024)
71
72 #ifdef WINDOWS
73 static CRITICAL_SECTION critical_section;
74 #define NMEM_ENTER EnterCriticalSection(&critical_section)
75 #define NMEM_LEAVE LeaveCriticalSection(&critical_section)
76 #elif _REENTRANT
77 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
78 #define NMEM_ENTER pthread_mutex_lock(&nmem_mutex);
79 #define NMEM_LEAVE pthread_mutex_unlock(&nmem_mutex);
80 #else
81 #define NMEM_ENTER
82 #define NMEM_LEAVE
83 #endif
84
85 static nmem_block *freelist = NULL;        /* "global" freelists */
86 static nmem_control *cfreelist = NULL;
87 static int nmem_active_no = 0;
88
89 static void free_block(nmem_block *p)
90 {  
91     p->next = freelist;
92     freelist = p;
93 #if NMEM_DEBUG
94     logf (LOG_DEBUG, "nmem free_block p=%p", p);
95 #endif
96 }
97
98 /*
99  * acquire a block with a minimum of size free bytes.
100  */
101 static nmem_block *get_block(int size)
102 {
103     nmem_block *r, *l;
104
105 #if NMEM_DEBUG
106     logf (LOG_DEBUG, "nmem get_block size=%d", size);
107 #endif
108     for (r = freelist, l = 0; r; l = r, r = r->next)
109         if (r->size >= size)
110             break;
111     if (r)
112     {
113 #if NMEM_DEBUG
114         logf (LOG_DEBUG, "nmem get_block found free block p=%p", r);
115 #endif
116         if (l)
117             l->next = r->next;
118         else
119             freelist = r->next;
120     }
121     else
122     {
123         int get = NMEM_CHUNK;
124
125         if (get < size)
126             get = size;
127 #if NMEM_DEBUG
128         logf (LOG_DEBUG, "nmem get_block alloc new block size=%d", get);
129 #endif
130         r = (nmem_block *)xmalloc(sizeof(*r));
131         r->buf = (char *)xmalloc(r->size = get);
132     }
133     r->top = 0;
134     return r;
135 }
136
137 void nmem_reset(NMEM n)
138 {
139     nmem_block *t;
140
141 #if NMEM_DEBUG
142     logf (LOG_DEBUG, "nmem_reset p=%p", n);
143 #endif
144     if (!n)
145         return;
146     NMEM_ENTER;
147     while (n->blocks)
148     {
149         t = n->blocks;
150         n->blocks = n->blocks->next;
151         free_block(t);
152     }
153     NMEM_LEAVE;
154     n->total = 0;
155 }
156
157 #if NMEM_DEBUG
158 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
159 #else
160 void *nmem_malloc(NMEM n, int size)
161 #endif
162 {
163     struct nmem_block *p;
164     char *r;
165
166 #if NMEM_DEBUG
167     logf (LOG_DEBUG, "%s:%d: nmem_malloc p=%p size=%d", file, line,
168                      n, size);
169 #endif
170     if (!n)
171         return xmalloc(size);
172     NMEM_ENTER;
173     p = n->blocks;
174     if (!p || p->size - p->top < size)
175     {
176         p = get_block(size);
177         p->next = n->blocks;
178         n->blocks = p;
179     }
180     r = p->buf + p->top;
181     /* align size */
182     p->top += (size + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
183     n->total += size;
184     NMEM_LEAVE;
185     return r;
186 }
187
188 int nmem_total(NMEM n)
189 {
190     return n->total;
191 }
192
193 #if NMEM_DEBUG
194 NMEM nmem_create_f(const char *file, int line)
195 #else
196 NMEM nmem_create(void)
197 #endif
198 {
199     NMEM r;
200     
201     NMEM_ENTER;
202     nmem_active_no++;
203     r = cfreelist;
204     if (r)
205         cfreelist = cfreelist->next;
206     else
207         r = (nmem_control *)xmalloc(sizeof(*r));
208     NMEM_LEAVE;
209
210 #if NMEM_DEBUG
211     logf (LOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
212                      nmem_active_no-1, r);
213 #endif
214     r->blocks = 0;
215     r->total = 0;
216     r->next = 0;
217     return r;
218 }
219
220 #if NMEM_DEBUG
221 void nmem_destroy_f(const char *file, int line, NMEM n)
222 #else
223 void nmem_destroy(NMEM n)
224 #endif
225 {
226     if (!n)
227         return;
228     nmem_reset(n);
229     NMEM_ENTER;
230     nmem_active_no--;
231     n->next = cfreelist;
232     cfreelist = n;
233     NMEM_LEAVE;
234 #if NMEM_DEBUG
235     logf (LOG_DEBUG, "%s:%d: nmem_destroy %d p=%p", file, line,
236                      nmem_active_no, n);
237 #endif
238 }
239
240 void nmem_critical_enter (void)
241 {
242     NMEM_ENTER;
243 }
244
245 void nmem_critical_leave (void)
246 {
247     NMEM_LEAVE;
248 }
249
250 void nmem_init (void)
251 {
252 #ifdef WINDOWS
253     InitializeCriticalSection(&critical_section);
254 #endif
255     nmem_active_no = 0;
256     freelist = NULL;
257     cfreelist = NULL;
258 }
259
260 void nmem_exit (void)
261 {
262     while (freelist)
263     {
264         struct nmem_block *fl = freelist;
265         freelist = freelist->next;
266         xfree (fl->buf);
267         xfree (fl);
268     }
269     while (cfreelist)
270     {
271         struct nmem_control *cfl = cfreelist;
272         cfreelist = cfreelist->next;
273         xfree (cfl);
274     }
275 #ifdef WINDOWS
276     DeleteCriticalSection(&critical_section);
277 #endif
278 }
279