1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: gsmemret.c 8250 2007-09-25 13:31:24Z giles $ */
15 /* Retrying memory allocator */
16 
17 #include "gx.h"
18 #include "gsmemret.h"
19 #include "gserrors.h"
20 
21 /* Raw memory procedures */
22 static gs_memory_proc_alloc_bytes(gs_retrying_alloc_bytes_immovable);
23 static gs_memory_proc_resize_object(gs_retrying_resize_object);
24 static gs_memory_proc_free_object(gs_forward_free_object);
25 static gs_memory_proc_stable(gs_retrying_stable);
26 static gs_memory_proc_status(gs_forward_status);
27 static gs_memory_proc_free_all(gs_forward_free_all);
28 static gs_memory_proc_consolidate_free(gs_forward_consolidate_free);
29 
30 /* Object memory procedures */
31 static gs_memory_proc_alloc_bytes(gs_retrying_alloc_bytes);
32 static gs_memory_proc_alloc_struct(gs_retrying_alloc_struct);
33 static gs_memory_proc_alloc_struct(gs_retrying_alloc_struct_immovable);
34 static gs_memory_proc_alloc_byte_array(gs_retrying_alloc_byte_array);
35 static gs_memory_proc_alloc_byte_array(gs_retrying_alloc_byte_array_immovable);
36 static gs_memory_proc_alloc_struct_array(gs_retrying_alloc_struct_array);
37 static gs_memory_proc_alloc_struct_array(gs_retrying_alloc_struct_array_immovable);
38 static gs_memory_proc_object_size(gs_forward_object_size);
39 static gs_memory_proc_object_type(gs_forward_object_type);
40 static gs_memory_proc_alloc_string(gs_retrying_alloc_string);
41 static gs_memory_proc_alloc_string(gs_retrying_alloc_string_immovable);
42 static gs_memory_proc_resize_string(gs_retrying_resize_string);
43 static gs_memory_proc_free_string(gs_forward_free_string);
44 static gs_memory_proc_register_root(gs_retrying_register_root);
45 static gs_memory_proc_unregister_root(gs_forward_unregister_root);
46 static gs_memory_proc_enable_free(gs_forward_enable_free);
47 static const gs_memory_procs_t retrying_procs = {
48     /* Raw memory procedures */
49     gs_retrying_alloc_bytes_immovable,
50     gs_retrying_resize_object,
51     gs_forward_free_object,
52     gs_retrying_stable,
53     gs_forward_status,
54     gs_forward_free_all,
55     gs_forward_consolidate_free,
56     /* Object memory procedures */
57     gs_retrying_alloc_bytes,
58     gs_retrying_alloc_struct,
59     gs_retrying_alloc_struct_immovable,
60     gs_retrying_alloc_byte_array,
61     gs_retrying_alloc_byte_array_immovable,
62     gs_retrying_alloc_struct_array,
63     gs_retrying_alloc_struct_array_immovable,
64     gs_forward_object_size,
65     gs_forward_object_type,
66     gs_retrying_alloc_string,
67     gs_retrying_alloc_string_immovable,
68     gs_retrying_resize_string,
69     gs_forward_free_string,
70     gs_retrying_register_root,
71     gs_forward_unregister_root,
72     gs_forward_enable_free
73 };
74 
75 /* Define a vacuous recovery procedure. */
76 static gs_memory_recover_status_t
no_recover_proc(gs_memory_retrying_t * rmem,void * proc_data)77 no_recover_proc(gs_memory_retrying_t *rmem, void *proc_data)
78 {
79     return RECOVER_STATUS_NO_RETRY;
80 }
81 
82 /* ---------- Public constructors/destructors ---------- */
83 
84 /* Initialize a gs_memory_retrying_t */
85 int				/* -ve error code or 0 */
gs_memory_retrying_init(gs_memory_retrying_t * rmem,gs_memory_t * target)86 gs_memory_retrying_init(
87 		      gs_memory_retrying_t * rmem,	/* allocator to init */
88 		      gs_memory_t * target	/* allocator to wrap */
89 )
90 {
91     rmem->stable_memory = 0;
92     rmem->procs = retrying_procs;
93     rmem->target = target;
94     rmem->gs_lib_ctx = target->gs_lib_ctx;
95     rmem->non_gc_memory = (gs_memory_t *)rmem;
96     gs_memory_retrying_set_recover(rmem, no_recover_proc, NULL);
97     return 0;
98 }
99 
100 /* Set the recovery closure of a retrying memory manager. */
101 void
gs_memory_retrying_set_recover(gs_memory_retrying_t * rmem,gs_memory_recover_proc_t recover_proc,void * recover_proc_data)102 gs_memory_retrying_set_recover(gs_memory_retrying_t *rmem,
103 			       gs_memory_recover_proc_t recover_proc,
104 			       void *recover_proc_data)
105 {
106     rmem->recover_proc = recover_proc;
107     rmem->recover_proc_data = recover_proc_data;
108 }
109 
110 /* Release a retrying memory manager. */
111 /* Note that this has no effect on the target. */
112 void
gs_memory_retrying_release(gs_memory_retrying_t * rmem)113 gs_memory_retrying_release(gs_memory_retrying_t *rmem)
114 {
115     gs_memory_free_all((gs_memory_t *)rmem, FREE_ALL_STRUCTURES,
116 		       "gs_memory_retrying_release");
117 }
118 
119 /* ---------- Accessors ------------- */
120 
121 /* Retrieve this allocator's target */
122 gs_memory_t *
gs_memory_retrying_target(const gs_memory_retrying_t * rmem)123 gs_memory_retrying_target(const gs_memory_retrying_t *rmem)
124 {
125     return rmem->target;
126 }
127 
128 /* -------- Private members just wrap retrying around a gs_memory --- */
129 
130 /*
131  * Contrary to our usual practice, we don't use BEGIN/END here, because
132  * that causes some compilers to give bogus error messages.
133  */
134 
135 #define DO_FORWARD(call_target)\
136 	gs_memory_retrying_t * const rmem = (gs_memory_retrying_t *)mem;\
137 	gs_memory_t *const target = rmem->target;\
138 \
139 	call_target
140 
141 #define RETURN_RETRYING(result_type, call_target)\
142 	gs_memory_retrying_t * const rmem = (gs_memory_retrying_t *)mem;\
143 	gs_memory_t *const target = rmem->target;\
144 	result_type temp;\
145 	gs_memory_recover_status_t retry = RECOVER_STATUS_RETRY_OK;\
146 \
147 	for (;;) {\
148 	    temp = call_target;\
149 	    if (temp != 0 || retry != RECOVER_STATUS_RETRY_OK)\
150 		break;\
151 	    retry = rmem->recover_proc(rmem, rmem->recover_proc_data);\
152 	}\
153 	return temp
154 
155 /* Procedures */
156 static void
gs_forward_free_all(gs_memory_t * mem,uint free_mask,client_name_t cname)157 gs_forward_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
158 {
159     gs_memory_retrying_t * const rmem = (gs_memory_retrying_t *)mem;
160     gs_memory_t * const target = rmem->target;
161 
162     /* Only free the structures and the allocator itself. */
163     rmem->target = 0;
164     if (free_mask & FREE_ALL_ALLOCATOR)
165 	gs_free_object(target, rmem, cname);
166 }
167 static void
gs_forward_consolidate_free(gs_memory_t * mem)168 gs_forward_consolidate_free(gs_memory_t * mem)
169 {
170     DO_FORWARD(target->procs.consolidate_free(target));
171 }
172 static byte *
gs_retrying_alloc_bytes(gs_memory_t * mem,uint size,client_name_t cname)173 gs_retrying_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
174 {
175     RETURN_RETRYING(
176 		    byte *,
177 		    target->procs.alloc_bytes(target, size, cname)
178 		    );
179 }
180 static byte *
gs_retrying_alloc_bytes_immovable(gs_memory_t * mem,uint size,client_name_t cname)181 gs_retrying_alloc_bytes_immovable(gs_memory_t * mem, uint size,
182 				client_name_t cname)
183 {
184     RETURN_RETRYING(
185 		    byte *,
186 		    target->procs.alloc_bytes_immovable(target, size, cname)
187 		    );
188 }
189 static void *
gs_retrying_alloc_struct(gs_memory_t * mem,gs_memory_type_ptr_t pstype,client_name_t cname)190 gs_retrying_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
191 		       client_name_t cname)
192 {
193     RETURN_RETRYING(
194 		    void *,
195 		    target->procs.alloc_struct(target, pstype, cname)
196 		    );
197 }
198 static void *
gs_retrying_alloc_struct_immovable(gs_memory_t * mem,gs_memory_type_ptr_t pstype,client_name_t cname)199 gs_retrying_alloc_struct_immovable(gs_memory_t * mem,
200 			   gs_memory_type_ptr_t pstype, client_name_t cname)
201 {
202     RETURN_RETRYING(
203 		    void *,
204 		    target->procs.alloc_struct_immovable(target, pstype, cname)
205 		    );
206 }
207 static byte *
gs_retrying_alloc_byte_array(gs_memory_t * mem,uint num_elements,uint elt_size,client_name_t cname)208 gs_retrying_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size,
209 			   client_name_t cname)
210 {
211     RETURN_RETRYING(
212 		    byte *,
213 		    target->procs.alloc_byte_array(target, num_elements,
214 						   elt_size, cname)
215 		    );
216 }
217 static byte *
gs_retrying_alloc_byte_array_immovable(gs_memory_t * mem,uint num_elements,uint elt_size,client_name_t cname)218 gs_retrying_alloc_byte_array_immovable(gs_memory_t * mem, uint num_elements,
219 				     uint elt_size, client_name_t cname)
220 {
221     RETURN_RETRYING(
222 		    byte *,
223 		    target->procs.alloc_byte_array_immovable(target,
224 							num_elements, elt_size,
225 							cname)
226 		    );
227 }
228 static void *
gs_retrying_alloc_struct_array(gs_memory_t * mem,uint num_elements,gs_memory_type_ptr_t pstype,client_name_t cname)229 gs_retrying_alloc_struct_array(gs_memory_t * mem, uint num_elements,
230 			   gs_memory_type_ptr_t pstype, client_name_t cname)
231 {
232     RETURN_RETRYING(
233 		    void *,
234 		    target->procs.alloc_struct_array(target, num_elements,
235 						     pstype, cname)
236 		    );
237 }
238 static void *
gs_retrying_alloc_struct_array_immovable(gs_memory_t * mem,uint num_elements,gs_memory_type_ptr_t pstype,client_name_t cname)239 gs_retrying_alloc_struct_array_immovable(gs_memory_t * mem, uint num_elements,
240 			   gs_memory_type_ptr_t pstype, client_name_t cname)
241 {
242     RETURN_RETRYING(
243 		    void *,
244 		    target->procs.alloc_struct_array_immovable(target,
245 							num_elements, pstype,
246 						        cname)
247 		    );
248 }
249 static void *
gs_retrying_resize_object(gs_memory_t * mem,void * obj,uint new_num_elements,client_name_t cname)250 gs_retrying_resize_object(gs_memory_t * mem, void *obj, uint new_num_elements,
251 			client_name_t cname)
252 {
253     RETURN_RETRYING(
254 		    void *,
255 		    target->procs.resize_object(target, obj, new_num_elements,
256 						cname)
257 		    );
258 }
259 static uint
gs_forward_object_size(gs_memory_t * mem,const void * ptr)260 gs_forward_object_size(gs_memory_t * mem, const void *ptr)
261 {
262     DO_FORWARD(return target->procs.object_size(target, ptr));
263 }
264 static gs_memory_type_ptr_t
gs_forward_object_type(const gs_memory_t * mem,const void * ptr)265 gs_forward_object_type(const gs_memory_t * mem, const void *ptr)
266 {
267     DO_FORWARD(return target->procs.object_type(target, ptr));
268 }
269 static void
gs_forward_free_object(gs_memory_t * mem,void * ptr,client_name_t cname)270 gs_forward_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
271 {
272     DO_FORWARD(target->procs.free_object(target, ptr, cname));
273 }
274 static byte *
gs_retrying_alloc_string(gs_memory_t * mem,uint nbytes,client_name_t cname)275 gs_retrying_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
276 {
277     RETURN_RETRYING(
278 		    byte *,
279 		    target->procs.alloc_string(target, nbytes, cname)
280 		    );
281 }
282 static byte *
gs_retrying_alloc_string_immovable(gs_memory_t * mem,uint nbytes,client_name_t cname)283 gs_retrying_alloc_string_immovable(gs_memory_t * mem, uint nbytes,
284 				 client_name_t cname)
285 {
286     RETURN_RETRYING(
287 		    byte *,
288 		    target->procs.alloc_string_immovable(target, nbytes, cname)
289 		    );
290 }
291 static byte *
gs_retrying_resize_string(gs_memory_t * mem,byte * data,uint old_num,uint new_num,client_name_t cname)292 gs_retrying_resize_string(gs_memory_t * mem, byte * data, uint old_num,
293 			uint new_num,
294 			client_name_t cname)
295 {
296     RETURN_RETRYING(
297 		    byte *,
298 		    target->procs.resize_string(target, data, old_num, new_num,
299 						cname)
300 		    );
301 }
302 static void
gs_forward_free_string(gs_memory_t * mem,byte * data,uint nbytes,client_name_t cname)303 gs_forward_free_string(gs_memory_t * mem, byte * data, uint nbytes,
304 		      client_name_t cname)
305 {
306     DO_FORWARD(target->procs.free_string(target, data, nbytes, cname));
307 }
308 static int
gs_retrying_register_root(gs_memory_t * mem,gs_gc_root_t * rp,gs_ptr_type_t ptype,void ** up,client_name_t cname)309 gs_retrying_register_root(gs_memory_t * mem, gs_gc_root_t * rp,
310 			gs_ptr_type_t ptype, void **up, client_name_t cname)
311 {
312     RETURN_RETRYING(
313 		    int,
314 		    target->procs.register_root(target, rp, ptype, up, cname)
315 		    );
316 }
317 static void
gs_forward_unregister_root(gs_memory_t * mem,gs_gc_root_t * rp,client_name_t cname)318 gs_forward_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp,
319 			  client_name_t cname)
320 {
321     DO_FORWARD(target->procs.unregister_root(target, rp, cname));
322 }
323 static gs_memory_t *
gs_retrying_stable(gs_memory_t * mem)324 gs_retrying_stable(gs_memory_t * mem)
325 {
326     if (!mem->stable_memory) {
327 	gs_memory_retrying_t * const rmem = (gs_memory_retrying_t *)mem;
328 	gs_memory_t *stable = gs_memory_stable(rmem->target);
329 
330 	if (stable == rmem->target)
331 	    mem->stable_memory = mem;
332 	else {
333 	    gs_memory_retrying_t *retrying_stable = (gs_memory_retrying_t *)
334 		gs_alloc_bytes(stable, sizeof(*rmem), "gs_retrying_stable");
335 
336 	    if (retrying_stable) {
337 		int code = gs_memory_retrying_init(retrying_stable, stable);
338 
339 		if (code < 0)
340 		    gs_free_object(stable, retrying_stable, "gs_retrying_stable");
341 		else
342 		    mem->stable_memory = (gs_memory_t *)retrying_stable;
343 	    }
344 	}
345     }
346     return mem->stable_memory;
347 }
348 static void
gs_forward_status(gs_memory_t * mem,gs_memory_status_t * pstat)349 gs_forward_status(gs_memory_t * mem, gs_memory_status_t * pstat)
350 {
351     DO_FORWARD(target->procs.status(target, pstat));
352 }
353 static void
gs_forward_enable_free(gs_memory_t * mem,bool enable)354 gs_forward_enable_free(gs_memory_t * mem, bool enable)
355 {
356     DO_FORWARD(target->procs.enable_free(target, enable));
357 }
358