1 /* Copyright (C) 1993, 1995, 1996, 1998, 1999 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: ialloc.c,v 1.8 2005/10/12 10:45:21 leonardo Exp $ */
18 /* Memory allocator for Ghostscript interpreter */
19 #include "gx.h"
20 #include "memory_.h"
21 #include "ierrors.h"
22 #include "gsstruct.h"
23 #include "iref.h" /* must precede iastate.h */
24 #include "iastate.h"
25 #include "igc.h" /* for gs_gc_reclaim */
26 #include "ipacked.h"
27 #include "iutil.h"
28 #include "ivmspace.h"
29 #include "store.h"
30
31 /*
32 * Define global and local instances.
33 */
34 public_st_gs_dual_memory();
35
36 /* Initialize the allocator */
37 int
ialloc_init(gs_dual_memory_t * dmem,gs_memory_t * rmem,uint chunk_size,bool level2)38 ialloc_init(gs_dual_memory_t *dmem, gs_memory_t * rmem, uint chunk_size,
39 bool level2)
40 {
41 gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, chunk_size);
42 gs_ref_memory_t *ilmem_stable = ialloc_alloc_state(rmem, chunk_size);
43 gs_ref_memory_t *igmem = 0;
44 gs_ref_memory_t *igmem_stable = 0;
45 gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, chunk_size);
46 int i;
47
48 if (ilmem == 0 || ilmem_stable == 0 || ismem == 0)
49 goto fail;
50 ilmem->stable_memory = (gs_memory_t *)ilmem_stable;
51 if (level2) {
52 igmem = ialloc_alloc_state(rmem, chunk_size);
53 igmem_stable = ialloc_alloc_state(rmem, chunk_size);
54 if (igmem == 0 || igmem_stable == 0)
55 goto fail;
56 igmem->stable_memory = (gs_memory_t *)igmem_stable;
57 } else
58 igmem = ilmem, igmem_stable = ilmem_stable;
59 for (i = 0; i < countof(dmem->spaces_indexed); i++)
60 dmem->spaces_indexed[i] = 0;
61 dmem->space_local = ilmem;
62 dmem->space_global = igmem;
63 dmem->space_system = ismem;
64 dmem->spaces.vm_reclaim = gs_gc_reclaim; /* real GC */
65 dmem->reclaim = 0; /* no interpreter GC yet */
66 /* Level 1 systems have only local VM. */
67 igmem->space = avm_global;
68 igmem_stable->space = avm_global;
69 ilmem->space = avm_local; /* overrides if ilmem == igmem */
70 ilmem_stable->space = avm_local; /* ditto */
71 ismem->space = avm_system;
72 # if IGC_PTR_STABILITY_CHECK
73 igmem->space_id = (i_vm_global << 1) + 1;
74 igmem_stable->space_id = i_vm_global << 1;
75 ilmem->space_id = (i_vm_local << 1) + 1; /* overrides if ilmem == igmem */
76 ilmem_stable->space_id = i_vm_local << 1; /* ditto */
77 ismem->space_id = (i_vm_system << 1);
78 # endif
79 ialloc_set_space(dmem, avm_global);
80 return 0;
81 fail:
82 gs_free_object(rmem, igmem_stable, "ialloc_init failure");
83 gs_free_object(rmem, igmem, "ialloc_init failure");
84 gs_free_object(rmem, ismem, "ialloc_init failure");
85 gs_free_object(rmem, ilmem_stable, "ialloc_init failure");
86 gs_free_object(rmem, ilmem, "ialloc_init failure");
87 return_error(e_VMerror);
88 }
89
90 /* ================ Local/global VM ================ */
91
92 /* Get the space attribute of an allocator */
93 uint
imemory_space(const gs_ref_memory_t * iimem)94 imemory_space(const gs_ref_memory_t * iimem)
95 {
96 return iimem->space;
97 }
98
99 /* Select the allocation space. */
100 void
ialloc_set_space(gs_dual_memory_t * dmem,uint space)101 ialloc_set_space(gs_dual_memory_t * dmem, uint space)
102 {
103 gs_ref_memory_t *mem = dmem->spaces_indexed[space >> r_space_shift];
104
105 dmem->current = mem;
106 dmem->current_space = mem->space;
107 }
108
109 /* Get the l_new attribute of a current allocator. */
110 /* (A copy of the new_mask in the gs_dual_memory_t.) */
111 uint
imemory_new_mask(const gs_ref_memory_t * imem)112 imemory_new_mask(const gs_ref_memory_t *imem)
113 {
114 return imem->new_mask;
115 }
116
117 /* Get the save level of an allocator. */
118 int
imemory_save_level(const gs_ref_memory_t * imem)119 imemory_save_level(const gs_ref_memory_t *imem)
120 {
121 return imem->save_level;
122 }
123
124 /* Reset the requests. */
125 void
ialloc_reset_requested(gs_dual_memory_t * dmem)126 ialloc_reset_requested(gs_dual_memory_t * dmem)
127 {
128 dmem->space_system->gc_status.requested = 0;
129 dmem->space_global->gc_status.requested = 0;
130 dmem->space_local->gc_status.requested = 0;
131 }
132
133 /* ================ Refs ================ */
134
135 #ifdef DEBUG
136 private int
ialloc_trace_space(const gs_ref_memory_t * imem)137 ialloc_trace_space(const gs_ref_memory_t *imem)
138 {
139 return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
140 }
141 #endif
142
143 /* Register a ref root. */
144 int
gs_register_ref_root(gs_memory_t * mem,gs_gc_root_t * root,void ** pp,client_name_t cname)145 gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t *root,
146 void **pp, client_name_t cname)
147 {
148 return gs_register_root(mem, root, ptr_ref_type, pp, cname);
149 }
150
151 /*
152 * As noted in iastate.h, every run of refs has an extra ref at the end
153 * to hold relocation information for the garbage collector;
154 * since sizeof(ref) % obj_align_mod == 0, we never need to
155 * allocate any additional padding space at the end of the block.
156 */
157
158 /* Allocate an array of refs. */
159 int
gs_alloc_ref_array(gs_ref_memory_t * mem,ref * parr,uint attrs,uint num_refs,client_name_t cname)160 gs_alloc_ref_array(gs_ref_memory_t * mem, ref * parr, uint attrs,
161 uint num_refs, client_name_t cname)
162 {
163 ref *obj;
164
165 /* If we're allocating a run of refs already, */
166 /* and we aren't about to overflow the maximum run length, use it. */
167 if (mem->cc.rtop == mem->cc.cbot &&
168 num_refs < (mem->cc.ctop - mem->cc.cbot) / sizeof(ref) &&
169 mem->cc.rtop - (byte *) mem->cc.rcur + num_refs * sizeof(ref) <
170 max_size_st_refs
171 ) {
172 ref *end;
173
174 obj = (ref *) mem->cc.rtop - 1; /* back up over last ref */
175 if_debug4('A', "[a%d:+$ ]%s(%u) = 0x%lx\n",
176 ialloc_trace_space(mem), client_name_string(cname),
177 num_refs, (ulong) obj);
178 mem->cc.rcur[-1].o_size += num_refs * sizeof(ref);
179 end = (ref *) (mem->cc.rtop = mem->cc.cbot +=
180 num_refs * sizeof(ref));
181 make_mark(end - 1);
182 } else {
183 /*
184 * Allocate a new run. We have to distinguish 3 cases:
185 * - Same chunk: pcc unchanged, end == cc.cbot.
186 * - Large chunk: pcc unchanged, end != cc.cbot.
187 * - New chunk: pcc changed.
188 */
189 chunk_t *pcc = mem->pcc;
190 ref *end;
191
192 obj = gs_alloc_struct_array((gs_memory_t *) mem, num_refs + 1,
193 ref, &st_refs, cname);
194 if (obj == 0)
195 return_error(e_VMerror);
196 /* Set the terminating ref now. */
197 end = (ref *) obj + num_refs;
198 make_mark(end);
199 /* Set has_refs in the chunk. */
200 if (mem->pcc != pcc || mem->cc.cbot == (byte *) (end + 1)) {
201 /* Ordinary chunk. */
202 mem->cc.rcur = (obj_header_t *) obj;
203 mem->cc.rtop = (byte *) (end + 1);
204 mem->cc.has_refs = true;
205 } else {
206 /* Large chunk. */
207 /* This happens only for very large arrays, */
208 /* so it doesn't need to be cheap. */
209 chunk_locator_t cl;
210
211 cl.memory = mem;
212 cl.cp = mem->clast;
213 chunk_locate_ptr(obj, &cl);
214 cl.cp->has_refs = true;
215 }
216 }
217 make_array(parr, attrs | mem->space, num_refs, obj);
218 return 0;
219 }
220
221 /* Resize an array of refs. Currently this is only implemented */
222 /* for shrinking, not for growing. */
223 int
gs_resize_ref_array(gs_ref_memory_t * mem,ref * parr,uint new_num_refs,client_name_t cname)224 gs_resize_ref_array(gs_ref_memory_t * mem, ref * parr,
225 uint new_num_refs, client_name_t cname)
226 {
227 uint old_num_refs = r_size(parr);
228 uint diff;
229 ref *obj = parr->value.refs;
230
231 if (new_num_refs > old_num_refs || !r_has_type(parr, t_array))
232 return_error(e_Fatal);
233 diff = old_num_refs - new_num_refs;
234 /* Check for LIFO. See gs_free_ref_array for more details. */
235 if (mem->cc.rtop == mem->cc.cbot &&
236 (byte *) (obj + (old_num_refs + 1)) == mem->cc.rtop
237 ) {
238 /* Shorten the refs object. */
239 ref *end = (ref *) (mem->cc.cbot = mem->cc.rtop -=
240 diff * sizeof(ref));
241
242 if_debug4('A', "[a%d:<$ ]%s(%u) 0x%lx\n",
243 ialloc_trace_space(mem), client_name_string(cname), diff,
244 (ulong) obj);
245 mem->cc.rcur[-1].o_size -= diff * sizeof(ref);
246 make_mark(end - 1);
247 } else {
248 /* Punt. */
249 if_debug4('A', "[a%d:<$#]%s(%u) 0x%lx\n",
250 ialloc_trace_space(mem), client_name_string(cname), diff,
251 (ulong) obj);
252 mem->lost.refs += diff * sizeof(ref);
253 }
254 r_set_size(parr, new_num_refs);
255 return 0;
256 }
257
258 /* Deallocate an array of refs. Only do this if LIFO, or if */
259 /* the array occupies an entire chunk by itself. */
260 void
gs_free_ref_array(gs_ref_memory_t * mem,ref * parr,client_name_t cname)261 gs_free_ref_array(gs_ref_memory_t * mem, ref * parr, client_name_t cname)
262 {
263 uint num_refs = r_size(parr);
264 ref *obj = parr->value.refs;
265
266 /*
267 * Compute the storage size of the array, and check for LIFO
268 * freeing or a separate chunk. Note that the array might be packed;
269 * for the moment, if it's anything but a t_array, punt.
270 * The +1s are for the extra ref for the GC.
271 */
272 if (!r_has_type(parr, t_array))
273 DO_NOTHING; /* don't look for special cases */
274 else if (mem->cc.rtop == mem->cc.cbot &&
275 (byte *) (obj + (num_refs + 1)) == mem->cc.rtop
276 ) {
277 if ((obj_header_t *) obj == mem->cc.rcur) {
278 /* Deallocate the entire refs object. */
279 gs_free_object((gs_memory_t *) mem, obj, cname);
280 mem->cc.rcur = 0;
281 mem->cc.rtop = 0;
282 } else {
283 /* Deallocate it at the end of the refs object. */
284 if_debug4('A', "[a%d:-$ ]%s(%u) 0x%lx\n",
285 ialloc_trace_space(mem), client_name_string(cname),
286 num_refs, (ulong) obj);
287 mem->cc.rcur[-1].o_size -= num_refs * sizeof(ref);
288 mem->cc.rtop = mem->cc.cbot = (byte *) (obj + 1);
289 make_mark(obj);
290 }
291 return;
292 } else if (num_refs >= (mem->large_size / arch_sizeof_ref - 1)) {
293 /* See if this array has a chunk all to itself. */
294 /* We only make this check when freeing very large objects, */
295 /* so it doesn't need to be cheap. */
296 chunk_locator_t cl;
297
298 cl.memory = mem;
299 cl.cp = mem->clast;
300 if (chunk_locate_ptr(obj, &cl) &&
301 obj == (ref *) ((obj_header_t *) (cl.cp->cbase) + 1) &&
302 (byte *) (obj + (num_refs + 1)) == cl.cp->cend
303 ) {
304 /* Free the chunk. */
305 if_debug4('a', "[a%d:-$L]%s(%u) 0x%lx\n",
306 ialloc_trace_space(mem), client_name_string(cname),
307 num_refs, (ulong) obj);
308 alloc_free_chunk(cl.cp, mem);
309 return;
310 }
311 }
312 /* Punt, but fill the array with nulls so that there won't be */
313 /* dangling references to confuse the garbage collector. */
314 if_debug4('A', "[a%d:-$#]%s(%u) 0x%lx\n",
315 ialloc_trace_space(mem), client_name_string(cname), num_refs,
316 (ulong) obj);
317 {
318 uint size;
319
320 switch (r_type(parr)) {
321 case t_shortarray:
322 size = num_refs * sizeof(ref_packed);
323 break;
324 case t_mixedarray:{
325 /* We have to parse the array to compute the storage size. */
326 uint i = 0;
327 const ref_packed *p = parr->value.packed;
328
329 for (; i < num_refs; ++i)
330 p = packed_next(p);
331 size = (const byte *)p - (const byte *)parr->value.packed;
332 break;
333 }
334 case t_array:
335 size = num_refs * sizeof(ref);
336 break;
337 default:
338 lprintf3("Unknown type 0x%x in free_ref_array(%u,0x%lx)!",
339 r_type(parr), num_refs, (ulong) obj);
340 return;
341 }
342 /*
343 * If there are any leftover packed elements, we don't
344 * worry about them, since they can't be dangling references.
345 */
346 refset_null_new(obj, size / sizeof(ref), 0);
347 mem->lost.refs += size;
348 }
349 }
350
351 /* Allocate a string ref. */
352 int
gs_alloc_string_ref(gs_ref_memory_t * mem,ref * psref,uint attrs,uint nbytes,client_name_t cname)353 gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
354 uint attrs, uint nbytes, client_name_t cname)
355 {
356 byte *str = gs_alloc_string((gs_memory_t *) mem, nbytes, cname);
357
358 if (str == 0)
359 return_error(e_VMerror);
360 make_string(psref, attrs | mem->space, nbytes, str);
361 return 0;
362 }
363