1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
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.
14
/* $Id: isave.c 8496 2008-01-21 20:59:55Z leonardo $ */
15
/* Save/restore manager for Ghostscript interpreter */
21
#include "stream.h" /* for linking for forgetsave */
28
#include "store.h" /* for ref_assign */
31
#include "gsutil.h" /* gs_next_ids prototype */
33
/* Structure descriptor */
34
private_st_alloc_save();
36
/* Define the maximum amount of data we are willing to scan repeatedly -- */
37
/* see below for details. */
38
static const long max_repeated_scan = 100000;
40
/* Define the minimum space for creating an inner chunk. */
41
/* Must be at least sizeof(chunk_head_t). */
42
static const long min_inner_chunk_space = sizeof(chunk_head_t) + 500;
45
* The logic for saving and restoring the state is complex.
46
* Both the changes to individual objects, and the overall state
47
* of the memory manager, must be saved and restored.
51
* To save the state of the memory manager:
52
* Save the state of the current chunk in which we are allocating.
53
* Shrink all chunks to their inner unallocated region.
54
* Save and reset the free block chains.
55
* By doing this, we guarantee that no object older than the save
58
* To restore the state of the memory manager:
59
* Free all chunks newer than the save, and the descriptors for
60
* the inner chunks created by the save.
61
* Make current the chunk that was current at the time of the save.
62
* Restore the state of the current chunk.
64
* In addition to save ("start transaction") and restore ("abort transaction"),
65
* we support forgetting a save ("commit transation"). To forget a save:
66
* Reassign to the next outer save all chunks newer than the save.
67
* Free the descriptors for the inners chunk, updating their outer
68
* chunks to reflect additional allocations in the inner chunks.
69
* Concatenate the free block chains with those of the outer save.
73
* For saving changes to individual objects, we add an "attribute" bit
74
* (l_new) that logically belongs to the slot where the ref is stored,
75
* not to the ref itself. The bit means "the contents of this slot
76
* have been changed, or the slot was allocated, since the last save."
77
* To keep track of changes since the save, we associate a chain of
78
* <slot, old_contents> pairs that remembers the old contents of slots.
80
* When creating an object, if the save level is non-zero:
81
* Set l_new in all slots.
83
* When storing into a slot, if the save level is non-zero:
84
* If l_new isn't set, save the address and contents of the slot
85
* on the current contents chain.
86
* Set l_new after storing the new value.
89
* If the save level is non-zero:
90
* Reset l_new in all slots on the contents chain, and in all
91
* objects created since the previous save.
92
* Push the head of the contents chain, and reset the chain to empty.
95
* Check all the stacks to make sure they don't contain references
96
* to objects created since the save.
97
* Restore all the slots on the contents chain.
98
* Pop the contents chain head.
99
* If the save level is now non-zero:
100
* Scan the newly restored contents chain, and set l_new in all
101
* the slots it references.
102
* Scan all objects created since the previous save, and set
103
* l_new in all the slots of each object.
106
* If the save level is greater than 1:
107
* Set l_new as for a restore, per the next outer save.
108
* Concatenate the next outer contents chain to the end of
110
* If the save level is 1:
111
* Reset l_new as for a save.
112
* Free the contents chain.
116
* A consequence of the foregoing algorithms is that the cost of a save is
117
* proportional to the total amount of data allocated since the previous
118
* save. If a PostScript program reads in a large amount of setup code and
119
* then uses save/restore heavily, each save/restore will be expensive. To
120
* mitigate this, we check to see how much data we have scanned at this save
121
* level: if it is large, we do a second, invisible save. This greatly
122
* reduces the cost of inner saves, at the expense of possibly saving some
123
* changes twice that otherwise would only have to be saved once.
127
* The presence of global and local VM complicates the situation further.
128
* There is a separate save chain and contents chain for each VM space.
129
* When multiple contexts are fully implemented, save and restore will have
130
* the following effects, according to the privacy status of the current
131
* context's global and local VM:
132
* Private global, private local:
133
* The outermost save saves both global and local VM;
134
* otherwise, save only saves local VM.
135
* Shared global, private local:
136
* Save only saves local VM.
137
* Shared global, shared local:
138
* Save only saves local VM, and suspends all other contexts
139
* sharing the same local VM until the matching restore.
140
* Since we do not currently implement multiple contexts, only the first
143
* Note that when saving the contents of a slot, the choice of chain
144
* is determined by the VM space in which the slot is allocated,
145
* not by the current allocation mode.
148
/* Tracing printout */
150
print_save(const char *str, uint spacen, const alloc_save_t *sav)
152
if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
153
str, spacen, (ulong)sav, (ulong)sav->client_data, (ulong)sav->id);
156
/* A link to igcref.c . */
157
ptr_proc_reloc(igc_reloc_ref_ptr_nocheck, ref_packed);
160
* Structure for saved change chain for save/restore. Because of the
161
* garbage collector, we need to distinguish the cases where the change
162
* is in a static object, a dynamic ref, or a dynamic struct.
164
typedef struct alloc_change_s alloc_change_t;
165
struct alloc_change_s {
166
alloc_change_t *next;
169
#define AC_OFFSET_STATIC (-2) /* static object */
170
#define AC_OFFSET_REF (-1) /* dynamic ref */
171
#define AC_OFFSET_ALLOCATED (-3) /* a newly allocated ref array */
172
short offset; /* if >= 0, offset within struct */
176
CLEAR_MARKS_PROC(change_clear_marks)
178
alloc_change_t *const ptr = (alloc_change_t *)vptr;
180
if (r_is_packed(&ptr->contents))
181
r_clear_pmark((ref_packed *) & ptr->contents);
183
r_clear_attrs(&ptr->contents, l_mark);
186
ENUM_PTRS_WITH(change_enum_ptrs, alloc_change_t *ptr) return 0;
187
ENUM_PTR(0, alloc_change_t, next);
189
if (ptr->offset >= 0)
190
ENUM_RETURN((byte *) ptr->where - ptr->offset);
192
if (ptr->offset != AC_OFFSET_ALLOCATED)
193
ENUM_RETURN_REF(ptr->where);
195
/* Don't enumerate ptr->where, because it
196
needs a special processing with
197
alloc_save__filter_changes. */
201
ENUM_RETURN_REF(&ptr->contents);
203
static RELOC_PTRS_WITH(change_reloc_ptrs, alloc_change_t *ptr)
205
RELOC_VAR(ptr->next);
206
switch (ptr->offset) {
207
case AC_OFFSET_STATIC:
210
RELOC_REF_PTR_VAR(ptr->where);
212
case AC_OFFSET_ALLOCATED:
213
/* We know that ptr->where may point to an unmarked object
214
because change_enum_ptrs skipped it,
215
and we know it always points to same space
216
because we took a special care when calling alloc_save_change_alloc.
217
Therefore we must skip the check for the mark,
218
which would happen if we call the regular relocation function
219
igc_reloc_ref_ptr from RELOC_REF_PTR_VAR.
220
Calling igc_reloc_ref_ptr_nocheck instead. */
221
{ /* A sanity check. */
222
obj_header_t *pre = (obj_header_t *)ptr->where - 1, *pre1 = 0;
224
if (pre->o_type != &st_refs)
225
pre1->o_type = 0; /* issue a segfault. */
227
if (ptr->where != 0 && !gcst->relocating_untraced)
228
ptr->where = igc_reloc_ref_ptr_nocheck(ptr->where, gcst);
232
byte *obj = (byte *) ptr->where - ptr->offset;
235
ptr->where = (ref_packed *) (obj + ptr->offset);
239
if (r_is_packed(&ptr->contents))
240
r_clear_pmark((ref_packed *) & ptr->contents);
242
RELOC_REF_VAR(ptr->contents);
243
r_clear_attrs(&ptr->contents, l_mark);
247
gs_private_st_complex_only(st_alloc_change, alloc_change_t, "alloc_change",
248
change_clear_marks, change_enum_ptrs, change_reloc_ptrs, 0);
250
/* Debugging printout */
253
alloc_save_print(alloc_change_t * cp, bool print_current)
255
dprintf2(" 0x%lx: 0x%lx: ", (ulong) cp, (ulong) cp->where);
256
if (r_is_packed(&cp->contents)) {
258
dprintf2("saved=%x cur=%x\n", *(ref_packed *) & cp->contents,
261
dprintf1("%x\n", *(ref_packed *) & cp->contents);
264
dprintf6("saved=%x %x %lx cur=%x %x %lx\n",
265
r_type_attrs(&cp->contents), r_size(&cp->contents),
266
(ulong) cp->contents.value.intval,
267
r_type_attrs((ref *) cp->where),
268
r_size((ref *) cp->where),
269
(ulong) ((ref *) cp->where)->value.intval);
271
dprintf3("%x %x %lx\n",
272
r_type_attrs(&cp->contents), r_size(&cp->contents),
273
(ulong) cp->contents.value.intval);
278
/* Forward references */
279
static int restore_resources(alloc_save_t *, gs_ref_memory_t *);
280
static void restore_free(gs_ref_memory_t *);
281
static int save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned);
282
static int save_set_new_changes(gs_ref_memory_t *, bool, bool);
283
static bool check_l_mark(void *obj);
285
/* Initialize the save/restore machinery. */
287
alloc_save_init(gs_dual_memory_t * dmem)
289
alloc_set_not_in_save(dmem);
292
/* Record that we are in a save. */
294
alloc_set_masks(gs_dual_memory_t *dmem, uint new_mask, uint test_mask)
297
gs_ref_memory_t *mem;
299
dmem->new_mask = new_mask;
300
dmem->test_mask = test_mask;
301
for (i = 0; i < countof(dmem->spaces.memories.indexed); ++i)
302
if ((mem = dmem->spaces.memories.indexed[i]) != 0) {
303
mem->new_mask = new_mask, mem->test_mask = test_mask;
304
if (mem->stable_memory != (gs_memory_t *)mem) {
305
mem = (gs_ref_memory_t *)mem->stable_memory;
306
mem->new_mask = new_mask, mem->test_mask = test_mask;
311
alloc_set_in_save(gs_dual_memory_t *dmem)
313
alloc_set_masks(dmem, l_new, l_new);
316
/* Record that we are not in a save. */
318
alloc_set_not_in_save(gs_dual_memory_t *dmem)
320
alloc_set_masks(dmem, 0, ~0);
323
/* Save the state. */
324
static alloc_save_t *alloc_save_space(gs_ref_memory_t *mem,
325
gs_dual_memory_t *dmem,
328
alloc_free_save(gs_ref_memory_t *mem, alloc_save_t *save, const char *scn)
330
gs_free_object((gs_memory_t *)mem, save, scn);
331
/* Free any inner chunk structures. This is the easiest way to do it. */
335
alloc_save_state(gs_dual_memory_t * dmem, void *cdata, ulong *psid)
337
gs_ref_memory_t *lmem = dmem->space_local;
338
gs_ref_memory_t *gmem = dmem->space_global;
339
ulong sid = gs_next_ids((const gs_memory_t *)lmem->stable_memory, 2);
341
lmem->save_level == 0 && gmem != lmem &&
342
gmem->num_contexts == 1;
343
alloc_save_t *gsave =
344
(global ? alloc_save_space(gmem, dmem, sid + 1) : (alloc_save_t *) 0);
345
alloc_save_t *lsave = alloc_save_space(lmem, dmem, sid);
347
if (lsave == 0 || (global && gsave == 0)) {
349
alloc_free_save(lmem, lsave, "alloc_save_state(local save)");
351
alloc_free_save(gmem, gsave, "alloc_save_state(global save)");
355
gsave->client_data = 0;
356
print_save("save", gmem->space, gsave);
357
/* Restore names when we do the local restore. */
358
lsave->restore_names = gsave->restore_names;
359
gsave->restore_names = false;
362
lsave->client_data = cdata;
363
print_save("save", lmem->space, lsave);
364
/* Reset the l_new attribute in all slots. The only slots that */
365
/* can have the attribute set are the ones on the changes chain, */
366
/* and ones in objects allocated since the last save. */
367
if (lmem->save_level > 1) {
369
int code = save_set_new(&lsave->state, false, true, &scanned);
373
#if 0 /* Disable invisible save levels. */
374
if ((lsave->state.total_scanned += scanned) > max_repeated_scan) {
375
/* Do a second, invisible save. */
378
rsave = alloc_save_space(lmem, dmem, 0L);
380
rsave->client_data = cdata;
381
#if 0 /* Bug 688153 */
382
rsave->id = lsave->id;
383
print_save("save", lmem->space, rsave);
384
lsave->id = 0; /* mark as invisible */
385
rsave->state.save_level--; /* ditto */
386
lsave->client_data = 0;
388
rsave->id = 0; /* mark as invisible */
389
print_save("save", lmem->space, rsave);
390
rsave->state.save_level--; /* ditto */
391
rsave->client_data = 0;
393
/* Inherit the allocated space count -- */
394
/* we need this for triggering a GC. */
395
print_save("save", lmem->space, lsave);
400
alloc_set_in_save(dmem);
404
/* Save the state of one space (global or local). */
405
static alloc_save_t *
406
alloc_save_space(gs_ref_memory_t * mem, gs_dual_memory_t * dmem, ulong sid)
408
gs_ref_memory_t save_mem;
411
chunk_t *new_pcc = 0;
414
alloc_close_chunk(mem);
416
gs_memory_status((gs_memory_t *) mem, &mem->previous_status);
419
/* Create inner chunks wherever it's worthwhile. */
421
for (cp = save_mem.cfirst; cp != 0; cp = cp->cnext) {
422
if (cp->ctop - cp->cbot > min_inner_chunk_space) {
423
/* Create an inner chunk to cover only the unallocated part. */
425
gs_raw_alloc_struct_immovable(mem->non_gc_memory, &st_chunk,
426
"alloc_save_space(inner)");
429
break; /* maybe should fail */
430
alloc_init_chunk(inner, cp->cbot, cp->ctop, cp->sreloc != 0, cp);
431
alloc_link_chunk(inner, mem);
432
if_debug2('u', "[u]inner chunk: cbot=0x%lx ctop=0x%lx\n",
433
(ulong) inner->cbot, (ulong) inner->ctop);
434
if (cp == save_mem.pcc)
439
alloc_open_chunk(mem);
441
save = gs_alloc_struct((gs_memory_t *) mem, alloc_save_t,
442
&st_alloc_save, "alloc_save_space(save)");
443
if_debug2('u', "[u]save space %u at 0x%lx\n",
444
mem->space, (ulong) save);
446
/* Free the inner chunk structures. This is the easiest way. */
451
save->state = save_mem;
452
save->spaces = dmem->spaces;
453
save->restore_names = (name_memory(mem) == (gs_memory_t *) mem);
454
save->is_current = (dmem->current == mem);
457
if_debug2('u', "[u%u]file_save 0x%lx\n",
458
mem->space, (ulong) mem->streams);
460
mem->total_scanned = 0;
461
mem->total_scanned_after_compacting = 0;
467
/* Record a state change that must be undone for restore, */
468
/* and mark it as having been saved. */
470
alloc_save_change_in(gs_ref_memory_t *mem, const ref * pcont,
471
ref_packed * where, client_name_t cname)
473
register alloc_change_t *cp;
475
if (mem->new_mask == 0)
476
return 0; /* no saving */
477
cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
478
&st_alloc_change, "alloc_save_change");
481
cp->next = mem->changes;
484
cp->offset = AC_OFFSET_STATIC;
485
else if (r_is_array(pcont) || r_has_type(pcont, t_dictionary))
486
cp->offset = AC_OFFSET_REF;
487
else if (r_is_struct(pcont))
488
cp->offset = (byte *) where - (byte *) pcont->value.pstruct;
490
lprintf3("Bad type %u for save! pcont = 0x%lx, where = 0x%lx\n",
491
r_type(pcont), (ulong) pcont, (ulong) where);
492
gs_abort((const gs_memory_t *)mem);
494
if (r_is_packed(where))
495
*(ref_packed *)&cp->contents = *where;
497
ref_assign_inline(&cp->contents, (ref *) where);
498
r_set_attrs((ref *) where, l_new);
502
if (gs_debug_c('U')) {
503
dlprintf1("[U]save(%s)", client_name_string(cname));
504
alloc_save_print(cp, false);
510
alloc_save_change(gs_dual_memory_t * dmem, const ref * pcont,
511
ref_packed * where, client_name_t cname)
513
gs_ref_memory_t *mem =
514
(pcont == NULL ? dmem->space_local :
515
dmem->spaces_indexed[r_space(pcont) >> r_space_shift]);
517
return alloc_save_change_in(mem, pcont, where, cname);
520
/* Allocate a structure for recording an allocation event. */
522
alloc_save_change_alloc(gs_ref_memory_t *mem, client_name_t cname, ref_packed ***ppr)
524
register alloc_change_t *cp;
526
if (mem->new_mask == 0)
527
return 0; /* no saving */
528
cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
529
&st_alloc_change, "alloc_save_change");
531
return_error(e_VMerror);
532
cp->next = mem->changes;
534
cp->offset = AC_OFFSET_ALLOCATED;
535
make_null(&cp->contents);
541
/* Remove an AC_OFFSET_ALLOCATED element. */
543
alloc_save_remove(gs_ref_memory_t *mem, ref_packed *obj, client_name_t cname)
545
alloc_change_t **cpp = &mem->changes;
547
for (; *cpp != NULL;) {
548
alloc_change_t *cp = *cpp;
550
if (cp->offset == AC_OFFSET_ALLOCATED && cp->where == obj) {
551
if (mem->scan_limit == cp)
552
mem->scan_limit = cp->next;
554
gs_free_object((gs_memory_t *)mem, cp, "alloc_save_remove");
560
/* Filter save change lists. */
562
alloc_save__filter_changes_in_space(gs_ref_memory_t *mem)
564
/* This is a special function, which is called
565
from the garbager after setting marks and before collecting
566
unused space. Therefore it just resets marks for
567
elements being released instead releasing them really. */
568
alloc_change_t **cpp = &mem->changes;
570
for (; *cpp != NULL; ) {
571
alloc_change_t *cp = *cpp;
573
if (cp->offset == AC_OFFSET_ALLOCATED && !check_l_mark(cp->where)) {
574
obj_header_t *pre = (obj_header_t *)cp - 1;
578
if (mem->scan_limit == cp)
579
mem->scan_limit = cp->next;
586
/* Filter save change lists. */
588
alloc_save__filter_changes(gs_ref_memory_t *memory)
590
gs_ref_memory_t *mem = memory;
592
for (; mem; mem = &mem->saved->state)
593
alloc_save__filter_changes_in_space(mem);
596
/* Return (the id of) the innermost externally visible save object, */
597
/* i.e., the innermost save with a non-zero ID. */
599
alloc_save_current_id(const gs_dual_memory_t * dmem)
601
const alloc_save_t *save = dmem->space_local->saved;
603
while (save != 0 && save->id == 0)
604
save = save->state.saved;
608
alloc_save_current(const gs_dual_memory_t * dmem)
610
return alloc_find_save(dmem, alloc_save_current_id(dmem));
613
/* Test whether a reference would be invalidated by a restore. */
615
alloc_is_since_save(const void *vptr, const alloc_save_t * save)
617
/* A reference postdates a save iff it is in a chunk allocated */
618
/* since the save (including any carried-over inner chunks). */
620
const char *const ptr = (const char *)vptr;
621
register const gs_ref_memory_t *mem = save->space_local;
623
if_debug2('U', "[U]is_since_save 0x%lx, 0x%lx:\n",
624
(ulong) ptr, (ulong) save);
625
if (mem->saved == 0) { /* This is a special case, the final 'restore' from */
626
/* alloc_restore_all. */
629
/* Check against chunks allocated since the save. */
630
/* (There may have been intermediate saves as well.) */
631
for (;; mem = &mem->saved->state) {
634
if_debug1('U', "[U]checking mem=0x%lx\n", (ulong) mem);
635
for (cp = mem->cfirst; cp != 0; cp = cp->cnext) {
636
if (ptr_is_within_chunk(ptr, cp)) {
637
if_debug3('U', "[U+]in new chunk 0x%lx: 0x%lx, 0x%lx\n",
639
(ulong) cp->cbase, (ulong) cp->cend);
642
if_debug1('U', "[U-]not in 0x%lx\n", (ulong) cp);
644
if (mem->saved == save) { /* We've checked all the more recent saves, */
651
* If we're about to do a global restore (a restore to the level 0),
652
* and there is only one context using this global VM
653
* (the normal case, in which global VM is saved by the
654
* outermost save), we also have to check the global save.
655
* Global saves can't be nested, which makes things easy.
657
if (save->state.save_level == 0 /* Restoring to save level 0 - see bug 688157, 688161 */ &&
658
(mem = save->space_global) != save->space_local &&
659
save->space_global->num_contexts == 1
663
if_debug1('U', "[U]checking global mem=0x%lx\n", (ulong) mem);
664
for (cp = mem->cfirst; cp != 0; cp = cp->cnext)
665
if (ptr_is_within_chunk(ptr, cp)) {
666
if_debug3('U', "[U+] new chunk 0x%lx: 0x%lx, 0x%lx\n",
667
(ulong) cp, (ulong) cp->cbase, (ulong) cp->cend);
676
/* Test whether a name would be invalidated by a restore. */
678
alloc_name_is_since_save(const gs_memory_t *mem,
679
const ref * pnref, const alloc_save_t * save)
681
const name_string_t *pnstr;
683
if (!save->restore_names)
685
pnstr = names_string_inline(mem->gs_lib_ctx->gs_name_table, pnref);
686
if (pnstr->foreign_string)
688
return alloc_is_since_save(pnstr->string_bytes, save);
691
alloc_name_index_is_since_save(const gs_memory_t *mem,
692
uint nidx, const alloc_save_t *save)
694
const name_string_t *pnstr;
696
if (!save->restore_names)
698
pnstr = names_index_string_inline(mem->gs_lib_ctx->gs_name_table, nidx);
699
if (pnstr->foreign_string)
701
return alloc_is_since_save(pnstr->string_bytes, save);
704
/* Check whether any names have been created since a given save */
705
/* that might be released by the restore. */
707
alloc_any_names_since_save(const alloc_save_t * save)
709
return save->restore_names;
712
/* Get the saved state with a given ID. */
714
alloc_find_save(const gs_dual_memory_t * dmem, ulong sid)
716
alloc_save_t *sprev = dmem->space_local->saved;
719
return 0; /* invalid id */
721
if (sprev->id == sid)
723
sprev = sprev->state.saved;
728
/* Get the client data from a saved state. */
730
alloc_save_client_data(const alloc_save_t * save)
732
return save->client_data;
736
* Do one step of restoring the state. The client is responsible for
737
* calling alloc_find_save to get the save object, and for ensuring that
738
* there are no surviving pointers for which alloc_is_since_save is true.
739
* Return true if the argument was the innermost save, in which case
740
* this is the last (or only) step.
741
* Note that "one step" may involve multiple internal steps,
742
* if this is the outermost restore (which requires restoring both local
743
* and global VM) or if we created extra save levels to reduce scanning.
745
static void restore_finalize(gs_ref_memory_t *);
746
static void restore_space(gs_ref_memory_t *, gs_dual_memory_t *);
749
alloc_restore_step_in(gs_dual_memory_t *dmem, alloc_save_t * save)
751
/* Get save->space_* now, because the save object will be freed. */
752
gs_ref_memory_t *lmem = save->space_local;
753
gs_ref_memory_t *gmem = save->space_global;
754
gs_ref_memory_t *mem = lmem;
758
/* Finalize all objects before releasing resources or undoing changes. */
764
restore_finalize(mem); /* finalize objects */
769
while (sprev != save);
770
if (mem->save_level == 0) {
771
/* This is the outermost save, which might also */
772
/* need to restore global VM. */
774
if (mem != lmem && mem->saved != 0)
775
restore_finalize(mem);
778
/* Do one (externally visible) step of restoring the state. */
785
code = restore_resources(sprev, mem); /* release other resources */
788
restore_space(mem, dmem); /* release memory */
792
while (sprev != save);
794
if (mem->save_level == 0) {
795
/* This is the outermost save, which might also */
796
/* need to restore global VM. */
798
if (mem != lmem && mem->saved != 0) {
799
code = restore_resources(mem->saved, mem);
802
restore_space(mem, dmem);
804
alloc_set_not_in_save(dmem);
805
} else { /* Set the l_new attribute in all slots that are now new. */
808
code = save_set_new(mem, true, false, &scanned);
813
return sprev == save;
815
/* Restore the memory of one space, by undoing changes and freeing */
816
/* memory allocated since the save. */
818
restore_space(gs_ref_memory_t * mem, gs_dual_memory_t *dmem)
820
alloc_save_t *save = mem->saved;
823
print_save("restore", mem->space, save);
825
/* Undo changes since the save. */
827
register alloc_change_t *cp = mem->changes;
831
if (gs_debug_c('U')) {
832
dlputs("[U]restore");
833
alloc_save_print(cp, true);
836
if (cp->offset == AC_OFFSET_ALLOCATED)
839
if (r_is_packed(&cp->contents))
840
*cp->where = *(ref_packed *) & cp->contents;
842
ref_assign_inline((ref *) cp->where, &cp->contents);
847
/* Free memory allocated since the save. */
848
/* Note that this frees all chunks except the inner ones */
849
/* belonging to this level. */
853
/* Restore the allocator state. */
855
int num_contexts = mem->num_contexts; /* don't restore */
858
mem->num_contexts = num_contexts;
860
alloc_open_chunk(mem);
862
/* Make the allocator current if it was current before the save. */
863
if (saved.is_current) {
865
dmem->current_space = mem->space;
869
/* Restore to the initial state, releasing all resources. */
870
/* The allocator is no longer usable after calling this routine! */
872
alloc_restore_all(gs_dual_memory_t * dmem)
875
* Save the memory pointers, since freeing space_local will also
878
gs_ref_memory_t *lmem = dmem->space_local;
879
gs_ref_memory_t *gmem = dmem->space_global;
880
gs_ref_memory_t *smem = dmem->space_system;
881
gs_ref_memory_t *mem;
884
/* Restore to a state outside any saves. */
885
while (lmem->save_level != 0) {
886
code = alloc_restore_step_in(dmem, lmem->saved);
891
/* Finalize memory. */
892
restore_finalize(lmem);
893
if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
894
restore_finalize(mem);
895
if (gmem != lmem && gmem->num_contexts == 1) {
896
restore_finalize(gmem);
897
if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
898
restore_finalize(mem);
900
restore_finalize(smem);
902
/* Release resources other than memory, using fake */
903
/* save and memory objects. */
905
alloc_save_t empty_save;
907
empty_save.spaces = dmem->spaces;
908
empty_save.restore_names = false; /* don't bother to release */
909
code = restore_resources(&empty_save, NULL);
914
/* Finally, release memory. */
916
if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
919
if (!--(gmem->num_contexts)) {
921
if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
930
* Finalize objects that will be freed by a restore.
931
* Note that we must temporarily disable the freeing operations
932
* of the allocator while doing this.
935
restore_finalize(gs_ref_memory_t * mem)
939
alloc_close_chunk(mem);
940
gs_enable_free((gs_memory_t *) mem, false);
941
for (cp = mem->clast; cp != 0; cp = cp->cprev) {
942
SCAN_CHUNK_OBJECTS(cp)
944
struct_proc_finalize((*finalize)) =
945
pre->o_type->finalize;
947
if_debug2('u', "[u]restore finalizing %s 0x%lx\n",
948
struct_type_name_string(pre->o_type),
950
(*finalize) (pre + 1);
954
gs_enable_free((gs_memory_t *) mem, true);
957
/* Release resources for a restore */
959
restore_resources(alloc_save_t * sprev, gs_ref_memory_t * mem)
964
/* Note restoring of the file list. */
965
if_debug4('u', "[u%u]file_restore 0x%lx => 0x%lx for 0x%lx\n",
966
mem->space, (ulong)mem->streams,
967
(ulong)sprev->state.streams, (ulong) sprev);
971
/* Remove entries from font and character caches. */
972
code = font_restore(sprev);
976
/* Adjust the name table. */
977
if (sprev->restore_names)
978
names_restore(mem->gs_lib_ctx->gs_name_table, sprev);
982
/* Release memory for a restore. */
984
restore_free(gs_ref_memory_t * mem)
986
/* Free chunks allocated since the save. */
987
gs_free_all((gs_memory_t *) mem);
990
/* Forget a save, by merging this level with the next outer one. */
991
static void file_forget_save(gs_ref_memory_t *);
992
static void combine_space(gs_ref_memory_t *);
993
static void forget_changes(gs_ref_memory_t *);
995
alloc_forget_save_in(gs_dual_memory_t *dmem, alloc_save_t * save)
997
gs_ref_memory_t *mem = save->space_local;
1002
print_save("forget_save", mem->space, save);
1004
/* Iteratively combine the current level with the previous one. */
1009
if (mem->save_level != 0) {
1010
alloc_change_t *chp = mem->changes;
1012
code = save_set_new(&sprev->state, true, false, &scanned);
1015
/* Concatenate the changes chains. */
1017
mem->changes = sprev->state.changes;
1019
while (chp->next != 0)
1021
chp->next = sprev->state.changes;
1023
file_forget_save(mem);
1024
combine_space(mem); /* combine memory */
1026
forget_changes(mem);
1027
code = save_set_new(mem, false, false, &scanned);
1030
file_forget_save(mem);
1031
combine_space(mem); /* combine memory */
1032
/* This is the outermost save, which might also */
1033
/* need to combine global VM. */
1034
mem = save->space_global;
1035
if (mem != save->space_local && mem->saved != 0) {
1036
forget_changes(mem);
1037
code = save_set_new(mem, false, false, &scanned);
1040
file_forget_save(mem);
1043
alloc_set_not_in_save(dmem);
1044
break; /* must be outermost */
1047
while (sprev != save);
1050
/* Combine the chunks of the next outer level with those of the current one, */
1051
/* and free the bookkeeping structures. */
1053
combine_space(gs_ref_memory_t * mem)
1055
alloc_save_t *saved = mem->saved;
1056
gs_ref_memory_t *omem = &saved->state;
1060
alloc_close_chunk(mem);
1061
for (cp = mem->cfirst; cp != 0; cp = csucc) {
1062
csucc = cp->cnext; /* save before relinking */
1064
alloc_link_chunk(cp, omem);
1066
chunk_t *outer = cp->outer;
1068
outer->inner_count--;
1071
if (mem->cfreed.cp == cp)
1072
mem->cfreed.cp = outer;
1073
/* "Free" the header of the inner chunk, */
1074
/* and any immediately preceding gap left by */
1075
/* the GC having compacted the outer chunk. */
1077
obj_header_t *hp = (obj_header_t *) outer->cbot;
1080
hp->o_size = (char *)(cp->chead + 1)
1082
hp->o_type = &st_bytes;
1083
/* The following call is probably not safe. */
1084
#if 0 /* **************** */
1085
gs_free_object((gs_memory_t *) mem,
1086
hp + 1, "combine_space(header)");
1087
#endif /* **************** */
1089
/* Update the outer chunk's allocation pointers. */
1090
outer->cbot = cp->cbot;
1091
outer->rcur = cp->rcur;
1092
outer->rtop = cp->rtop;
1093
outer->ctop = cp->ctop;
1094
outer->has_refs |= cp->has_refs;
1095
gs_free_object(mem->non_gc_memory, cp,
1096
"combine_space(inner)");
1099
/* Update relevant parts of allocator state. */
1100
mem->cfirst = omem->cfirst;
1101
mem->clast = omem->clast;
1102
mem->allocated += omem->allocated;
1103
mem->gc_allocated += omem->allocated;
1104
mem->lost.objects += omem->lost.objects;
1105
mem->lost.refs += omem->lost.refs;
1106
mem->lost.strings += omem->lost.strings;
1107
mem->saved = omem->saved;
1108
mem->previous_status = omem->previous_status;
1109
{ /* Concatenate free lists. */
1112
for (i = 0; i < num_freelists; i++) {
1113
obj_header_t *olist = omem->freelists[i];
1114
obj_header_t *list = mem->freelists[i];
1118
mem->freelists[i] = olist;
1120
while (*(obj_header_t **) list != 0)
1121
list = *(obj_header_t **) list;
1122
*(obj_header_t **) list = olist;
1125
if (omem->largest_free_size > mem->largest_free_size)
1126
mem->largest_free_size = omem->largest_free_size;
1128
gs_free_object((gs_memory_t *) mem, saved, "combine_space(saved)");
1129
alloc_open_chunk(mem);
1131
/* Free the changes chain for a level 0 .forgetsave, */
1132
/* resetting the l_new flag in the changed refs. */
1134
forget_changes(gs_ref_memory_t * mem)
1136
register alloc_change_t *chp = mem->changes;
1137
alloc_change_t *next;
1139
for (; chp; chp = next) {
1140
ref_packed *prp = chp->where;
1142
if_debug1('U', "[U]forgetting change 0x%lx\n", (ulong) chp);
1143
if (chp->offset == AC_OFFSET_ALLOCATED)
1146
if (!r_is_packed(prp))
1147
r_clear_attrs((ref *) prp, l_new);
1149
gs_free_object((gs_memory_t *) mem, chp, "forget_changes");
1153
/* Update the streams list when forgetting a save. */
1155
file_forget_save(gs_ref_memory_t * mem)
1157
const alloc_save_t *save = mem->saved;
1158
stream *streams = mem->streams;
1159
stream *saved_streams = save->state.streams;
1161
if_debug4('u', "[u%d]file_forget_save 0x%lx + 0x%lx for 0x%lx\n",
1162
mem->space, (ulong) streams, (ulong) saved_streams,
1165
mem->streams = saved_streams;
1166
else if (saved_streams != 0) {
1167
while (streams->next != 0)
1168
streams = streams->next;
1169
streams->next = saved_streams;
1170
saved_streams->prev = streams;
1175
mark_allocated(void *obj, bool to_new, uint *psize)
1177
obj_header_t *pre = (obj_header_t *)obj - 1;
1178
uint size = pre_obj_contents_size(pre);
1179
ref_packed *prp = (ref_packed *) (pre + 1);
1180
ref_packed *next = (ref_packed *) ((char *)prp + size);
1181
#ifdef ALIGNMENT_ALIASING_BUG
1183
# define RP_REF(rp) (rpref = (ref *)rp, rpref)
1185
# define RP_REF(rp) ((ref *)rp)
1188
if (pre->o_type != &st_refs) {
1189
/* Must not happen. */
1190
if_debug0('u', "Wrong object type when expected a ref.\n");
1191
return_error(e_Fatal);
1193
/* We know that every block of refs ends with */
1194
/* a full-size ref, so we only need the end check */
1195
/* when we encounter one of those. */
1198
if (r_is_packed(prp))
1201
RP_REF(prp)->tas.type_attrs |= l_new;
1202
prp += packed_per_ref;
1208
if (r_is_packed(prp))
1211
RP_REF(prp)->tas.type_attrs &= ~l_new;
1212
prp += packed_per_ref;
1222
/* Check if a block contains refs marked by garbager. */
1224
check_l_mark(void *obj)
1226
obj_header_t *pre = (obj_header_t *)obj - 1;
1227
uint size = pre_obj_contents_size(pre);
1228
ref_packed *prp = (ref_packed *) (pre + 1);
1229
ref_packed *next = (ref_packed *) ((char *)prp + size);
1230
#ifdef ALIGNMENT_ALIASING_BUG
1232
# define RP_REF(rp) (rpref = (ref *)rp, rpref)
1234
# define RP_REF(rp) ((ref *)rp)
1237
/* We know that every block of refs ends with */
1238
/* a full-size ref, so we only need the end check */
1239
/* when we encounter one of those. */
1241
if (r_is_packed(prp)) {
1242
if (r_has_pmark(prp))
1246
if (r_has_attr(RP_REF(prp), l_mark))
1248
prp += packed_per_ref;
1256
/* Set or reset the l_new attribute in every relevant slot. */
1257
/* This includes every slot on the current change chain, */
1258
/* and every (ref) slot allocated at this save level. */
1259
/* Return the number of bytes of data scanned. */
1261
save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned)
1266
/* Handle the change chain. */
1267
code = save_set_new_changes(mem, to_new, set_limit);
1271
/* Handle newly allocated ref objects. */
1272
SCAN_MEM_CHUNKS(mem, cp) {
1274
bool has_refs = false;
1276
SCAN_CHUNK_OBJECTS(cp)
1278
if_debug3('U', "[U]set_new scan(0x%lx(%u), %d)\n",
1279
(ulong) pre, size, to_new);
1280
if (pre->o_type == &st_refs) {
1281
/* These are refs, scan them. */
1282
ref_packed *prp = (ref_packed *) (pre + 1);
1285
code = mark_allocated(prp, to_new, &size);
1290
scanned += sizeof(obj_header_t);
1292
cp->has_refs = has_refs;
1296
if_debug2('u', "[u]set_new (%s) scanned %ld\n",
1297
(to_new ? "restore" : "save"), scanned);
1298
*pscanned = scanned;
1302
/* Drop redundant elements from the changes list and set l_new. */
1304
drop_redundant_changes(gs_ref_memory_t * mem)
1306
register alloc_change_t *chp = mem->changes, *chp_back = NULL, *chp_forth;
1308
/* First reverse the list and set all. */
1309
for (; chp; chp = chp_forth) {
1310
chp_forth = chp->next;
1311
if (chp->offset != AC_OFFSET_ALLOCATED) {
1312
ref_packed *prp = chp->where;
1314
if (!r_is_packed(prp)) {
1315
ref *const rp = (ref *)prp;
1317
rp->tas.type_attrs |= l_new;
1320
chp->next = chp_back;
1323
mem->changes = chp_back;
1325
/* Then filter, reset and reverse again. */
1326
for (chp = mem->changes; chp; chp = chp_forth) {
1327
chp_forth = chp->next;
1328
if (chp->offset != AC_OFFSET_ALLOCATED) {
1329
ref_packed *prp = chp->where;
1331
if (!r_is_packed(prp)) {
1332
ref *const rp = (ref *)prp;
1334
if ((rp->tas.type_attrs & l_new) == 0) {
1335
if (mem->scan_limit == chp)
1336
mem->scan_limit = chp_back;
1337
if (mem->changes == chp)
1338
mem->changes = chp_back;
1339
gs_free_object((gs_memory_t *)mem, chp, "alloc_save_remove");
1342
rp->tas.type_attrs &= ~l_new;
1345
chp->next = chp_back;
1348
mem->changes = chp_back;
1352
/* Set or reset the l_new attribute on the changes chain. */
1354
save_set_new_changes(gs_ref_memory_t * mem, bool to_new, bool set_limit)
1356
register alloc_change_t *chp;
1357
register uint new = (to_new ? l_new : 0);
1360
if (!to_new && mem->total_scanned_after_compacting > max_repeated_scan * 16) {
1361
mem->total_scanned_after_compacting = 0;
1362
drop_redundant_changes(mem);
1364
for (chp = mem->changes; chp; chp = chp->next) {
1365
if (chp->offset == AC_OFFSET_ALLOCATED) {
1366
if (chp->where != 0) {
1368
int code = mark_allocated((void *)chp->where, to_new, &size);
1375
ref_packed *prp = chp->where;
1377
if_debug3('U', "[U]set_new 0x%lx: (0x%lx, %d)\n",
1378
(ulong)chp, (ulong)prp, new);
1379
if (!r_is_packed(prp)) {
1380
ref *const rp = (ref *) prp;
1382
rp->tas.type_attrs =
1383
(rp->tas.type_attrs & ~l_new) + new;
1386
if (mem->scan_limit == chp)
1390
mem->total_scanned_after_compacting += scanned;
1391
if (scanned + mem->total_scanned >= max_repeated_scan) {
1392
mem->scan_limit = mem->changes;
1393
mem->total_scanned = 0;
1395
mem->total_scanned += scanned;