13ff48bf5SDavid du Colombier /* Copyright (C) 1993, 2000 Aladdin Enterprises. All rights reserved.
27dd7cddfSDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
57dd7cddfSDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: isave.c,v 1.14 2005/06/23 07:35:30 igor Exp $ */
187dd7cddfSDavid du Colombier /* Save/restore manager for Ghostscript interpreter */
197dd7cddfSDavid du Colombier #include "ghost.h"
207dd7cddfSDavid du Colombier #include "memory_.h"
21*593dc095SDavid du Colombier #include "ierrors.h"
227dd7cddfSDavid du Colombier #include "gsexit.h"
237dd7cddfSDavid du Colombier #include "gsstruct.h"
247dd7cddfSDavid du Colombier #include "stream.h" /* for linking for forgetsave */
257dd7cddfSDavid du Colombier #include "iastate.h"
267dd7cddfSDavid du Colombier #include "inamedef.h"
277dd7cddfSDavid du Colombier #include "iname.h"
287dd7cddfSDavid du Colombier #include "ipacked.h"
297dd7cddfSDavid du Colombier #include "isave.h"
307dd7cddfSDavid du Colombier #include "isstate.h"
317dd7cddfSDavid du Colombier #include "store.h" /* for ref_assign */
327dd7cddfSDavid du Colombier #include "ivmspace.h"
337dd7cddfSDavid du Colombier #include "gsutil.h" /* gs_next_ids prototype */
347dd7cddfSDavid du Colombier
357dd7cddfSDavid du Colombier
367dd7cddfSDavid du Colombier /* Structure descriptor */
377dd7cddfSDavid du Colombier private_st_alloc_save();
387dd7cddfSDavid du Colombier
397dd7cddfSDavid du Colombier /* Define the maximum amount of data we are willing to scan repeatedly -- */
407dd7cddfSDavid du Colombier /* see below for details. */
417dd7cddfSDavid du Colombier private const long max_repeated_scan = 100000;
427dd7cddfSDavid du Colombier
43*593dc095SDavid du Colombier /* Define the minimum space for creating an inner chunk. */
44*593dc095SDavid du Colombier /* Must be at least sizeof(chunk_head_t). */
45*593dc095SDavid du Colombier private const long min_inner_chunk_space = sizeof(chunk_head_t) + 500;
46*593dc095SDavid du Colombier
477dd7cddfSDavid du Colombier /*
487dd7cddfSDavid du Colombier * The logic for saving and restoring the state is complex.
497dd7cddfSDavid du Colombier * Both the changes to individual objects, and the overall state
507dd7cddfSDavid du Colombier * of the memory manager, must be saved and restored.
517dd7cddfSDavid du Colombier */
527dd7cddfSDavid du Colombier
537dd7cddfSDavid du Colombier /*
547dd7cddfSDavid du Colombier * To save the state of the memory manager:
557dd7cddfSDavid du Colombier * Save the state of the current chunk in which we are allocating.
56*593dc095SDavid du Colombier * Shrink all chunks to their inner unallocated region.
577dd7cddfSDavid du Colombier * Save and reset the free block chains.
587dd7cddfSDavid du Colombier * By doing this, we guarantee that no object older than the save
597dd7cddfSDavid du Colombier * can be freed.
607dd7cddfSDavid du Colombier *
617dd7cddfSDavid du Colombier * To restore the state of the memory manager:
62*593dc095SDavid du Colombier * Free all chunks newer than the save, and the descriptors for
63*593dc095SDavid du Colombier * the inner chunks created by the save.
647dd7cddfSDavid du Colombier * Make current the chunk that was current at the time of the save.
657dd7cddfSDavid du Colombier * Restore the state of the current chunk.
667dd7cddfSDavid du Colombier *
677dd7cddfSDavid du Colombier * In addition to save ("start transaction") and restore ("abort transaction"),
687dd7cddfSDavid du Colombier * we support forgetting a save ("commit transation"). To forget a save:
697dd7cddfSDavid du Colombier * Reassign to the next outer save all chunks newer than the save.
70*593dc095SDavid du Colombier * Free the descriptors for the inners chunk, updating their outer
71*593dc095SDavid du Colombier * chunks to reflect additional allocations in the inner chunks.
727dd7cddfSDavid du Colombier * Concatenate the free block chains with those of the outer save.
737dd7cddfSDavid du Colombier */
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier /*
767dd7cddfSDavid du Colombier * For saving changes to individual objects, we add an "attribute" bit
777dd7cddfSDavid du Colombier * (l_new) that logically belongs to the slot where the ref is stored,
787dd7cddfSDavid du Colombier * not to the ref itself. The bit means "the contents of this slot
797dd7cddfSDavid du Colombier * have been changed, or the slot was allocated, since the last save."
807dd7cddfSDavid du Colombier * To keep track of changes since the save, we associate a chain of
817dd7cddfSDavid du Colombier * <slot, old_contents> pairs that remembers the old contents of slots.
827dd7cddfSDavid du Colombier *
837dd7cddfSDavid du Colombier * When creating an object, if the save level is non-zero:
847dd7cddfSDavid du Colombier * Set l_new in all slots.
857dd7cddfSDavid du Colombier *
867dd7cddfSDavid du Colombier * When storing into a slot, if the save level is non-zero:
877dd7cddfSDavid du Colombier * If l_new isn't set, save the address and contents of the slot
887dd7cddfSDavid du Colombier * on the current contents chain.
897dd7cddfSDavid du Colombier * Set l_new after storing the new value.
907dd7cddfSDavid du Colombier *
917dd7cddfSDavid du Colombier * To do a save:
927dd7cddfSDavid du Colombier * If the save level is non-zero:
937dd7cddfSDavid du Colombier * Reset l_new in all slots on the contents chain, and in all
947dd7cddfSDavid du Colombier * objects created since the previous save.
957dd7cddfSDavid du Colombier * Push the head of the contents chain, and reset the chain to empty.
967dd7cddfSDavid du Colombier *
977dd7cddfSDavid du Colombier * To do a restore:
987dd7cddfSDavid du Colombier * Check all the stacks to make sure they don't contain references
997dd7cddfSDavid du Colombier * to objects created since the save.
1007dd7cddfSDavid du Colombier * Restore all the slots on the contents chain.
1017dd7cddfSDavid du Colombier * Pop the contents chain head.
1027dd7cddfSDavid du Colombier * If the save level is now non-zero:
1037dd7cddfSDavid du Colombier * Scan the newly restored contents chain, and set l_new in all
1047dd7cddfSDavid du Colombier * the slots it references.
1057dd7cddfSDavid du Colombier * Scan all objects created since the previous save, and set
1067dd7cddfSDavid du Colombier * l_new in all the slots of each object.
1077dd7cddfSDavid du Colombier *
1087dd7cddfSDavid du Colombier * To forget a save:
1097dd7cddfSDavid du Colombier * If the save level is greater than 1:
1107dd7cddfSDavid du Colombier * Set l_new as for a restore, per the next outer save.
1117dd7cddfSDavid du Colombier * Concatenate the next outer contents chain to the end of
1127dd7cddfSDavid du Colombier * the current one.
1137dd7cddfSDavid du Colombier * If the save level is 1:
1147dd7cddfSDavid du Colombier * Reset l_new as for a save.
1157dd7cddfSDavid du Colombier * Free the contents chain.
1167dd7cddfSDavid du Colombier */
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier /*
1197dd7cddfSDavid du Colombier * A consequence of the foregoing algorithms is that the cost of a save is
1207dd7cddfSDavid du Colombier * proportional to the total amount of data allocated since the previous
1217dd7cddfSDavid du Colombier * save. If a PostScript program reads in a large amount of setup code and
1227dd7cddfSDavid du Colombier * then uses save/restore heavily, each save/restore will be expensive. To
1237dd7cddfSDavid du Colombier * mitigate this, we check to see how much data we have scanned at this save
1247dd7cddfSDavid du Colombier * level: if it is large, we do a second, invisible save. This greatly
1257dd7cddfSDavid du Colombier * reduces the cost of inner saves, at the expense of possibly saving some
1267dd7cddfSDavid du Colombier * changes twice that otherwise would only have to be saved once.
1277dd7cddfSDavid du Colombier */
1287dd7cddfSDavid du Colombier
1297dd7cddfSDavid du Colombier /*
1307dd7cddfSDavid du Colombier * The presence of global and local VM complicates the situation further.
1317dd7cddfSDavid du Colombier * There is a separate save chain and contents chain for each VM space.
1327dd7cddfSDavid du Colombier * When multiple contexts are fully implemented, save and restore will have
1337dd7cddfSDavid du Colombier * the following effects, according to the privacy status of the current
1347dd7cddfSDavid du Colombier * context's global and local VM:
1357dd7cddfSDavid du Colombier * Private global, private local:
1367dd7cddfSDavid du Colombier * The outermost save saves both global and local VM;
1377dd7cddfSDavid du Colombier * otherwise, save only saves local VM.
1387dd7cddfSDavid du Colombier * Shared global, private local:
1397dd7cddfSDavid du Colombier * Save only saves local VM.
1407dd7cddfSDavid du Colombier * Shared global, shared local:
1417dd7cddfSDavid du Colombier * Save only saves local VM, and suspends all other contexts
1427dd7cddfSDavid du Colombier * sharing the same local VM until the matching restore.
1437dd7cddfSDavid du Colombier * Since we do not currently implement multiple contexts, only the first
1447dd7cddfSDavid du Colombier * case is relevant.
1457dd7cddfSDavid du Colombier *
1467dd7cddfSDavid du Colombier * Note that when saving the contents of a slot, the choice of chain
1477dd7cddfSDavid du Colombier * is determined by the VM space in which the slot is allocated,
1487dd7cddfSDavid du Colombier * not by the current allocation mode.
1497dd7cddfSDavid du Colombier */
1507dd7cddfSDavid du Colombier
1517dd7cddfSDavid du Colombier /* Tracing printout */
1527dd7cddfSDavid du Colombier private void
print_save(const char * str,uint spacen,const alloc_save_t * sav)1537dd7cddfSDavid du Colombier print_save(const char *str, uint spacen, const alloc_save_t *sav)
1547dd7cddfSDavid du Colombier {
1557dd7cddfSDavid du Colombier if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
1567dd7cddfSDavid du Colombier str, spacen, (ulong)sav, (ulong)sav->client_data, (ulong)sav->id);
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier
1597dd7cddfSDavid du Colombier /*
1607dd7cddfSDavid du Colombier * Structure for saved change chain for save/restore. Because of the
1617dd7cddfSDavid du Colombier * garbage collector, we need to distinguish the cases where the change
1627dd7cddfSDavid du Colombier * is in a static object, a dynamic ref, or a dynamic struct.
1637dd7cddfSDavid du Colombier */
1647dd7cddfSDavid du Colombier typedef struct alloc_change_s alloc_change_t;
1657dd7cddfSDavid du Colombier struct alloc_change_s {
1667dd7cddfSDavid du Colombier alloc_change_t *next;
1677dd7cddfSDavid du Colombier ref_packed *where;
1687dd7cddfSDavid du Colombier ref contents;
1697dd7cddfSDavid du Colombier #define AC_OFFSET_STATIC (-2) /* static object */
1707dd7cddfSDavid du Colombier #define AC_OFFSET_REF (-1) /* dynamic ref */
1717dd7cddfSDavid du Colombier short offset; /* if >= 0, offset within struct */
1727dd7cddfSDavid du Colombier };
1737dd7cddfSDavid du Colombier
1747dd7cddfSDavid du Colombier private
CLEAR_MARKS_PROC(change_clear_marks)1757dd7cddfSDavid du Colombier CLEAR_MARKS_PROC(change_clear_marks)
1767dd7cddfSDavid du Colombier {
1777dd7cddfSDavid du Colombier alloc_change_t *const ptr = (alloc_change_t *)vptr;
1787dd7cddfSDavid du Colombier
1797dd7cddfSDavid du Colombier if (r_is_packed(&ptr->contents))
1807dd7cddfSDavid du Colombier r_clear_pmark((ref_packed *) & ptr->contents);
1817dd7cddfSDavid du Colombier else
1827dd7cddfSDavid du Colombier r_clear_attrs(&ptr->contents, l_mark);
1837dd7cddfSDavid du Colombier }
1847dd7cddfSDavid du Colombier private
1857dd7cddfSDavid du Colombier ENUM_PTRS_WITH(change_enum_ptrs, alloc_change_t *ptr) return 0;
1867dd7cddfSDavid du Colombier ENUM_PTR(0, alloc_change_t, next);
1877dd7cddfSDavid du Colombier case 1:
1887dd7cddfSDavid du Colombier if (ptr->offset >= 0)
1897dd7cddfSDavid du Colombier ENUM_RETURN((byte *) ptr->where - ptr->offset);
1907dd7cddfSDavid du Colombier else
1917dd7cddfSDavid du Colombier ENUM_RETURN_REF(ptr->where);
1927dd7cddfSDavid du Colombier case 2:
1937dd7cddfSDavid du Colombier ENUM_RETURN_REF(&ptr->contents);
1947dd7cddfSDavid du Colombier ENUM_PTRS_END
RELOC_PTRS_WITH(change_reloc_ptrs,alloc_change_t * ptr)1957dd7cddfSDavid du Colombier private RELOC_PTRS_WITH(change_reloc_ptrs, alloc_change_t *ptr)
1967dd7cddfSDavid du Colombier {
1977dd7cddfSDavid du Colombier RELOC_VAR(ptr->next);
1987dd7cddfSDavid du Colombier switch (ptr->offset) {
1997dd7cddfSDavid du Colombier case AC_OFFSET_STATIC:
2007dd7cddfSDavid du Colombier break;
2017dd7cddfSDavid du Colombier case AC_OFFSET_REF:
2027dd7cddfSDavid du Colombier RELOC_REF_PTR_VAR(ptr->where);
2037dd7cddfSDavid du Colombier break;
2047dd7cddfSDavid du Colombier default:
2057dd7cddfSDavid du Colombier {
2067dd7cddfSDavid du Colombier byte *obj = (byte *) ptr->where - ptr->offset;
2077dd7cddfSDavid du Colombier
2087dd7cddfSDavid du Colombier RELOC_VAR(obj);
2097dd7cddfSDavid du Colombier ptr->where = (ref_packed *) (obj + ptr->offset);
2107dd7cddfSDavid du Colombier }
2117dd7cddfSDavid du Colombier break;
2127dd7cddfSDavid du Colombier }
2137dd7cddfSDavid du Colombier if (r_is_packed(&ptr->contents))
2147dd7cddfSDavid du Colombier r_clear_pmark((ref_packed *) & ptr->contents);
2157dd7cddfSDavid du Colombier else {
2167dd7cddfSDavid du Colombier RELOC_REF_VAR(ptr->contents);
2177dd7cddfSDavid du Colombier r_clear_attrs(&ptr->contents, l_mark);
2187dd7cddfSDavid du Colombier }
2197dd7cddfSDavid du Colombier }
2207dd7cddfSDavid du Colombier RELOC_PTRS_END
2217dd7cddfSDavid du Colombier gs_private_st_complex_only(st_alloc_change, alloc_change_t, "alloc_change",
2227dd7cddfSDavid du Colombier change_clear_marks, change_enum_ptrs, change_reloc_ptrs, 0);
2237dd7cddfSDavid du Colombier
2247dd7cddfSDavid du Colombier /* Debugging printout */
2257dd7cddfSDavid du Colombier #ifdef DEBUG
2267dd7cddfSDavid du Colombier private void
alloc_save_print(alloc_change_t * cp,bool print_current)2277dd7cddfSDavid du Colombier alloc_save_print(alloc_change_t * cp, bool print_current)
2287dd7cddfSDavid du Colombier {
2297dd7cddfSDavid du Colombier dprintf2(" 0x%lx: 0x%lx: ", (ulong) cp, (ulong) cp->where);
2307dd7cddfSDavid du Colombier if (r_is_packed(&cp->contents)) {
2317dd7cddfSDavid du Colombier if (print_current)
2327dd7cddfSDavid du Colombier dprintf2("saved=%x cur=%x\n", *(ref_packed *) & cp->contents,
2337dd7cddfSDavid du Colombier *cp->where);
2347dd7cddfSDavid du Colombier else
2357dd7cddfSDavid du Colombier dprintf1("%x\n", *(ref_packed *) & cp->contents);
2367dd7cddfSDavid du Colombier } else {
2377dd7cddfSDavid du Colombier if (print_current)
2387dd7cddfSDavid du Colombier dprintf6("saved=%x %x %lx cur=%x %x %lx\n",
2397dd7cddfSDavid du Colombier r_type_attrs(&cp->contents), r_size(&cp->contents),
2407dd7cddfSDavid du Colombier (ulong) cp->contents.value.intval,
2417dd7cddfSDavid du Colombier r_type_attrs((ref *) cp->where),
2427dd7cddfSDavid du Colombier r_size((ref *) cp->where),
2437dd7cddfSDavid du Colombier (ulong) ((ref *) cp->where)->value.intval);
2447dd7cddfSDavid du Colombier else
2457dd7cddfSDavid du Colombier dprintf3("%x %x %lx\n",
2467dd7cddfSDavid du Colombier r_type_attrs(&cp->contents), r_size(&cp->contents),
2477dd7cddfSDavid du Colombier (ulong) cp->contents.value.intval);
2487dd7cddfSDavid du Colombier }
2497dd7cddfSDavid du Colombier }
2507dd7cddfSDavid du Colombier #endif
2517dd7cddfSDavid du Colombier
2527dd7cddfSDavid du Colombier /* Forward references */
253*593dc095SDavid du Colombier private void restore_resources(alloc_save_t *, gs_ref_memory_t *);
254*593dc095SDavid du Colombier private void restore_free(gs_ref_memory_t *);
255*593dc095SDavid du Colombier private long save_set_new(gs_ref_memory_t *, bool);
256*593dc095SDavid du Colombier private void save_set_new_changes(gs_ref_memory_t *, bool);
2577dd7cddfSDavid du Colombier
2587dd7cddfSDavid du Colombier /* Initialize the save/restore machinery. */
2597dd7cddfSDavid du Colombier void
alloc_save_init(gs_dual_memory_t * dmem)2607dd7cddfSDavid du Colombier alloc_save_init(gs_dual_memory_t * dmem)
2617dd7cddfSDavid du Colombier {
2627dd7cddfSDavid du Colombier alloc_set_not_in_save(dmem);
2637dd7cddfSDavid du Colombier }
2647dd7cddfSDavid du Colombier
2657dd7cddfSDavid du Colombier /* Record that we are in a save. */
2667dd7cddfSDavid du Colombier private void
alloc_set_masks(gs_dual_memory_t * dmem,uint new_mask,uint test_mask)2677dd7cddfSDavid du Colombier alloc_set_masks(gs_dual_memory_t *dmem, uint new_mask, uint test_mask)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier int i;
2707dd7cddfSDavid du Colombier gs_ref_memory_t *mem;
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier dmem->new_mask = new_mask;
2737dd7cddfSDavid du Colombier dmem->test_mask = test_mask;
2747dd7cddfSDavid du Colombier for (i = 0; i < countof(dmem->spaces.memories.indexed); ++i)
2757dd7cddfSDavid du Colombier if ((mem = dmem->spaces.memories.indexed[i]) != 0) {
2767dd7cddfSDavid du Colombier mem->new_mask = new_mask, mem->test_mask = test_mask;
2777dd7cddfSDavid du Colombier if (mem->stable_memory != (gs_memory_t *)mem) {
2787dd7cddfSDavid du Colombier mem = (gs_ref_memory_t *)mem->stable_memory;
2797dd7cddfSDavid du Colombier mem->new_mask = new_mask, mem->test_mask = test_mask;
2807dd7cddfSDavid du Colombier }
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier void
alloc_set_in_save(gs_dual_memory_t * dmem)2847dd7cddfSDavid du Colombier alloc_set_in_save(gs_dual_memory_t *dmem)
2857dd7cddfSDavid du Colombier {
2867dd7cddfSDavid du Colombier alloc_set_masks(dmem, l_new, l_new);
2877dd7cddfSDavid du Colombier }
2887dd7cddfSDavid du Colombier
2897dd7cddfSDavid du Colombier /* Record that we are not in a save. */
2907dd7cddfSDavid du Colombier void
alloc_set_not_in_save(gs_dual_memory_t * dmem)2917dd7cddfSDavid du Colombier alloc_set_not_in_save(gs_dual_memory_t *dmem)
2927dd7cddfSDavid du Colombier {
2937dd7cddfSDavid du Colombier alloc_set_masks(dmem, 0, ~0);
2947dd7cddfSDavid du Colombier }
2957dd7cddfSDavid du Colombier
2967dd7cddfSDavid du Colombier /* Save the state. */
297*593dc095SDavid du Colombier private alloc_save_t *alloc_save_space(gs_ref_memory_t *mem,
2987dd7cddfSDavid du Colombier gs_dual_memory_t *dmem,
299*593dc095SDavid du Colombier ulong sid);
3007dd7cddfSDavid du Colombier private void
alloc_free_save(gs_ref_memory_t * mem,alloc_save_t * save,const char * scn)301*593dc095SDavid du Colombier alloc_free_save(gs_ref_memory_t *mem, alloc_save_t *save, const char *scn)
3027dd7cddfSDavid du Colombier {
3037dd7cddfSDavid du Colombier gs_free_object((gs_memory_t *)mem, save, scn);
304*593dc095SDavid du Colombier /* Free any inner chunk structures. This is the easiest way to do it. */
305*593dc095SDavid du Colombier restore_free(mem);
3067dd7cddfSDavid du Colombier }
3077dd7cddfSDavid du Colombier ulong
alloc_save_state(gs_dual_memory_t * dmem,void * cdata)3087dd7cddfSDavid du Colombier alloc_save_state(gs_dual_memory_t * dmem, void *cdata)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier gs_ref_memory_t *lmem = dmem->space_local;
3117dd7cddfSDavid du Colombier gs_ref_memory_t *gmem = dmem->space_global;
312*593dc095SDavid du Colombier ulong sid = gs_next_ids((const gs_memory_t *)lmem->stable_memory, 2);
3137dd7cddfSDavid du Colombier bool global =
3147dd7cddfSDavid du Colombier lmem->save_level == 0 && gmem != lmem &&
3157dd7cddfSDavid du Colombier gmem->num_contexts == 1;
3167dd7cddfSDavid du Colombier alloc_save_t *gsave =
3177dd7cddfSDavid du Colombier (global ? alloc_save_space(gmem, dmem, sid + 1) : (alloc_save_t *) 0);
3187dd7cddfSDavid du Colombier alloc_save_t *lsave = alloc_save_space(lmem, dmem, sid);
3197dd7cddfSDavid du Colombier
3207dd7cddfSDavid du Colombier if (lsave == 0 || (global && gsave == 0)) {
3217dd7cddfSDavid du Colombier if (lsave != 0)
322*593dc095SDavid du Colombier alloc_free_save(lmem, lsave, "alloc_save_state(local save)");
3237dd7cddfSDavid du Colombier if (gsave != 0)
324*593dc095SDavid du Colombier alloc_free_save(gmem, gsave, "alloc_save_state(global save)");
3257dd7cddfSDavid du Colombier return 0;
3267dd7cddfSDavid du Colombier }
3277dd7cddfSDavid du Colombier if (gsave != 0) {
3287dd7cddfSDavid du Colombier gsave->client_data = 0;
3297dd7cddfSDavid du Colombier print_save("save", gmem->space, gsave);
3307dd7cddfSDavid du Colombier /* Restore names when we do the local restore. */
3317dd7cddfSDavid du Colombier lsave->restore_names = gsave->restore_names;
3327dd7cddfSDavid du Colombier gsave->restore_names = false;
3337dd7cddfSDavid du Colombier }
3347dd7cddfSDavid du Colombier lsave->id = sid;
3357dd7cddfSDavid du Colombier lsave->client_data = cdata;
3367dd7cddfSDavid du Colombier print_save("save", lmem->space, lsave);
3377dd7cddfSDavid du Colombier /* Reset the l_new attribute in all slots. The only slots that */
3387dd7cddfSDavid du Colombier /* can have the attribute set are the ones on the changes chain, */
3397dd7cddfSDavid du Colombier /* and ones in objects allocated since the last save. */
3407dd7cddfSDavid du Colombier if (lmem->save_level > 1) {
3417dd7cddfSDavid du Colombier long scanned = save_set_new(&lsave->state, false);
3427dd7cddfSDavid du Colombier
3437dd7cddfSDavid du Colombier if ((lsave->state.total_scanned += scanned) > max_repeated_scan) {
3447dd7cddfSDavid du Colombier /* Do a second, invisible save. */
3457dd7cddfSDavid du Colombier alloc_save_t *rsave;
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier rsave = alloc_save_space(lmem, dmem, 0L);
3487dd7cddfSDavid du Colombier if (rsave != 0) {
3497dd7cddfSDavid du Colombier rsave->client_data = cdata;
350*593dc095SDavid du Colombier #if 0 /* Bug 688153 */
3517dd7cddfSDavid du Colombier rsave->id = lsave->id;
3527dd7cddfSDavid du Colombier print_save("save", lmem->space, rsave);
3537dd7cddfSDavid du Colombier lsave->id = 0; /* mark as invisible */
3547dd7cddfSDavid du Colombier rsave->state.save_level--; /* ditto */
3557dd7cddfSDavid du Colombier lsave->client_data = 0;
356*593dc095SDavid du Colombier #else
357*593dc095SDavid du Colombier rsave->id = 0; /* mark as invisible */
358*593dc095SDavid du Colombier print_save("save", lmem->space, rsave);
359*593dc095SDavid du Colombier rsave->state.save_level--; /* ditto */
360*593dc095SDavid du Colombier rsave->client_data = 0;
361*593dc095SDavid du Colombier #endif
3627dd7cddfSDavid du Colombier /* Inherit the allocated space count -- */
3637dd7cddfSDavid du Colombier /* we need this for triggering a GC. */
3647dd7cddfSDavid du Colombier rsave->state.inherited =
3657dd7cddfSDavid du Colombier lsave->state.allocated + lsave->state.inherited;
3667dd7cddfSDavid du Colombier lmem->inherited = rsave->state.inherited;
3677dd7cddfSDavid du Colombier print_save("save", lmem->space, lsave);
3687dd7cddfSDavid du Colombier }
3697dd7cddfSDavid du Colombier }
3707dd7cddfSDavid du Colombier }
3717dd7cddfSDavid du Colombier alloc_set_in_save(dmem);
3727dd7cddfSDavid du Colombier return sid;
3737dd7cddfSDavid du Colombier }
3747dd7cddfSDavid du Colombier /* Save the state of one space (global or local). */
3757dd7cddfSDavid du Colombier private alloc_save_t *
alloc_save_space(gs_ref_memory_t * mem,gs_dual_memory_t * dmem,ulong sid)3767dd7cddfSDavid du Colombier alloc_save_space(gs_ref_memory_t * mem, gs_dual_memory_t * dmem, ulong sid)
3777dd7cddfSDavid du Colombier {
3787dd7cddfSDavid du Colombier gs_ref_memory_t save_mem;
3797dd7cddfSDavid du Colombier alloc_save_t *save;
380*593dc095SDavid du Colombier chunk_t *cp;
381*593dc095SDavid du Colombier chunk_t *new_pcc = 0;
3827dd7cddfSDavid du Colombier
3837dd7cddfSDavid du Colombier save_mem = *mem;
3847dd7cddfSDavid du Colombier alloc_close_chunk(mem);
385*593dc095SDavid du Colombier mem->pcc = 0;
3867dd7cddfSDavid du Colombier gs_memory_status((gs_memory_t *) mem, &mem->previous_status);
3877dd7cddfSDavid du Colombier ialloc_reset(mem);
388*593dc095SDavid du Colombier
389*593dc095SDavid du Colombier /* Create inner chunks wherever it's worthwhile. */
390*593dc095SDavid du Colombier
391*593dc095SDavid du Colombier for (cp = save_mem.cfirst; cp != 0; cp = cp->cnext) {
392*593dc095SDavid du Colombier if (cp->ctop - cp->cbot > min_inner_chunk_space) {
393*593dc095SDavid du Colombier /* Create an inner chunk to cover only the unallocated part. */
394*593dc095SDavid du Colombier chunk_t *inner =
395*593dc095SDavid du Colombier gs_raw_alloc_struct_immovable(mem->non_gc_memory, &st_chunk,
396*593dc095SDavid du Colombier "alloc_save_space(inner)");
397*593dc095SDavid du Colombier
398*593dc095SDavid du Colombier if (inner == 0)
399*593dc095SDavid du Colombier break; /* maybe should fail */
400*593dc095SDavid du Colombier alloc_init_chunk(inner, cp->cbot, cp->ctop, cp->sreloc != 0, cp);
401*593dc095SDavid du Colombier alloc_link_chunk(inner, mem);
402*593dc095SDavid du Colombier if_debug2('u', "[u]inner chunk: cbot=0x%lx ctop=0x%lx\n",
403*593dc095SDavid du Colombier (ulong) inner->cbot, (ulong) inner->ctop);
404*593dc095SDavid du Colombier if (cp == save_mem.pcc)
405*593dc095SDavid du Colombier new_pcc = inner;
4067dd7cddfSDavid du Colombier }
407*593dc095SDavid du Colombier }
408*593dc095SDavid du Colombier mem->pcc = new_pcc;
409*593dc095SDavid du Colombier alloc_open_chunk(mem);
410*593dc095SDavid du Colombier
4117dd7cddfSDavid du Colombier save = gs_alloc_struct((gs_memory_t *) mem, alloc_save_t,
4127dd7cddfSDavid du Colombier &st_alloc_save, "alloc_save_space(save)");
413*593dc095SDavid du Colombier if_debug2('u', "[u]save space %u at 0x%lx\n",
4147dd7cddfSDavid du Colombier mem->space, (ulong) save);
4157dd7cddfSDavid du Colombier if (save == 0) {
416*593dc095SDavid du Colombier /* Free the inner chunk structures. This is the easiest way. */
417*593dc095SDavid du Colombier restore_free(mem);
4187dd7cddfSDavid du Colombier *mem = save_mem;
4197dd7cddfSDavid du Colombier return 0;
4207dd7cddfSDavid du Colombier }
4217dd7cddfSDavid du Colombier save->state = save_mem;
4227dd7cddfSDavid du Colombier save->spaces = dmem->spaces;
423*593dc095SDavid du Colombier save->restore_names = (name_memory(mem) == (gs_memory_t *) mem);
4247dd7cddfSDavid du Colombier save->is_current = (dmem->current == mem);
4257dd7cddfSDavid du Colombier save->id = sid;
4267dd7cddfSDavid du Colombier mem->saved = save;
4277dd7cddfSDavid du Colombier if_debug2('u', "[u%u]file_save 0x%lx\n",
4287dd7cddfSDavid du Colombier mem->space, (ulong) mem->streams);
4297dd7cddfSDavid du Colombier mem->streams = 0;
4307dd7cddfSDavid du Colombier mem->total_scanned = 0;
4317dd7cddfSDavid du Colombier if (sid)
4327dd7cddfSDavid du Colombier mem->save_level++;
4337dd7cddfSDavid du Colombier return save;
4347dd7cddfSDavid du Colombier }
4357dd7cddfSDavid du Colombier
4367dd7cddfSDavid du Colombier /* Record a state change that must be undone for restore, */
4377dd7cddfSDavid du Colombier /* and mark it as having been saved. */
4387dd7cddfSDavid du Colombier int
alloc_save_change_in(gs_ref_memory_t * mem,const ref * pcont,ref_packed * where,client_name_t cname)4397dd7cddfSDavid du Colombier alloc_save_change_in(gs_ref_memory_t *mem, const ref * pcont,
4407dd7cddfSDavid du Colombier ref_packed * where, client_name_t cname)
4417dd7cddfSDavid du Colombier {
4427dd7cddfSDavid du Colombier register alloc_change_t *cp;
4437dd7cddfSDavid du Colombier
4447dd7cddfSDavid du Colombier if (mem->new_mask == 0)
4457dd7cddfSDavid du Colombier return 0; /* no saving */
4467dd7cddfSDavid du Colombier cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
4477dd7cddfSDavid du Colombier &st_alloc_change, "alloc_save_change");
4487dd7cddfSDavid du Colombier if (cp == 0)
4497dd7cddfSDavid du Colombier return -1;
4507dd7cddfSDavid du Colombier cp->next = mem->changes;
4517dd7cddfSDavid du Colombier cp->where = where;
4527dd7cddfSDavid du Colombier if (pcont == NULL)
4537dd7cddfSDavid du Colombier cp->offset = AC_OFFSET_STATIC;
4547dd7cddfSDavid du Colombier else if (r_is_array(pcont) || r_has_type(pcont, t_dictionary))
4557dd7cddfSDavid du Colombier cp->offset = AC_OFFSET_REF;
4567dd7cddfSDavid du Colombier else if (r_is_struct(pcont))
4577dd7cddfSDavid du Colombier cp->offset = (byte *) where - (byte *) pcont->value.pstruct;
4587dd7cddfSDavid du Colombier else {
4597dd7cddfSDavid du Colombier lprintf3("Bad type %u for save! pcont = 0x%lx, where = 0x%lx\n",
4607dd7cddfSDavid du Colombier r_type(pcont), (ulong) pcont, (ulong) where);
461*593dc095SDavid du Colombier gs_abort((const gs_memory_t *)mem);
4627dd7cddfSDavid du Colombier }
4637dd7cddfSDavid du Colombier if (r_is_packed(where))
4647dd7cddfSDavid du Colombier *(ref_packed *)&cp->contents = *where;
4657dd7cddfSDavid du Colombier else {
4667dd7cddfSDavid du Colombier ref_assign_inline(&cp->contents, (ref *) where);
4677dd7cddfSDavid du Colombier r_set_attrs((ref *) where, l_new);
4687dd7cddfSDavid du Colombier }
4697dd7cddfSDavid du Colombier mem->changes = cp;
4707dd7cddfSDavid du Colombier #ifdef DEBUG
4717dd7cddfSDavid du Colombier if (gs_debug_c('U')) {
4727dd7cddfSDavid du Colombier dlprintf1("[U]save(%s)", client_name_string(cname));
4737dd7cddfSDavid du Colombier alloc_save_print(cp, false);
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier #endif
4767dd7cddfSDavid du Colombier return 0;
4777dd7cddfSDavid du Colombier }
4787dd7cddfSDavid du Colombier int
alloc_save_change(gs_dual_memory_t * dmem,const ref * pcont,ref_packed * where,client_name_t cname)4797dd7cddfSDavid du Colombier alloc_save_change(gs_dual_memory_t * dmem, const ref * pcont,
4807dd7cddfSDavid du Colombier ref_packed * where, client_name_t cname)
4817dd7cddfSDavid du Colombier {
4827dd7cddfSDavid du Colombier gs_ref_memory_t *mem =
4837dd7cddfSDavid du Colombier (pcont == NULL ? dmem->space_local :
4847dd7cddfSDavid du Colombier dmem->spaces_indexed[r_space(pcont) >> r_space_shift]);
4857dd7cddfSDavid du Colombier
4867dd7cddfSDavid du Colombier return alloc_save_change_in(mem, pcont, where, cname);
4877dd7cddfSDavid du Colombier }
4887dd7cddfSDavid du Colombier
4897dd7cddfSDavid du Colombier /* Return (the id of) the innermost externally visible save object, */
4907dd7cddfSDavid du Colombier /* i.e., the innermost save with a non-zero ID. */
4917dd7cddfSDavid du Colombier ulong
alloc_save_current_id(const gs_dual_memory_t * dmem)4927dd7cddfSDavid du Colombier alloc_save_current_id(const gs_dual_memory_t * dmem)
4937dd7cddfSDavid du Colombier {
4947dd7cddfSDavid du Colombier const alloc_save_t *save = dmem->space_local->saved;
4957dd7cddfSDavid du Colombier
4967dd7cddfSDavid du Colombier while (save != 0 && save->id == 0)
4977dd7cddfSDavid du Colombier save = save->state.saved;
4987dd7cddfSDavid du Colombier return save->id;
4997dd7cddfSDavid du Colombier }
5007dd7cddfSDavid du Colombier alloc_save_t *
alloc_save_current(const gs_dual_memory_t * dmem)5017dd7cddfSDavid du Colombier alloc_save_current(const gs_dual_memory_t * dmem)
5027dd7cddfSDavid du Colombier {
5037dd7cddfSDavid du Colombier return alloc_find_save(dmem, alloc_save_current_id(dmem));
5047dd7cddfSDavid du Colombier }
5057dd7cddfSDavid du Colombier
5067dd7cddfSDavid du Colombier /* Test whether a reference would be invalidated by a restore. */
5077dd7cddfSDavid du Colombier bool
alloc_is_since_save(const void * vptr,const alloc_save_t * save)5087dd7cddfSDavid du Colombier alloc_is_since_save(const void *vptr, const alloc_save_t * save)
5097dd7cddfSDavid du Colombier {
5107dd7cddfSDavid du Colombier /* A reference postdates a save iff it is in a chunk allocated */
511*593dc095SDavid du Colombier /* since the save (including any carried-over inner chunks). */
5127dd7cddfSDavid du Colombier
5137dd7cddfSDavid du Colombier const char *const ptr = (const char *)vptr;
5147dd7cddfSDavid du Colombier register const gs_ref_memory_t *mem = save->space_local;
5157dd7cddfSDavid du Colombier
5167dd7cddfSDavid du Colombier if_debug2('U', "[U]is_since_save 0x%lx, 0x%lx:\n",
5177dd7cddfSDavid du Colombier (ulong) ptr, (ulong) save);
5187dd7cddfSDavid du Colombier if (mem->saved == 0) { /* This is a special case, the final 'restore' from */
5197dd7cddfSDavid du Colombier /* alloc_restore_all. */
5207dd7cddfSDavid du Colombier return true;
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier /* Check against chunks allocated since the save. */
5237dd7cddfSDavid du Colombier /* (There may have been intermediate saves as well.) */
5247dd7cddfSDavid du Colombier for (;; mem = &mem->saved->state) {
5257dd7cddfSDavid du Colombier const chunk_t *cp;
5267dd7cddfSDavid du Colombier
5277dd7cddfSDavid du Colombier if_debug1('U', "[U]checking mem=0x%lx\n", (ulong) mem);
5287dd7cddfSDavid du Colombier for (cp = mem->cfirst; cp != 0; cp = cp->cnext) {
5297dd7cddfSDavid du Colombier if (ptr_is_within_chunk(ptr, cp)) {
5307dd7cddfSDavid du Colombier if_debug3('U', "[U+]in new chunk 0x%lx: 0x%lx, 0x%lx\n",
5317dd7cddfSDavid du Colombier (ulong) cp,
5327dd7cddfSDavid du Colombier (ulong) cp->cbase, (ulong) cp->cend);
5337dd7cddfSDavid du Colombier return true;
5347dd7cddfSDavid du Colombier }
5357dd7cddfSDavid du Colombier if_debug1('U', "[U-]not in 0x%lx\n", (ulong) cp);
5367dd7cddfSDavid du Colombier }
5377dd7cddfSDavid du Colombier if (mem->saved == save) { /* We've checked all the more recent saves, */
5387dd7cddfSDavid du Colombier /* must be OK. */
5397dd7cddfSDavid du Colombier break;
5407dd7cddfSDavid du Colombier }
5417dd7cddfSDavid du Colombier }
5427dd7cddfSDavid du Colombier
5437dd7cddfSDavid du Colombier /*
544*593dc095SDavid du Colombier * If we're about to do a global restore (a restore to the level 0),
5457dd7cddfSDavid du Colombier * and there is only one context using this global VM
5467dd7cddfSDavid du Colombier * (the normal case, in which global VM is saved by the
5477dd7cddfSDavid du Colombier * outermost save), we also have to check the global save.
5487dd7cddfSDavid du Colombier * Global saves can't be nested, which makes things easy.
5497dd7cddfSDavid du Colombier */
550*593dc095SDavid du Colombier if (save->state.save_level == 0 /* Restoring to save level 0 - see bug 688157, 688161 */ &&
5517dd7cddfSDavid du Colombier (mem = save->space_global) != save->space_local &&
5527dd7cddfSDavid du Colombier save->space_global->num_contexts == 1
5537dd7cddfSDavid du Colombier ) {
5547dd7cddfSDavid du Colombier const chunk_t *cp;
5557dd7cddfSDavid du Colombier
5567dd7cddfSDavid du Colombier if_debug1('U', "[U]checking global mem=0x%lx\n", (ulong) mem);
5577dd7cddfSDavid du Colombier for (cp = mem->cfirst; cp != 0; cp = cp->cnext)
5587dd7cddfSDavid du Colombier if (ptr_is_within_chunk(ptr, cp)) {
5597dd7cddfSDavid du Colombier if_debug3('U', "[U+] new chunk 0x%lx: 0x%lx, 0x%lx\n",
5607dd7cddfSDavid du Colombier (ulong) cp, (ulong) cp->cbase, (ulong) cp->cend);
5617dd7cddfSDavid du Colombier return true;
5627dd7cddfSDavid du Colombier }
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier return false;
5657dd7cddfSDavid du Colombier
5667dd7cddfSDavid du Colombier #undef ptr
5677dd7cddfSDavid du Colombier }
5687dd7cddfSDavid du Colombier
5697dd7cddfSDavid du Colombier /* Test whether a name would be invalidated by a restore. */
5707dd7cddfSDavid du Colombier bool
alloc_name_is_since_save(const gs_memory_t * mem,const ref * pnref,const alloc_save_t * save)571*593dc095SDavid du Colombier alloc_name_is_since_save(const gs_memory_t *mem,
572*593dc095SDavid du Colombier const ref * pnref, const alloc_save_t * save)
5737dd7cddfSDavid du Colombier {
5747dd7cddfSDavid du Colombier const name_string_t *pnstr;
5757dd7cddfSDavid du Colombier
5767dd7cddfSDavid du Colombier if (!save->restore_names)
5777dd7cddfSDavid du Colombier return false;
578*593dc095SDavid du Colombier pnstr = names_string_inline(mem->gs_lib_ctx->gs_name_table, pnref);
5797dd7cddfSDavid du Colombier if (pnstr->foreign_string)
5807dd7cddfSDavid du Colombier return false;
5817dd7cddfSDavid du Colombier return alloc_is_since_save(pnstr->string_bytes, save);
5827dd7cddfSDavid du Colombier }
5837dd7cddfSDavid du Colombier bool
alloc_name_index_is_since_save(const gs_memory_t * mem,uint nidx,const alloc_save_t * save)584*593dc095SDavid du Colombier alloc_name_index_is_since_save(const gs_memory_t *mem,
585*593dc095SDavid du Colombier uint nidx, const alloc_save_t *save)
5867dd7cddfSDavid du Colombier {
587*593dc095SDavid du Colombier const name_string_t *pnstr;
5887dd7cddfSDavid du Colombier
589*593dc095SDavid du Colombier if (!save->restore_names)
590*593dc095SDavid du Colombier return false;
591*593dc095SDavid du Colombier pnstr = names_index_string_inline(mem->gs_lib_ctx->gs_name_table, nidx);
592*593dc095SDavid du Colombier if (pnstr->foreign_string)
593*593dc095SDavid du Colombier return false;
594*593dc095SDavid du Colombier return alloc_is_since_save(pnstr->string_bytes, save);
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier
5977dd7cddfSDavid du Colombier /* Check whether any names have been created since a given save */
5987dd7cddfSDavid du Colombier /* that might be released by the restore. */
5997dd7cddfSDavid du Colombier bool
alloc_any_names_since_save(const alloc_save_t * save)6007dd7cddfSDavid du Colombier alloc_any_names_since_save(const alloc_save_t * save)
6017dd7cddfSDavid du Colombier {
6027dd7cddfSDavid du Colombier return save->restore_names;
6037dd7cddfSDavid du Colombier }
6047dd7cddfSDavid du Colombier
6057dd7cddfSDavid du Colombier /* Get the saved state with a given ID. */
6067dd7cddfSDavid du Colombier alloc_save_t *
alloc_find_save(const gs_dual_memory_t * dmem,ulong sid)6077dd7cddfSDavid du Colombier alloc_find_save(const gs_dual_memory_t * dmem, ulong sid)
6087dd7cddfSDavid du Colombier {
6097dd7cddfSDavid du Colombier alloc_save_t *sprev = dmem->space_local->saved;
6107dd7cddfSDavid du Colombier
6117dd7cddfSDavid du Colombier if (sid == 0)
6127dd7cddfSDavid du Colombier return 0; /* invalid id */
6137dd7cddfSDavid du Colombier while (sprev != 0) {
6147dd7cddfSDavid du Colombier if (sprev->id == sid)
6157dd7cddfSDavid du Colombier return sprev;
6167dd7cddfSDavid du Colombier sprev = sprev->state.saved;
6177dd7cddfSDavid du Colombier }
6187dd7cddfSDavid du Colombier return 0;
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier
6217dd7cddfSDavid du Colombier /* Get the client data from a saved state. */
6227dd7cddfSDavid du Colombier void *
alloc_save_client_data(const alloc_save_t * save)6237dd7cddfSDavid du Colombier alloc_save_client_data(const alloc_save_t * save)
6247dd7cddfSDavid du Colombier {
6257dd7cddfSDavid du Colombier return save->client_data;
6267dd7cddfSDavid du Colombier }
6277dd7cddfSDavid du Colombier
6287dd7cddfSDavid du Colombier /*
6297dd7cddfSDavid du Colombier * Do one step of restoring the state. The client is responsible for
6307dd7cddfSDavid du Colombier * calling alloc_find_save to get the save object, and for ensuring that
6317dd7cddfSDavid du Colombier * there are no surviving pointers for which alloc_is_since_save is true.
6327dd7cddfSDavid du Colombier * Return true if the argument was the innermost save, in which case
6337dd7cddfSDavid du Colombier * this is the last (or only) step.
6347dd7cddfSDavid du Colombier * Note that "one step" may involve multiple internal steps,
6357dd7cddfSDavid du Colombier * if this is the outermost restore (which requires restoring both local
6367dd7cddfSDavid du Colombier * and global VM) or if we created extra save levels to reduce scanning.
6377dd7cddfSDavid du Colombier */
638*593dc095SDavid du Colombier private void restore_finalize(gs_ref_memory_t *);
639*593dc095SDavid du Colombier private void restore_space(gs_ref_memory_t *, gs_dual_memory_t *);
6407dd7cddfSDavid du Colombier
6417dd7cddfSDavid du Colombier bool
alloc_restore_step_in(gs_dual_memory_t * dmem,alloc_save_t * save)6427dd7cddfSDavid du Colombier alloc_restore_step_in(gs_dual_memory_t *dmem, alloc_save_t * save)
6437dd7cddfSDavid du Colombier {
6447dd7cddfSDavid du Colombier /* Get save->space_* now, because the save object will be freed. */
6457dd7cddfSDavid du Colombier gs_ref_memory_t *lmem = save->space_local;
6467dd7cddfSDavid du Colombier gs_ref_memory_t *gmem = save->space_global;
6477dd7cddfSDavid du Colombier gs_ref_memory_t *mem = lmem;
6487dd7cddfSDavid du Colombier alloc_save_t *sprev;
6497dd7cddfSDavid du Colombier
6507dd7cddfSDavid du Colombier /* Finalize all objects before releasing resources or undoing changes. */
6517dd7cddfSDavid du Colombier do {
6527dd7cddfSDavid du Colombier ulong sid;
6537dd7cddfSDavid du Colombier
6547dd7cddfSDavid du Colombier sprev = mem->saved;
6557dd7cddfSDavid du Colombier sid = sprev->id;
6567dd7cddfSDavid du Colombier restore_finalize(mem); /* finalize objects */
6577dd7cddfSDavid du Colombier mem = &sprev->state;
6587dd7cddfSDavid du Colombier if (sid != 0)
6597dd7cddfSDavid du Colombier break;
6607dd7cddfSDavid du Colombier }
6617dd7cddfSDavid du Colombier while (sprev != save);
6627dd7cddfSDavid du Colombier if (mem->save_level == 0) {
6637dd7cddfSDavid du Colombier /* This is the outermost save, which might also */
6647dd7cddfSDavid du Colombier /* need to restore global VM. */
6657dd7cddfSDavid du Colombier mem = gmem;
6667dd7cddfSDavid du Colombier if (mem != lmem && mem->saved != 0)
6677dd7cddfSDavid du Colombier restore_finalize(mem);
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier
6707dd7cddfSDavid du Colombier /* Do one (externally visible) step of restoring the state. */
6717dd7cddfSDavid du Colombier mem = lmem;
6727dd7cddfSDavid du Colombier do {
6737dd7cddfSDavid du Colombier ulong sid;
6747dd7cddfSDavid du Colombier
6757dd7cddfSDavid du Colombier sprev = mem->saved;
6767dd7cddfSDavid du Colombier sid = sprev->id;
6777dd7cddfSDavid du Colombier restore_resources(sprev, mem); /* release other resources */
6787dd7cddfSDavid du Colombier restore_space(mem, dmem); /* release memory */
6797dd7cddfSDavid du Colombier if (sid != 0)
6807dd7cddfSDavid du Colombier break;
6817dd7cddfSDavid du Colombier }
6827dd7cddfSDavid du Colombier while (sprev != save);
6837dd7cddfSDavid du Colombier
6847dd7cddfSDavid du Colombier if (mem->save_level == 0) {
6857dd7cddfSDavid du Colombier /* This is the outermost save, which might also */
6867dd7cddfSDavid du Colombier /* need to restore global VM. */
6877dd7cddfSDavid du Colombier mem = gmem;
6887dd7cddfSDavid du Colombier if (mem != lmem && mem->saved != 0) {
6897dd7cddfSDavid du Colombier restore_resources(mem->saved, mem);
6907dd7cddfSDavid du Colombier restore_space(mem, dmem);
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier alloc_set_not_in_save(dmem);
6937dd7cddfSDavid du Colombier } else { /* Set the l_new attribute in all slots that are now new. */
6947dd7cddfSDavid du Colombier save_set_new(mem, true);
6957dd7cddfSDavid du Colombier }
6967dd7cddfSDavid du Colombier
6977dd7cddfSDavid du Colombier return sprev == save;
6987dd7cddfSDavid du Colombier }
6997dd7cddfSDavid du Colombier /* Restore the memory of one space, by undoing changes and freeing */
7007dd7cddfSDavid du Colombier /* memory allocated since the save. */
7017dd7cddfSDavid du Colombier private void
restore_space(gs_ref_memory_t * mem,gs_dual_memory_t * dmem)7027dd7cddfSDavid du Colombier restore_space(gs_ref_memory_t * mem, gs_dual_memory_t *dmem)
7037dd7cddfSDavid du Colombier {
7047dd7cddfSDavid du Colombier alloc_save_t *save = mem->saved;
7057dd7cddfSDavid du Colombier alloc_save_t saved;
7067dd7cddfSDavid du Colombier
7077dd7cddfSDavid du Colombier print_save("restore", mem->space, save);
7087dd7cddfSDavid du Colombier
7097dd7cddfSDavid du Colombier /* Undo changes since the save. */
7107dd7cddfSDavid du Colombier {
7117dd7cddfSDavid du Colombier register alloc_change_t *cp = mem->changes;
7127dd7cddfSDavid du Colombier
7137dd7cddfSDavid du Colombier while (cp) {
7147dd7cddfSDavid du Colombier #ifdef DEBUG
7157dd7cddfSDavid du Colombier if (gs_debug_c('U')) {
7167dd7cddfSDavid du Colombier dlputs("[U]restore");
7177dd7cddfSDavid du Colombier alloc_save_print(cp, true);
7187dd7cddfSDavid du Colombier }
7197dd7cddfSDavid du Colombier #endif
7207dd7cddfSDavid du Colombier if (r_is_packed(&cp->contents))
7217dd7cddfSDavid du Colombier *cp->where = *(ref_packed *) & cp->contents;
7227dd7cddfSDavid du Colombier else
7237dd7cddfSDavid du Colombier ref_assign_inline((ref *) cp->where, &cp->contents);
7247dd7cddfSDavid du Colombier cp = cp->next;
7257dd7cddfSDavid du Colombier }
7267dd7cddfSDavid du Colombier }
7277dd7cddfSDavid du Colombier
7287dd7cddfSDavid du Colombier /* Free memory allocated since the save. */
729*593dc095SDavid du Colombier /* Note that this frees all chunks except the inner ones */
7307dd7cddfSDavid du Colombier /* belonging to this level. */
7317dd7cddfSDavid du Colombier saved = *save;
7327dd7cddfSDavid du Colombier restore_free(mem);
7337dd7cddfSDavid du Colombier
7347dd7cddfSDavid du Colombier /* Restore the allocator state. */
7357dd7cddfSDavid du Colombier {
7367dd7cddfSDavid du Colombier int num_contexts = mem->num_contexts; /* don't restore */
7377dd7cddfSDavid du Colombier
7387dd7cddfSDavid du Colombier *mem = saved.state;
7397dd7cddfSDavid du Colombier mem->num_contexts = num_contexts;
7407dd7cddfSDavid du Colombier }
7417dd7cddfSDavid du Colombier alloc_open_chunk(mem);
7427dd7cddfSDavid du Colombier
7437dd7cddfSDavid du Colombier /* Make the allocator current if it was current before the save. */
7447dd7cddfSDavid du Colombier if (saved.is_current) {
7457dd7cddfSDavid du Colombier dmem->current = mem;
7467dd7cddfSDavid du Colombier dmem->current_space = mem->space;
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier }
7497dd7cddfSDavid du Colombier
7507dd7cddfSDavid du Colombier /* Restore to the initial state, releasing all resources. */
7517dd7cddfSDavid du Colombier /* The allocator is no longer usable after calling this routine! */
7527dd7cddfSDavid du Colombier void
alloc_restore_all(gs_dual_memory_t * dmem)7537dd7cddfSDavid du Colombier alloc_restore_all(gs_dual_memory_t * dmem)
7547dd7cddfSDavid du Colombier {
7557dd7cddfSDavid du Colombier /*
7567dd7cddfSDavid du Colombier * Save the memory pointers, since freeing space_local will also
7577dd7cddfSDavid du Colombier * free dmem itself.
7587dd7cddfSDavid du Colombier */
7597dd7cddfSDavid du Colombier gs_ref_memory_t *lmem = dmem->space_local;
7607dd7cddfSDavid du Colombier gs_ref_memory_t *gmem = dmem->space_global;
7617dd7cddfSDavid du Colombier gs_ref_memory_t *smem = dmem->space_system;
7627dd7cddfSDavid du Colombier gs_ref_memory_t *mem;
7637dd7cddfSDavid du Colombier
7647dd7cddfSDavid du Colombier /* Restore to a state outside any saves. */
7657dd7cddfSDavid du Colombier while (lmem->save_level != 0)
7667dd7cddfSDavid du Colombier discard(alloc_restore_step_in(dmem, lmem->saved));
7677dd7cddfSDavid du Colombier
7687dd7cddfSDavid du Colombier /* Finalize memory. */
7697dd7cddfSDavid du Colombier restore_finalize(lmem);
7707dd7cddfSDavid du Colombier if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
7717dd7cddfSDavid du Colombier restore_finalize(mem);
7727dd7cddfSDavid du Colombier if (gmem != lmem && gmem->num_contexts == 1) {
7737dd7cddfSDavid du Colombier restore_finalize(gmem);
7747dd7cddfSDavid du Colombier if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
7757dd7cddfSDavid du Colombier restore_finalize(mem);
7767dd7cddfSDavid du Colombier }
7777dd7cddfSDavid du Colombier restore_finalize(smem);
7787dd7cddfSDavid du Colombier
7797dd7cddfSDavid du Colombier /* Release resources other than memory, using fake */
7807dd7cddfSDavid du Colombier /* save and memory objects. */
7817dd7cddfSDavid du Colombier {
7827dd7cddfSDavid du Colombier alloc_save_t empty_save;
7837dd7cddfSDavid du Colombier
7847dd7cddfSDavid du Colombier empty_save.spaces = dmem->spaces;
7857dd7cddfSDavid du Colombier empty_save.restore_names = false; /* don't bother to release */
7867dd7cddfSDavid du Colombier restore_resources(&empty_save, NULL);
7877dd7cddfSDavid du Colombier }
7887dd7cddfSDavid du Colombier
7897dd7cddfSDavid du Colombier /* Finally, release memory. */
7907dd7cddfSDavid du Colombier restore_free(lmem);
7917dd7cddfSDavid du Colombier if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
7927dd7cddfSDavid du Colombier restore_free(mem);
7937dd7cddfSDavid du Colombier if (gmem != lmem) {
7947dd7cddfSDavid du Colombier if (!--(gmem->num_contexts)) {
7957dd7cddfSDavid du Colombier restore_free(gmem);
7967dd7cddfSDavid du Colombier if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
7977dd7cddfSDavid du Colombier restore_free(mem);
7987dd7cddfSDavid du Colombier }
7997dd7cddfSDavid du Colombier }
8007dd7cddfSDavid du Colombier restore_free(smem);
8017dd7cddfSDavid du Colombier
8027dd7cddfSDavid du Colombier }
8037dd7cddfSDavid du Colombier
8047dd7cddfSDavid du Colombier /*
8057dd7cddfSDavid du Colombier * Finalize objects that will be freed by a restore.
8067dd7cddfSDavid du Colombier * Note that we must temporarily disable the freeing operations
8077dd7cddfSDavid du Colombier * of the allocator while doing this.
8087dd7cddfSDavid du Colombier */
8097dd7cddfSDavid du Colombier private void
restore_finalize(gs_ref_memory_t * mem)8107dd7cddfSDavid du Colombier restore_finalize(gs_ref_memory_t * mem)
8117dd7cddfSDavid du Colombier {
8127dd7cddfSDavid du Colombier chunk_t *cp;
8137dd7cddfSDavid du Colombier
8147dd7cddfSDavid du Colombier alloc_close_chunk(mem);
8157dd7cddfSDavid du Colombier gs_enable_free((gs_memory_t *) mem, false);
8167dd7cddfSDavid du Colombier for (cp = mem->clast; cp != 0; cp = cp->cprev) {
8177dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
8187dd7cddfSDavid du Colombier DO_ALL
8197dd7cddfSDavid du Colombier struct_proc_finalize((*finalize)) =
8207dd7cddfSDavid du Colombier pre->o_type->finalize;
8217dd7cddfSDavid du Colombier if (finalize != 0) {
8227dd7cddfSDavid du Colombier if_debug2('u', "[u]restore finalizing %s 0x%lx\n",
8237dd7cddfSDavid du Colombier struct_type_name_string(pre->o_type),
8247dd7cddfSDavid du Colombier (ulong) (pre + 1));
8257dd7cddfSDavid du Colombier (*finalize) (pre + 1);
8267dd7cddfSDavid du Colombier }
8277dd7cddfSDavid du Colombier END_OBJECTS_SCAN
8287dd7cddfSDavid du Colombier }
8297dd7cddfSDavid du Colombier gs_enable_free((gs_memory_t *) mem, true);
8307dd7cddfSDavid du Colombier }
8317dd7cddfSDavid du Colombier
8327dd7cddfSDavid du Colombier /* Release resources for a restore */
8337dd7cddfSDavid du Colombier private void
restore_resources(alloc_save_t * sprev,gs_ref_memory_t * mem)8347dd7cddfSDavid du Colombier restore_resources(alloc_save_t * sprev, gs_ref_memory_t * mem)
8357dd7cddfSDavid du Colombier {
8367dd7cddfSDavid du Colombier #ifdef DEBUG
8377dd7cddfSDavid du Colombier if (mem) {
8387dd7cddfSDavid du Colombier /* Note restoring of the file list. */
8397dd7cddfSDavid du Colombier if_debug4('u', "[u%u]file_restore 0x%lx => 0x%lx for 0x%lx\n",
8407dd7cddfSDavid du Colombier mem->space, (ulong)mem->streams,
8417dd7cddfSDavid du Colombier (ulong)sprev->state.streams, (ulong) sprev);
8427dd7cddfSDavid du Colombier }
8437dd7cddfSDavid du Colombier #endif
8447dd7cddfSDavid du Colombier
8457dd7cddfSDavid du Colombier /* Remove entries from font and character caches. */
8467dd7cddfSDavid du Colombier font_restore(sprev);
8477dd7cddfSDavid du Colombier
8487dd7cddfSDavid du Colombier /* Adjust the name table. */
8497dd7cddfSDavid du Colombier if (sprev->restore_names)
850*593dc095SDavid du Colombier names_restore(mem->gs_lib_ctx->gs_name_table, sprev);
8517dd7cddfSDavid du Colombier }
8527dd7cddfSDavid du Colombier
8537dd7cddfSDavid du Colombier /* Release memory for a restore. */
8547dd7cddfSDavid du Colombier private void
restore_free(gs_ref_memory_t * mem)8557dd7cddfSDavid du Colombier restore_free(gs_ref_memory_t * mem)
8567dd7cddfSDavid du Colombier {
8577dd7cddfSDavid du Colombier /* Free chunks allocated since the save. */
8587dd7cddfSDavid du Colombier gs_free_all((gs_memory_t *) mem);
8597dd7cddfSDavid du Colombier }
8607dd7cddfSDavid du Colombier
8617dd7cddfSDavid du Colombier /* Forget a save, by merging this level with the next outer one. */
862*593dc095SDavid du Colombier private void file_forget_save(gs_ref_memory_t *);
863*593dc095SDavid du Colombier private void combine_space(gs_ref_memory_t *);
864*593dc095SDavid du Colombier private void forget_changes(gs_ref_memory_t *);
8657dd7cddfSDavid du Colombier void
alloc_forget_save_in(gs_dual_memory_t * dmem,alloc_save_t * save)8667dd7cddfSDavid du Colombier alloc_forget_save_in(gs_dual_memory_t *dmem, alloc_save_t * save)
8677dd7cddfSDavid du Colombier {
8687dd7cddfSDavid du Colombier gs_ref_memory_t *mem = save->space_local;
8697dd7cddfSDavid du Colombier alloc_save_t *sprev;
8707dd7cddfSDavid du Colombier
8717dd7cddfSDavid du Colombier print_save("forget_save", mem->space, save);
8727dd7cddfSDavid du Colombier
8737dd7cddfSDavid du Colombier /* Iteratively combine the current level with the previous one. */
8747dd7cddfSDavid du Colombier do {
8757dd7cddfSDavid du Colombier sprev = mem->saved;
8767dd7cddfSDavid du Colombier if (sprev->id != 0)
8777dd7cddfSDavid du Colombier mem->save_level--;
8787dd7cddfSDavid du Colombier if (mem->save_level != 0) {
8797dd7cddfSDavid du Colombier alloc_change_t *chp = mem->changes;
8807dd7cddfSDavid du Colombier
8817dd7cddfSDavid du Colombier save_set_new(&sprev->state, true);
8827dd7cddfSDavid du Colombier /* Concatenate the changes chains. */
8837dd7cddfSDavid du Colombier if (chp == 0)
8847dd7cddfSDavid du Colombier mem->changes = sprev->state.changes;
8857dd7cddfSDavid du Colombier else {
8867dd7cddfSDavid du Colombier while (chp->next != 0)
8877dd7cddfSDavid du Colombier chp = chp->next;
8887dd7cddfSDavid du Colombier chp->next = sprev->state.changes;
8897dd7cddfSDavid du Colombier }
8907dd7cddfSDavid du Colombier file_forget_save(mem);
8917dd7cddfSDavid du Colombier combine_space(mem); /* combine memory */
8927dd7cddfSDavid du Colombier } else {
8937dd7cddfSDavid du Colombier forget_changes(mem);
8947dd7cddfSDavid du Colombier save_set_new(mem, false);
8957dd7cddfSDavid du Colombier file_forget_save(mem);
8967dd7cddfSDavid du Colombier combine_space(mem); /* combine memory */
8977dd7cddfSDavid du Colombier /* This is the outermost save, which might also */
8987dd7cddfSDavid du Colombier /* need to combine global VM. */
8997dd7cddfSDavid du Colombier mem = save->space_global;
9007dd7cddfSDavid du Colombier if (mem != save->space_local && mem->saved != 0) {
9017dd7cddfSDavid du Colombier forget_changes(mem);
9027dd7cddfSDavid du Colombier save_set_new(mem, false);
9037dd7cddfSDavid du Colombier file_forget_save(mem);
9047dd7cddfSDavid du Colombier combine_space(mem);
9057dd7cddfSDavid du Colombier }
9067dd7cddfSDavid du Colombier alloc_set_not_in_save(dmem);
9077dd7cddfSDavid du Colombier break; /* must be outermost */
9087dd7cddfSDavid du Colombier }
9097dd7cddfSDavid du Colombier }
9107dd7cddfSDavid du Colombier while (sprev != save);
9117dd7cddfSDavid du Colombier }
9127dd7cddfSDavid du Colombier /* Combine the chunks of the next outer level with those of the current one, */
9137dd7cddfSDavid du Colombier /* and free the bookkeeping structures. */
9147dd7cddfSDavid du Colombier private void
combine_space(gs_ref_memory_t * mem)9157dd7cddfSDavid du Colombier combine_space(gs_ref_memory_t * mem)
9167dd7cddfSDavid du Colombier {
9177dd7cddfSDavid du Colombier alloc_save_t *saved = mem->saved;
9187dd7cddfSDavid du Colombier gs_ref_memory_t *omem = &saved->state;
9197dd7cddfSDavid du Colombier chunk_t *cp;
9207dd7cddfSDavid du Colombier chunk_t *csucc;
9217dd7cddfSDavid du Colombier
9227dd7cddfSDavid du Colombier alloc_close_chunk(mem);
9237dd7cddfSDavid du Colombier for (cp = mem->cfirst; cp != 0; cp = csucc) {
9247dd7cddfSDavid du Colombier csucc = cp->cnext; /* save before relinking */
9257dd7cddfSDavid du Colombier if (cp->outer == 0)
9267dd7cddfSDavid du Colombier alloc_link_chunk(cp, omem);
9277dd7cddfSDavid du Colombier else {
9287dd7cddfSDavid du Colombier chunk_t *outer = cp->outer;
9297dd7cddfSDavid du Colombier
9307dd7cddfSDavid du Colombier outer->inner_count--;
9317dd7cddfSDavid du Colombier if (mem->pcc == cp)
9327dd7cddfSDavid du Colombier mem->pcc = outer;
9337dd7cddfSDavid du Colombier if (mem->cfreed.cp == cp)
9347dd7cddfSDavid du Colombier mem->cfreed.cp = outer;
9357dd7cddfSDavid du Colombier /* "Free" the header of the inner chunk, */
9367dd7cddfSDavid du Colombier /* and any immediately preceding gap left by */
9377dd7cddfSDavid du Colombier /* the GC having compacted the outer chunk. */
9387dd7cddfSDavid du Colombier {
9397dd7cddfSDavid du Colombier obj_header_t *hp = (obj_header_t *) outer->cbot;
9407dd7cddfSDavid du Colombier
9417dd7cddfSDavid du Colombier hp->o_alone = 0;
9427dd7cddfSDavid du Colombier hp->o_size = (char *)(cp->chead + 1)
9437dd7cddfSDavid du Colombier - (char *)(hp + 1);
9447dd7cddfSDavid du Colombier hp->o_type = &st_bytes;
9457dd7cddfSDavid du Colombier /* The following call is probably not safe. */
9467dd7cddfSDavid du Colombier #if 0 /* **************** */
9477dd7cddfSDavid du Colombier gs_free_object((gs_memory_t *) mem,
9487dd7cddfSDavid du Colombier hp + 1, "combine_space(header)");
9497dd7cddfSDavid du Colombier #endif /* **************** */
9507dd7cddfSDavid du Colombier }
9517dd7cddfSDavid du Colombier /* Update the outer chunk's allocation pointers. */
9527dd7cddfSDavid du Colombier outer->cbot = cp->cbot;
9537dd7cddfSDavid du Colombier outer->rcur = cp->rcur;
9547dd7cddfSDavid du Colombier outer->rtop = cp->rtop;
9557dd7cddfSDavid du Colombier outer->ctop = cp->ctop;
9567dd7cddfSDavid du Colombier outer->has_refs |= cp->has_refs;
957*593dc095SDavid du Colombier gs_free_object(mem->non_gc_memory, cp,
9587dd7cddfSDavid du Colombier "combine_space(inner)");
9597dd7cddfSDavid du Colombier }
9607dd7cddfSDavid du Colombier }
9617dd7cddfSDavid du Colombier /* Update relevant parts of allocator state. */
9627dd7cddfSDavid du Colombier mem->cfirst = omem->cfirst;
9637dd7cddfSDavid du Colombier mem->clast = omem->clast;
9647dd7cddfSDavid du Colombier mem->allocated += omem->allocated;
9657dd7cddfSDavid du Colombier mem->gc_allocated += omem->allocated;
9667dd7cddfSDavid du Colombier mem->lost.objects += omem->lost.objects;
9677dd7cddfSDavid du Colombier mem->lost.refs += omem->lost.refs;
9687dd7cddfSDavid du Colombier mem->lost.strings += omem->lost.strings;
9697dd7cddfSDavid du Colombier mem->saved = omem->saved;
9707dd7cddfSDavid du Colombier mem->previous_status = omem->previous_status;
9717dd7cddfSDavid du Colombier { /* Concatenate free lists. */
9727dd7cddfSDavid du Colombier int i;
9737dd7cddfSDavid du Colombier
9747dd7cddfSDavid du Colombier for (i = 0; i < num_freelists; i++) {
9757dd7cddfSDavid du Colombier obj_header_t *olist = omem->freelists[i];
9767dd7cddfSDavid du Colombier obj_header_t *list = mem->freelists[i];
9777dd7cddfSDavid du Colombier
9787dd7cddfSDavid du Colombier if (olist == 0);
9797dd7cddfSDavid du Colombier else if (list == 0)
9807dd7cddfSDavid du Colombier mem->freelists[i] = olist;
9817dd7cddfSDavid du Colombier else {
9827dd7cddfSDavid du Colombier while (*(obj_header_t **) list != 0)
9837dd7cddfSDavid du Colombier list = *(obj_header_t **) list;
9847dd7cddfSDavid du Colombier *(obj_header_t **) list = olist;
9857dd7cddfSDavid du Colombier }
9867dd7cddfSDavid du Colombier }
9873ff48bf5SDavid du Colombier if (omem->largest_free_size > mem->largest_free_size)
9883ff48bf5SDavid du Colombier mem->largest_free_size = omem->largest_free_size;
9897dd7cddfSDavid du Colombier }
9907dd7cddfSDavid du Colombier gs_free_object((gs_memory_t *) mem, saved, "combine_space(saved)");
9917dd7cddfSDavid du Colombier alloc_open_chunk(mem);
9927dd7cddfSDavid du Colombier }
9937dd7cddfSDavid du Colombier /* Free the changes chain for a level 0 .forgetsave, */
9947dd7cddfSDavid du Colombier /* resetting the l_new flag in the changed refs. */
9957dd7cddfSDavid du Colombier private void
forget_changes(gs_ref_memory_t * mem)9967dd7cddfSDavid du Colombier forget_changes(gs_ref_memory_t * mem)
9977dd7cddfSDavid du Colombier {
9987dd7cddfSDavid du Colombier register alloc_change_t *chp = mem->changes;
9997dd7cddfSDavid du Colombier alloc_change_t *next;
10007dd7cddfSDavid du Colombier
10017dd7cddfSDavid du Colombier for (; chp; chp = next) {
10027dd7cddfSDavid du Colombier ref_packed *prp = chp->where;
10037dd7cddfSDavid du Colombier
10047dd7cddfSDavid du Colombier if_debug1('U', "[U]forgetting change 0x%lx\n", (ulong) chp);
10057dd7cddfSDavid du Colombier if (!r_is_packed(prp))
10067dd7cddfSDavid du Colombier r_clear_attrs((ref *) prp, l_new);
10077dd7cddfSDavid du Colombier next = chp->next;
10087dd7cddfSDavid du Colombier gs_free_object((gs_memory_t *) mem, chp, "forget_changes");
10097dd7cddfSDavid du Colombier }
10107dd7cddfSDavid du Colombier mem->changes = 0;
10117dd7cddfSDavid du Colombier }
10127dd7cddfSDavid du Colombier /* Update the streams list when forgetting a save. */
10137dd7cddfSDavid du Colombier private void
file_forget_save(gs_ref_memory_t * mem)10147dd7cddfSDavid du Colombier file_forget_save(gs_ref_memory_t * mem)
10157dd7cddfSDavid du Colombier {
10167dd7cddfSDavid du Colombier const alloc_save_t *save = mem->saved;
10177dd7cddfSDavid du Colombier stream *streams = mem->streams;
10187dd7cddfSDavid du Colombier stream *saved_streams = save->state.streams;
10197dd7cddfSDavid du Colombier
10207dd7cddfSDavid du Colombier if_debug4('u', "[u%d]file_forget_save 0x%lx + 0x%lx for 0x%lx\n",
10217dd7cddfSDavid du Colombier mem->space, (ulong) streams, (ulong) saved_streams,
10227dd7cddfSDavid du Colombier (ulong) save);
10237dd7cddfSDavid du Colombier if (streams == 0)
10247dd7cddfSDavid du Colombier mem->streams = saved_streams;
10257dd7cddfSDavid du Colombier else if (saved_streams != 0) {
10267dd7cddfSDavid du Colombier while (streams->next != 0)
10277dd7cddfSDavid du Colombier streams = streams->next;
10287dd7cddfSDavid du Colombier streams->next = saved_streams;
10297dd7cddfSDavid du Colombier saved_streams->prev = streams;
10307dd7cddfSDavid du Colombier }
10317dd7cddfSDavid du Colombier }
10327dd7cddfSDavid du Colombier
10337dd7cddfSDavid du Colombier /* ------ Internal routines ------ */
10347dd7cddfSDavid du Colombier
10357dd7cddfSDavid du Colombier /* Set or reset the l_new attribute in every relevant slot. */
10367dd7cddfSDavid du Colombier /* This includes every slot on the current change chain, */
10377dd7cddfSDavid du Colombier /* and every (ref) slot allocated at this save level. */
10387dd7cddfSDavid du Colombier /* Return the number of bytes of data scanned. */
10397dd7cddfSDavid du Colombier private long
save_set_new(gs_ref_memory_t * mem,bool to_new)10407dd7cddfSDavid du Colombier save_set_new(gs_ref_memory_t * mem, bool to_new)
10417dd7cddfSDavid du Colombier {
10427dd7cddfSDavid du Colombier long scanned = 0;
10437dd7cddfSDavid du Colombier
10447dd7cddfSDavid du Colombier /* Handle the change chain. */
10457dd7cddfSDavid du Colombier save_set_new_changes(mem, to_new);
10467dd7cddfSDavid du Colombier
10477dd7cddfSDavid du Colombier /* Handle newly allocated ref objects. */
10487dd7cddfSDavid du Colombier SCAN_MEM_CHUNKS(mem, cp) {
10497dd7cddfSDavid du Colombier if (cp->has_refs) {
10507dd7cddfSDavid du Colombier bool has_refs = false;
10517dd7cddfSDavid du Colombier
10527dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
10537dd7cddfSDavid du Colombier DO_ALL
10547dd7cddfSDavid du Colombier if_debug3('U', "[U]set_new scan(0x%lx(%u), %d)\n",
10557dd7cddfSDavid du Colombier (ulong) pre, size, to_new);
10567dd7cddfSDavid du Colombier if (pre->o_type == &st_refs) {
10577dd7cddfSDavid du Colombier /* These are refs, scan them. */
10587dd7cddfSDavid du Colombier ref_packed *prp = (ref_packed *) (pre + 1);
10597dd7cddfSDavid du Colombier ref_packed *next = (ref_packed *) ((char *)prp + size);
10607dd7cddfSDavid du Colombier #ifdef ALIGNMENT_ALIASING_BUG
10617dd7cddfSDavid du Colombier ref *rpref;
10627dd7cddfSDavid du Colombier # define RP_REF(rp) (rpref = (ref *)rp, rpref)
10637dd7cddfSDavid du Colombier #else
10647dd7cddfSDavid du Colombier # define RP_REF(rp) ((ref *)rp)
10657dd7cddfSDavid du Colombier #endif
10667dd7cddfSDavid du Colombier
10677dd7cddfSDavid du Colombier if_debug2('U', "[U]refs 0x%lx to 0x%lx\n",
10687dd7cddfSDavid du Colombier (ulong) prp, (ulong) next);
10697dd7cddfSDavid du Colombier has_refs = true;
10707dd7cddfSDavid du Colombier scanned += size;
10717dd7cddfSDavid du Colombier /* We know that every block of refs ends with */
10727dd7cddfSDavid du Colombier /* a full-size ref, so we only need the end check */
10737dd7cddfSDavid du Colombier /* when we encounter one of those. */
10747dd7cddfSDavid du Colombier if (to_new)
10757dd7cddfSDavid du Colombier while (1) {
10767dd7cddfSDavid du Colombier if (r_is_packed(prp))
10777dd7cddfSDavid du Colombier prp++;
10787dd7cddfSDavid du Colombier else {
10797dd7cddfSDavid du Colombier RP_REF(prp)->tas.type_attrs |= l_new;
10807dd7cddfSDavid du Colombier prp += packed_per_ref;
10817dd7cddfSDavid du Colombier if (prp >= next)
10827dd7cddfSDavid du Colombier break;
10837dd7cddfSDavid du Colombier }
10847dd7cddfSDavid du Colombier } else
10857dd7cddfSDavid du Colombier while (1) {
10867dd7cddfSDavid du Colombier if (r_is_packed(prp))
10877dd7cddfSDavid du Colombier prp++;
10887dd7cddfSDavid du Colombier else {
10897dd7cddfSDavid du Colombier RP_REF(prp)->tas.type_attrs &= ~l_new;
10907dd7cddfSDavid du Colombier prp += packed_per_ref;
10917dd7cddfSDavid du Colombier if (prp >= next)
10927dd7cddfSDavid du Colombier break;
10937dd7cddfSDavid du Colombier }
10947dd7cddfSDavid du Colombier }
10957dd7cddfSDavid du Colombier #undef RP_REF
10967dd7cddfSDavid du Colombier } else
10977dd7cddfSDavid du Colombier scanned += sizeof(obj_header_t);
10987dd7cddfSDavid du Colombier END_OBJECTS_SCAN
10997dd7cddfSDavid du Colombier cp->has_refs = has_refs;
11007dd7cddfSDavid du Colombier }
11017dd7cddfSDavid du Colombier }
11027dd7cddfSDavid du Colombier END_CHUNKS_SCAN
11037dd7cddfSDavid du Colombier if_debug2('u', "[u]set_new (%s) scanned %ld\n",
11047dd7cddfSDavid du Colombier (to_new ? "restore" : "save"), scanned);
11057dd7cddfSDavid du Colombier return scanned;
11067dd7cddfSDavid du Colombier }
11077dd7cddfSDavid du Colombier
11087dd7cddfSDavid du Colombier /* Set or reset the l_new attribute on the changes chain. */
11097dd7cddfSDavid du Colombier private void
save_set_new_changes(gs_ref_memory_t * mem,bool to_new)11107dd7cddfSDavid du Colombier save_set_new_changes(gs_ref_memory_t * mem, bool to_new)
11117dd7cddfSDavid du Colombier {
11127dd7cddfSDavid du Colombier register alloc_change_t *chp = mem->changes;
11137dd7cddfSDavid du Colombier register uint new = (to_new ? l_new : 0);
11147dd7cddfSDavid du Colombier
11157dd7cddfSDavid du Colombier for (; chp; chp = chp->next) {
11167dd7cddfSDavid du Colombier ref_packed *prp = chp->where;
11177dd7cddfSDavid du Colombier
11187dd7cddfSDavid du Colombier if_debug3('U', "[U]set_new 0x%lx: (0x%lx, %d)\n",
11197dd7cddfSDavid du Colombier (ulong)chp, (ulong)prp, new);
11207dd7cddfSDavid du Colombier if (!r_is_packed(prp)) {
11217dd7cddfSDavid du Colombier ref *const rp = (ref *) prp;
11227dd7cddfSDavid du Colombier
11237dd7cddfSDavid du Colombier rp->tas.type_attrs =
11247dd7cddfSDavid du Colombier (rp->tas.type_attrs & ~l_new) + new;
11257dd7cddfSDavid du Colombier }
11267dd7cddfSDavid du Colombier }
11277dd7cddfSDavid du Colombier }
1128