xref: /plan9/sys/src/cmd/gs/src/gsmalloc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 2000, 2002 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gsmalloc.c,v 1.13 2004/08/18 22:24:47 stefan Exp $ */
18 /* C heap allocator */
19 #include "malloc_.h"
20 #include "gdebug.h"
21 #include "gserror.h"
22 #include "gserrors.h"
23 #include "gstypes.h"
24 #include "gsmemory.h"
25 #include "gsmdebug.h"
26 #include "gsstruct.h"		/* for st_bytes */
27 #include "gsmalloc.h"
28 #include "gsmemlok.h"		/* locking (multithreading) wrapper */
29 #include "gsmemret.h"		/* retrying wrapper */
30 
31 
32 /* ------ Heap allocator ------ */
33 
34 /*
35  * An implementation of Ghostscript's memory manager interface
36  * that works directly with the C heap.  We keep track of all allocated
37  * blocks so we can free them at cleanup time.
38  */
39 /* Raw memory procedures */
40 private gs_memory_proc_alloc_bytes(gs_heap_alloc_bytes);
41 private gs_memory_proc_resize_object(gs_heap_resize_object);
42 private gs_memory_proc_free_object(gs_heap_free_object);
43 private gs_memory_proc_stable(gs_heap_stable);
44 private gs_memory_proc_status(gs_heap_status);
45 private gs_memory_proc_free_all(gs_heap_free_all);
46 
47 /* Object memory procedures */
48 private gs_memory_proc_alloc_struct(gs_heap_alloc_struct);
49 private gs_memory_proc_alloc_byte_array(gs_heap_alloc_byte_array);
50 private gs_memory_proc_alloc_struct_array(gs_heap_alloc_struct_array);
51 private gs_memory_proc_object_size(gs_heap_object_size);
52 private gs_memory_proc_object_type(gs_heap_object_type);
53 private gs_memory_proc_alloc_string(gs_heap_alloc_string);
54 private gs_memory_proc_resize_string(gs_heap_resize_string);
55 private gs_memory_proc_free_string(gs_heap_free_string);
56 private gs_memory_proc_register_root(gs_heap_register_root);
57 private gs_memory_proc_unregister_root(gs_heap_unregister_root);
58 private gs_memory_proc_enable_free(gs_heap_enable_free);
59 private const gs_memory_procs_t gs_malloc_memory_procs =
60 {
61     /* Raw memory procedures */
62     gs_heap_alloc_bytes,
63     gs_heap_resize_object,
64     gs_heap_free_object,
65     gs_heap_stable,
66     gs_heap_status,
67     gs_heap_free_all,
68     gs_ignore_consolidate_free,
69     /* Object memory procedures */
70     gs_heap_alloc_bytes,
71     gs_heap_alloc_struct,
72     gs_heap_alloc_struct,
73     gs_heap_alloc_byte_array,
74     gs_heap_alloc_byte_array,
75     gs_heap_alloc_struct_array,
76     gs_heap_alloc_struct_array,
77     gs_heap_object_size,
78     gs_heap_object_type,
79     gs_heap_alloc_string,
80     gs_heap_alloc_string,
81     gs_heap_resize_string,
82     gs_heap_free_string,
83     gs_heap_register_root,
84     gs_heap_unregister_root,
85     gs_heap_enable_free
86 };
87 
88 /* We must make sure that malloc_blocks leave the block aligned. */
89 /*typedef struct gs_malloc_block_s gs_malloc_block_t; */
90 #define malloc_block_data\
91 	gs_malloc_block_t *next;\
92 	gs_malloc_block_t *prev;\
93 	uint size;\
94 	gs_memory_type_ptr_t type;\
95 	client_name_t cname
96 struct malloc_block_data_s {
97     malloc_block_data;
98 };
99 struct gs_malloc_block_s {
100     malloc_block_data;
101 /* ANSI C does not allow zero-size arrays, so we need the following */
102 /* unnecessary and wasteful workaround: */
103 #define _npad (-size_of(struct malloc_block_data_s) & (arch_align_memory_mod - 1))
104     byte _pad[(_npad == 0 ? arch_align_memory_mod : _npad)];
105 #undef _npad
106 };
107 
108 /* Initialize a malloc allocator. */
109 private long heap_available(void);
110 gs_malloc_memory_t *
gs_malloc_memory_init(void)111 gs_malloc_memory_init(void)
112 {
113     gs_malloc_memory_t *mem =
114 	(gs_malloc_memory_t *)malloc(sizeof(gs_malloc_memory_t));
115 
116     mem->stable_memory = 0;	/* just for tidyness, never referenced */
117     mem->procs = gs_malloc_memory_procs;
118     mem->allocated = 0;
119     mem->limit = max_long;
120     mem->used = 0;
121     mem->max_used = 0;
122     mem->gs_lib_ctx = 0;
123     mem->non_gc_memory = (gs_memory_t *)mem;
124 
125     return mem;
126 }
127 /*
128  * Estimate the amount of available memory by probing with mallocs.
129  * We may under-estimate by a lot, but that's better than winding up with
130  * a seriously inflated address space.  This is quite a hack!
131  */
132 #define max_malloc_probes 20
133 #define malloc_probe_size 64000
134 private long
heap_available()135 heap_available()
136 {
137     long avail = 0;
138     void *probes[max_malloc_probes];
139     uint n;
140 
141     for (n = 0; n < max_malloc_probes; n++) {
142 	if ((probes[n] = malloc(malloc_probe_size)) == 0)
143 	    break;
144 	if_debug2('a', "[a]heap_available probe[%d]=0x%lx\n",
145 		  n, (ulong) probes[n]);
146 	avail += malloc_probe_size;
147     }
148     while (n)
149 	free(probes[--n]);
150     return avail;
151 }
152 
153 /* Allocate various kinds of blocks. */
154 private byte *
gs_heap_alloc_bytes(gs_memory_t * mem,uint size,client_name_t cname)155 gs_heap_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
156 {
157     gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
158     byte *ptr = 0;
159 
160 #ifdef DEBUG
161     const char *msg;
162     static const char *const ok_msg = "OK";
163 
164 #  define set_msg(str) (msg = (str))
165 #else
166 #  define set_msg(str) DO_NOTHING
167 #endif
168 
169     if (size > mmem->limit - sizeof(gs_malloc_block_t)) {
170 	/* Definitely too large to allocate; also avoids overflow. */
171 	set_msg("exceeded limit");
172     } else {
173 	uint added = size + sizeof(gs_malloc_block_t);
174 
175 	if (mmem->limit - added < mmem->used)
176 	    set_msg("exceeded limit");
177 	else if ((ptr = (byte *) malloc(added)) == 0)
178 	    set_msg("failed");
179 	else {
180 	    gs_malloc_block_t *bp = (gs_malloc_block_t *) ptr;
181 
182 	    /*
183 	     * We would like to check that malloc aligns blocks at least as
184 	     * strictly as the compiler (as defined by arch_align_memory_mod).
185 	     * However, Microsoft VC 6 does not satisfy this requirement.
186 	     * See gsmemory.h for more explanation.
187 	     */
188 	    set_msg(ok_msg);
189 	    if (mmem->allocated)
190 		mmem->allocated->prev = bp;
191 	    bp->next = mmem->allocated;
192 	    bp->prev = 0;
193 	    bp->size = size;
194 	    bp->type = &st_bytes;
195 	    bp->cname = cname;
196 	    mmem->allocated = bp;
197 	    ptr = (byte *) (bp + 1);
198 	    gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
199 	    mmem->used += size + sizeof(gs_malloc_block_t);
200 	    if (mmem->used > mmem->max_used)
201 		mmem->max_used = mmem->used;
202 	}
203     }
204 #ifdef DEBUG
205     if (gs_debug_c('a') || msg != ok_msg)
206 	dlprintf4("[a+]gs_malloc(%s)(%u) = 0x%lx: %s\n",
207 		  client_name_string(cname), size, (ulong) ptr, msg);
208 #endif
209     return ptr;
210 #undef set_msg
211 }
212 private void *
gs_heap_alloc_struct(gs_memory_t * mem,gs_memory_type_ptr_t pstype,client_name_t cname)213 gs_heap_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
214 		     client_name_t cname)
215 {
216     void *ptr =
217     gs_heap_alloc_bytes(mem, gs_struct_type_size(pstype), cname);
218 
219     if (ptr == 0)
220 	return 0;
221     ((gs_malloc_block_t *) ptr)[-1].type = pstype;
222     return ptr;
223 }
224 private byte *
gs_heap_alloc_byte_array(gs_memory_t * mem,uint num_elements,uint elt_size,client_name_t cname)225 gs_heap_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size,
226 			 client_name_t cname)
227 {
228     ulong lsize = (ulong) num_elements * elt_size;
229 
230     if (lsize != (uint) lsize)
231 	return 0;
232     return gs_heap_alloc_bytes(mem, (uint) lsize, cname);
233 }
234 private void *
gs_heap_alloc_struct_array(gs_memory_t * mem,uint num_elements,gs_memory_type_ptr_t pstype,client_name_t cname)235 gs_heap_alloc_struct_array(gs_memory_t * mem, uint num_elements,
236 			   gs_memory_type_ptr_t pstype, client_name_t cname)
237 {
238     void *ptr =
239     gs_heap_alloc_byte_array(mem, num_elements,
240 			     gs_struct_type_size(pstype), cname);
241 
242     if (ptr == 0)
243 	return 0;
244     ((gs_malloc_block_t *) ptr)[-1].type = pstype;
245     return ptr;
246 }
247 private void *
gs_heap_resize_object(gs_memory_t * mem,void * obj,uint new_num_elements,client_name_t cname)248 gs_heap_resize_object(gs_memory_t * mem, void *obj, uint new_num_elements,
249 		      client_name_t cname)
250 {
251     gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
252     gs_malloc_block_t *ptr = (gs_malloc_block_t *) obj - 1;
253     gs_memory_type_ptr_t pstype = ptr->type;
254     uint old_size = gs_object_size(mem, obj) + sizeof(gs_malloc_block_t);
255     uint new_size =
256 	gs_struct_type_size(pstype) * new_num_elements +
257 	sizeof(gs_malloc_block_t);
258     gs_malloc_block_t *new_ptr;
259 
260     if (new_size == old_size)
261         return obj;
262     new_ptr = (gs_malloc_block_t *) gs_realloc(ptr, old_size, new_size);
263     if (new_ptr == 0)
264 	return 0;
265     if (new_ptr->prev)
266 	new_ptr->prev->next = new_ptr;
267     else
268 	mmem->allocated = new_ptr;
269     if (new_ptr->next)
270 	new_ptr->next->prev = new_ptr;
271     new_ptr->size = new_size - sizeof(gs_malloc_block_t);
272     mmem->used -= old_size;
273     mmem->used += new_size;
274     if (new_size > old_size)
275 	gs_alloc_fill((byte *) new_ptr + old_size,
276 		      gs_alloc_fill_alloc, new_size - old_size);
277     return new_ptr + 1;
278 }
279 private uint
gs_heap_object_size(gs_memory_t * mem,const void * ptr)280 gs_heap_object_size(gs_memory_t * mem, const void *ptr)
281 {
282     return ((const gs_malloc_block_t *)ptr)[-1].size;
283 }
284 private gs_memory_type_ptr_t
gs_heap_object_type(gs_memory_t * mem,const void * ptr)285 gs_heap_object_type(gs_memory_t * mem, const void *ptr)
286 {
287     return ((const gs_malloc_block_t *)ptr)[-1].type;
288 }
289 private void
gs_heap_free_object(gs_memory_t * mem,void * ptr,client_name_t cname)290 gs_heap_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
291 {
292     gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
293     gs_malloc_block_t *bp = mmem->allocated;
294     gs_memory_type_ptr_t pstype;
295     struct_proc_finalize((*finalize));
296 
297     if_debug3('a', "[a-]gs_free(%s) 0x%lx(%u)\n",
298 	      client_name_string(cname), (ulong) ptr,
299 	      (ptr == 0 ? 0 : ((gs_malloc_block_t *) ptr)[-1].size));
300     if (ptr == 0)
301 	return;
302     pstype = ((gs_malloc_block_t *) ptr)[-1].type;
303     finalize = pstype->finalize;
304     if (finalize != 0) {
305 	if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
306 		  struct_type_name_string(pstype),
307 		  (ulong) ptr, client_name_string(cname));
308 	(*finalize) (ptr);
309     }
310     if (ptr == bp + 1) {
311 	mmem->allocated = bp->next;
312 	mmem->used -= bp->size + sizeof(gs_malloc_block_t);
313 
314 	if (mmem->allocated)
315 	    mmem->allocated->prev = 0;
316 	gs_alloc_fill(bp, gs_alloc_fill_free,
317 		      bp->size + sizeof(gs_malloc_block_t));
318 	free(bp);
319     } else {
320 	gs_malloc_block_t *np;
321 
322 	/*
323 	 * bp == 0 at this point is an error, but we'd rather have an
324 	 * error message than an invalid access.
325 	 */
326 	if (bp) {
327 	    for (; (np = bp->next) != 0; bp = np) {
328 		if (ptr == np + 1) {
329 		    bp->next = np->next;
330 		    if (np->next)
331 			np->next->prev = bp;
332 		    mmem->used -= np->size + sizeof(gs_malloc_block_t);
333 		    gs_alloc_fill(np, gs_alloc_fill_free,
334 				  np->size + sizeof(gs_malloc_block_t));
335 		    free(np);
336 		    return;
337 		}
338 	    }
339 	}
340 	lprintf2("%s: free 0x%lx not found!\n",
341 		 client_name_string(cname), (ulong) ptr);
342 	free((char *)((gs_malloc_block_t *) ptr - 1));
343     }
344 }
345 private byte *
gs_heap_alloc_string(gs_memory_t * mem,uint nbytes,client_name_t cname)346 gs_heap_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
347 {
348     return gs_heap_alloc_bytes(mem, nbytes, cname);
349 }
350 private byte *
gs_heap_resize_string(gs_memory_t * mem,byte * data,uint old_num,uint new_num,client_name_t cname)351 gs_heap_resize_string(gs_memory_t * mem, byte * data, uint old_num, uint new_num,
352 		      client_name_t cname)
353 {
354     if (gs_heap_object_type(mem, data) != &st_bytes)
355 	lprintf2("%s: resizing non-string 0x%lx!\n",
356 		 client_name_string(cname), (ulong) data);
357     return gs_heap_resize_object(mem, data, new_num, cname);
358 }
359 private void
gs_heap_free_string(gs_memory_t * mem,byte * data,uint nbytes,client_name_t cname)360 gs_heap_free_string(gs_memory_t * mem, byte * data, uint nbytes,
361 		    client_name_t cname)
362 {
363     /****** SHOULD CHECK SIZE IF DEBUGGING ******/
364     gs_heap_free_object(mem, data, cname);
365 }
366 private int
gs_heap_register_root(gs_memory_t * mem,gs_gc_root_t * rp,gs_ptr_type_t ptype,void ** up,client_name_t cname)367 gs_heap_register_root(gs_memory_t * mem, gs_gc_root_t * rp,
368 		      gs_ptr_type_t ptype, void **up, client_name_t cname)
369 {
370     return 0;
371 }
372 private void
gs_heap_unregister_root(gs_memory_t * mem,gs_gc_root_t * rp,client_name_t cname)373 gs_heap_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp,
374 			client_name_t cname)
375 {
376 }
377 private gs_memory_t *
gs_heap_stable(gs_memory_t * mem)378 gs_heap_stable(gs_memory_t *mem)
379 {
380     return mem;			/* heap memory is stable */
381 }
382 private void
gs_heap_status(gs_memory_t * mem,gs_memory_status_t * pstat)383 gs_heap_status(gs_memory_t * mem, gs_memory_status_t * pstat)
384 {
385     gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
386 
387     pstat->allocated = mmem->used + heap_available();
388     pstat->used = mmem->used;
389 }
390 private void
gs_heap_enable_free(gs_memory_t * mem,bool enable)391 gs_heap_enable_free(gs_memory_t * mem, bool enable)
392 {
393     if (enable)
394 	mem->procs.free_object = gs_heap_free_object,
395 	    mem->procs.free_string = gs_heap_free_string;
396     else
397 	mem->procs.free_object = gs_ignore_free_object,
398 	    mem->procs.free_string = gs_ignore_free_string;
399 }
400 
401 /* Release all memory acquired by this allocator. */
402 private void
gs_heap_free_all(gs_memory_t * mem,uint free_mask,client_name_t cname)403 gs_heap_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
404 {
405     gs_malloc_memory_t *const mmem = (gs_malloc_memory_t *) mem;
406 
407     if (free_mask & FREE_ALL_DATA) {
408 	gs_malloc_block_t *bp = mmem->allocated;
409 	gs_malloc_block_t *np;
410 
411 	for (; bp != 0; bp = np) {
412 	    np = bp->next;
413 	    if_debug3('a', "[a]gs_heap_free_all(%s) 0x%lx(%u)\n",
414 		      client_name_string(bp->cname), (ulong) (bp + 1),
415 		      bp->size);
416 	    gs_alloc_fill(bp + 1, gs_alloc_fill_free, bp->size);
417 	    free(bp);
418 	}
419     }
420     if (free_mask & FREE_ALL_ALLOCATOR)
421 	free(mem);
422 }
423 
424 /* ------ Wrapping ------ */
425 
426 /* Create the retrying and the locked wrapper for the heap allocator. */
427 int
gs_malloc_wrap(gs_memory_t ** wrapped,gs_malloc_memory_t * contents)428 gs_malloc_wrap(gs_memory_t **wrapped, gs_malloc_memory_t *contents)
429 {
430     gs_memory_t *cmem = (gs_memory_t *)contents;
431     gs_memory_locked_t *lmem = (gs_memory_locked_t *)
432 	gs_alloc_bytes_immovable(cmem, sizeof(gs_memory_locked_t),
433 				 "gs_malloc_wrap(locked)");
434     gs_memory_retrying_t *rmem;
435     int code;
436 
437     if (lmem == 0)
438 	return_error(gs_error_VMerror);
439     code = gs_memory_locked_init(lmem, cmem);
440     if (code < 0) {
441 	gs_free_object(cmem, lmem, "gs_malloc_wrap(locked)");
442 	return code;
443     }
444 
445     rmem = (gs_memory_retrying_t *)
446 	gs_alloc_bytes_immovable((gs_memory_t *)lmem,
447 				 sizeof(gs_memory_retrying_t),
448 				 "gs_malloc_wrap(retrying)");
449     if (rmem == 0) {
450 	gs_memory_locked_release(lmem);
451 	gs_free_object(cmem, lmem, "gs_malloc_wrap(locked)");
452 	return_error(gs_error_VMerror);
453     }
454     code = gs_memory_retrying_init(rmem, (gs_memory_t *)lmem);
455     if (code < 0) {
456 	gs_free_object((gs_memory_t *)lmem, rmem, "gs_malloc_wrap(retrying)");
457 	gs_memory_locked_release(lmem);
458 	gs_free_object(cmem, lmem, "gs_malloc_wrap(locked)");
459 	return code;
460     }
461 
462     *wrapped = (gs_memory_t *)rmem;
463     return 0;
464 }
465 
466 /* Get the wrapped contents. */
467 gs_malloc_memory_t *
gs_malloc_wrapped_contents(gs_memory_t * wrapped)468 gs_malloc_wrapped_contents(gs_memory_t *wrapped)
469 {
470     gs_memory_retrying_t *rmem = (gs_memory_retrying_t *)wrapped;
471     gs_memory_locked_t *lmem =
472 	(gs_memory_locked_t *)gs_memory_retrying_target(rmem);
473     if (lmem)
474 	return (gs_malloc_memory_t *)gs_memory_locked_target(lmem);
475     return (gs_malloc_memory_t *) wrapped;
476 }
477 
478 /* Free the wrapper, and return the wrapped contents. */
479 gs_malloc_memory_t *
gs_malloc_unwrap(gs_memory_t * wrapped)480 gs_malloc_unwrap(gs_memory_t *wrapped)
481 {
482     gs_memory_retrying_t *rmem = (gs_memory_retrying_t *)wrapped;
483     gs_memory_locked_t *lmem =
484 	(gs_memory_locked_t *)gs_memory_retrying_target(rmem);
485     gs_memory_t *contents = gs_memory_locked_target(lmem);
486 
487     gs_free_object((gs_memory_t *)lmem, rmem, "gs_malloc_unwrap(retrying)");
488     gs_memory_locked_release(lmem);
489     gs_free_object(contents, lmem, "gs_malloc_unwrap(locked)");
490     return (gs_malloc_memory_t *)contents;
491 }
492 
493 
494 /* Create the default allocator, and return it. */
495 gs_memory_t *
gs_malloc_init(const gs_memory_t * parent)496 gs_malloc_init(const gs_memory_t *parent)
497 {
498     gs_malloc_memory_t *malloc_memory_default = gs_malloc_memory_init();
499     gs_memory_t *memory_t_default;
500 
501     if (parent)
502 	malloc_memory_default->gs_lib_ctx = parent->gs_lib_ctx;
503     else
504         gs_lib_ctx_init((gs_memory_t *)malloc_memory_default);
505 
506     gs_malloc_wrap(&memory_t_default, malloc_memory_default);
507     memory_t_default->stable_memory = memory_t_default;
508     return memory_t_default;
509 }
510 
511 /* Release the default allocator. */
512 void
gs_malloc_release(gs_memory_t * mem)513 gs_malloc_release(gs_memory_t *mem)
514 {
515     gs_malloc_memory_t * malloc_memory_default = gs_malloc_unwrap(mem);
516     gs_malloc_memory_release(malloc_memory_default);
517 }
518