1 /* Copyright (C) 1995, 1996, 1997, 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: ireclaim.c,v 1.7 2003/09/03 03:22:59 giles Exp $ */
18 /* Interpreter's interface to garbage collector */
19 #include "ghost.h"
20 #include "ierrors.h"
21 #include "gsstruct.h"
22 #include "iastate.h"
23 #include "icontext.h"
24 #include "interp.h"
25 #include "isave.h" /* for isstate.h */
26 #include "isstate.h" /* for mem->saved->state */
27 #include "dstack.h" /* for dsbot, dsp, dict_set_top */
28 #include "estack.h" /* for esbot, esp */
29 #include "ostack.h" /* for osbot, osp */
30 #include "opdef.h" /* for defining init procedure */
31 #include "store.h" /* for make_array */
32
33 /* Import preparation and cleanup routines. */
34 extern void ialloc_gc_prepare(gs_ref_memory_t *);
35
36 /* Forward references */
37 private void gs_vmreclaim(gs_dual_memory_t *, bool);
38
39 /* Initialize the GC hook in the allocator. */
40 private int ireclaim(gs_dual_memory_t *, int);
41 private int
ireclaim_init(i_ctx_t * i_ctx_p)42 ireclaim_init(i_ctx_t *i_ctx_p)
43 {
44 gs_imemory.reclaim = ireclaim;
45 return 0;
46 }
47
48 /* GC hook called when the allocator signals a GC is needed (space = -1), */
49 /* or for vmreclaim (space = the space to collect). */
50 private int
ireclaim(gs_dual_memory_t * dmem,int space)51 ireclaim(gs_dual_memory_t * dmem, int space)
52 {
53 bool global;
54 gs_ref_memory_t *mem;
55
56 if (space < 0) {
57 /* Determine which allocator exceeded the limit. */
58 int i;
59
60 mem = dmem->space_global; /* just in case */
61 for (i = 0; i < countof(dmem->spaces_indexed); ++i) {
62 mem = dmem->spaces_indexed[i];
63 if (mem == 0)
64 continue;
65 if (mem->gc_status.requested > 0 ||
66 ((gs_ref_memory_t *)mem->stable_memory)->gc_status.requested > 0
67 )
68 break;
69 }
70 } else {
71 mem = dmem->spaces_indexed[space >> r_space_shift];
72 }
73 if_debug3('0', "[0]GC called, space=%d, requestor=%d, requested=%ld\n",
74 space, mem->space, (long)mem->gc_status.requested);
75 global = mem->space != avm_local;
76 /* Since dmem may move, reset the request now. */
77 ialloc_reset_requested(dmem);
78 gs_vmreclaim(dmem, global);
79 ialloc_set_limit(mem);
80 if (space < 0) {
81 gs_memory_status_t stats;
82 ulong allocated;
83
84 /* If the ammount still allocated after the GC is complete */
85 /* exceeds the max_vm setting, then return a VMerror */
86 gs_memory_status((gs_memory_t *) mem, &stats);
87 allocated = stats.allocated;
88 if (mem->stable_memory != (gs_memory_t *)mem) {
89 gs_memory_status(mem->stable_memory, &stats);
90 allocated += stats.allocated;
91 }
92 if (allocated >= mem->gc_status.max_vm) {
93 /* We can't satisfy this request within max_vm. */
94 return_error(e_VMerror);
95 }
96 }
97 return 0;
98 }
99
100 /* Interpreter entry to garbage collector. */
101 private void
gs_vmreclaim(gs_dual_memory_t * dmem,bool global)102 gs_vmreclaim(gs_dual_memory_t *dmem, bool global)
103 {
104 /* HACK: we know the gs_dual_memory_t is embedded in a context state. */
105 i_ctx_t *i_ctx_p =
106 (i_ctx_t *)((char *)dmem - offset_of(i_ctx_t, memory));
107 gs_ref_memory_t *lmem = dmem->space_local;
108 int code = context_state_store(i_ctx_p);
109 gs_ref_memory_t *memories[5];
110 gs_ref_memory_t *mem;
111 int nmem, i;
112
113 memories[0] = dmem->space_system;
114 memories[1] = mem = dmem->space_global;
115 nmem = 2;
116 if (lmem != dmem->space_global)
117 memories[nmem++] = lmem;
118 for (i = nmem; --i >= 0;) {
119 mem = memories[i];
120 if (mem->stable_memory != (gs_memory_t *)mem)
121 memories[nmem++] = (gs_ref_memory_t *)mem->stable_memory;
122 }
123
124 /****** ABORT IF code < 0 ******/
125 for (i = nmem; --i >= 0; )
126 alloc_close_chunk(memories[i]);
127
128 /* Prune the file list so it won't retain potentially collectible */
129 /* files. */
130
131 for (i = (global ? i_vm_system : i_vm_local);
132 i < countof(dmem->spaces_indexed);
133 ++i
134 ) {
135 gs_ref_memory_t *mem = dmem->spaces_indexed[i];
136
137 if (mem == 0 || (i > 0 && mem == dmem->spaces_indexed[i - 1]))
138 continue;
139 if (mem->stable_memory != (gs_memory_t *)mem)
140 ialloc_gc_prepare((gs_ref_memory_t *)mem->stable_memory);
141 for (;; mem = &mem->saved->state) {
142 ialloc_gc_prepare(mem);
143 if (mem->saved == 0)
144 break;
145 }
146 }
147
148 /* Do the actual collection. */
149
150 {
151 void *ctxp = i_ctx_p;
152 gs_gc_root_t context_root;
153
154 gs_register_struct_root((gs_memory_t *)lmem, &context_root,
155 &ctxp, "i_ctx_p root");
156 GS_RECLAIM(&dmem->spaces, global);
157 gs_unregister_root((gs_memory_t *)lmem, &context_root, "i_ctx_p root");
158 i_ctx_p = ctxp;
159 dmem = &i_ctx_p->memory;
160 }
161
162 /* Update caches not handled by context_state_load. */
163
164 *systemdict = *ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 1);
165
166 /* Reload the context state. */
167
168 code = context_state_load(i_ctx_p);
169 /****** ABORT IF code < 0 ******/
170
171 /* Update the cached value pointers in names. */
172
173 dicts_gc_cleanup();
174
175 /* Reopen the active chunks. */
176
177 for (i = 0; i < nmem; ++i)
178 alloc_open_chunk(memories[i]);
179 }
180
181 /* ------ Initialization procedure ------ */
182
183 const op_def ireclaim_l2_op_defs[] =
184 {
185 op_def_end(ireclaim_init)
186 };
187