1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
5
6 /**
7 * \file nmem.c
8 * \brief Implements Nibble Memory
9 *
10 * This is a simple and fairly wasteful little module for nibble memory
11 * allocation.
12 *
13 */
14 #if HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <assert.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stddef.h>
23 #include <yaz/xmalloc.h>
24 #include <yaz/nmem.h>
25 #include <yaz/log.h>
26 #include <yaz/snprintf.h>
27
28 #if YAZ_POSIX_THREADS
29 #include <pthread.h>
30 #endif
31
32 #if YAZ_POSIX_THREADS
33 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
34 #endif
35 static size_t no_nmem_handles = 0;
36 static size_t no_nmem_blocks = 0;
37 static size_t nmem_allocated = 0;
38
39 #define NMEM_CHUNK (4*1024)
40
41 struct nmem_block
42 {
43 char *buf; /* memory allocated in this block */
44 size_t size; /* size of buf */
45 size_t top; /* top of buffer */
46 struct nmem_block *next;
47 };
48
49 struct nmem_control
50 {
51 size_t total;
52 struct nmem_block *blocks;
53 struct nmem_control *next;
54 };
55
56 struct align {
57 char x;
58 union {
59 char c;
60 short s;
61 int i;
62 long l;
63 #if HAVE_LONG_LONG
64 long long ll;
65 #endif
66 float f;
67 double d;
68 } u;
69 };
70
71 #define NMEM_ALIGN (offsetof(struct align, u))
72
73 static int log_level = 0;
74 static int log_level_initialized = 0;
75
nmem_lock(void)76 static void nmem_lock(void)
77 {
78 #if YAZ_POSIX_THREADS
79 pthread_mutex_lock(&nmem_mutex);
80 #endif
81 }
82
nmem_unlock(void)83 static void nmem_unlock(void)
84 {
85 #if YAZ_POSIX_THREADS
86 pthread_mutex_unlock(&nmem_mutex);
87 #endif
88 }
89
free_block(struct nmem_block * p)90 static void free_block(struct nmem_block *p)
91 {
92 nmem_lock();
93 no_nmem_blocks--;
94 nmem_allocated -= p->size;
95 nmem_unlock();
96 xfree(p->buf);
97 xfree(p);
98 if (log_level)
99 yaz_log(log_level, "nmem free_block p=%p", p);
100 }
101
102 /*
103 * acquire a block with a minimum of size free bytes.
104 */
get_block(size_t size)105 static struct nmem_block *get_block(size_t size)
106 {
107 struct nmem_block *r;
108 size_t get = NMEM_CHUNK;
109
110 if (log_level)
111 yaz_log(log_level, "nmem get_block size=%ld", (long) size);
112
113 if (get < size)
114 get = size;
115 if (log_level)
116 yaz_log(log_level, "nmem get_block alloc new block size=%ld",
117 (long) get);
118
119 r = (struct nmem_block *) xmalloc(sizeof(*r));
120 r->buf = (char *)xmalloc(r->size = get);
121 r->top = 0;
122 nmem_lock();
123 no_nmem_blocks++;
124 nmem_allocated += r->size;
125 nmem_unlock();
126 return r;
127 }
128
nmem_reset(NMEM n)129 void nmem_reset(NMEM n)
130 {
131 struct nmem_block *t;
132
133 yaz_log(log_level, "nmem_reset p=%p", n);
134 if (!n)
135 return;
136 while (n->blocks)
137 {
138 t = n->blocks;
139 n->blocks = n->blocks->next;
140 free_block(t);
141 }
142 n->total = 0;
143 }
144
nmem_malloc(NMEM n,size_t size)145 void *nmem_malloc(NMEM n, size_t size)
146 {
147 struct nmem_block *p;
148 char *r;
149
150 if (!n)
151 {
152 yaz_log(YLOG_FATAL, "calling nmem_malloc with an null pointer");
153 abort();
154 }
155 p = n->blocks;
156 if (!p || p->size < size + p->top)
157 {
158 p = get_block(size);
159 p->next = n->blocks;
160 n->blocks = p;
161 }
162 r = p->buf + p->top;
163 /* align size */
164 p->top += (size + (NMEM_ALIGN - 1)) & ~(NMEM_ALIGN - 1);
165 n->total += size;
166 return r;
167 }
168
nmem_total(NMEM n)169 size_t nmem_total(NMEM n)
170 {
171 return n->total;
172 }
173
nmem_init_globals(void)174 void nmem_init_globals(void)
175 {
176 #if YAZ_POSIX_THREADS
177 pthread_atfork(nmem_lock, nmem_unlock, nmem_unlock);
178 #endif
179 }
180
nmem_create(void)181 NMEM nmem_create(void)
182 {
183 NMEM r;
184
185 nmem_lock();
186 no_nmem_handles++;
187 nmem_unlock();
188 if (!log_level_initialized)
189 {
190 /* below will call nmem_init_globals once */
191 log_level = yaz_log_module_level("nmem");
192 log_level_initialized = 1;
193 }
194
195 r = (struct nmem_control *)xmalloc(sizeof(*r));
196
197 r->blocks = 0;
198 r->total = 0;
199 r->next = 0;
200
201 return r;
202 }
203
nmem_destroy(NMEM n)204 void nmem_destroy(NMEM n)
205 {
206 if (!n)
207 return;
208
209 nmem_reset(n);
210 xfree(n);
211 nmem_lock();
212 no_nmem_handles--;
213 nmem_unlock();
214 }
215
nmem_transfer(NMEM dst,NMEM src)216 void nmem_transfer(NMEM dst, NMEM src)
217 {
218 struct nmem_block *t;
219 while ((t = src->blocks))
220 {
221 src->blocks = t->next;
222 t->next = dst->blocks;
223 dst->blocks = t;
224 }
225 dst->total += src->total;
226 src->total = 0;
227 }
228
nmem_get_status(char * dst,size_t l)229 int nmem_get_status(char *dst, size_t l)
230 {
231 size_t handles, blocks, allocated;
232
233 nmem_lock();
234 handles = no_nmem_handles;
235 blocks = no_nmem_blocks;
236 allocated = nmem_allocated;
237 nmem_unlock();
238 yaz_snprintf(dst, l,
239 "<nmem>\n"
240 " <handles>%zd</handles>\n"
241 " <blocks>%zd</blocks>\n"
242 " <allocated>%zd</allocated>\n"
243 "</nmem>\n", handles, blocks, allocated);
244 return 0;
245 }
246 /*
247 * Local variables:
248 * c-basic-offset: 4
249 * c-file-style: "Stroustrup"
250 * indent-tabs-mode: nil
251 * End:
252 * vim: shiftwidth=4 tabstop=8 expandtab
253 */
254
255