/*--------------------------------------------------------------------------- * Swapping of object programs and variables. * *--------------------------------------------------------------------------- * TODO: 'status swap' should also list the swaps/s. * TODO: Background defragmentation of the swapfile * * The swapper helps reducing the resident memory usage of the driver * by writing program code and variable values of objects into a file. * The decision which object to swap is done by the backend loop, which * hopefully selects only objects which are not in use at the moment. * * The program and variables are swapped independently because the more * complicated structure of variable values makes swapping them significantly * more expensive than swapping the program. Additionally, since programs * don't change, once a program has been swapped out, later swap ins can * all be satisfied from the image created on the first swap out - no need * for a costly rewrite of the program image. * * Every program resp. variable block swapped is identified by a "swap * number", which incidentally is the offset at which the data can be found * in the swap file. The swap number or'ed with 0x01 is stored in place of * the .prog resp. .variables pointer in the object structure. Also the * object flag O_SWAPPED is set if either one of the data blocks has been * swapped. * * Programs can be swapped only if the have but one reference - that means * that inherited or cloned objects can't swap. The line number information * for a program is included in the program's swap block. * * Variables can be swapped all the time, however, some of the _values_ * can't be removed from memory: arrays and mappings with more than one * reference (this conveniently includes recursive data structures), objects, * closure, etc. For these values, the swapper writes a binary copy of * the referencing svalue into the file. All other values are written into * the file by value and removed from memory. * * The swap file is managed by the swapper and opened as soon as it is * needed. If no other name is set, the filename defaults to * "SWAP_FILE.". The file is kept open over the whole runtime of * the driver. * * The space in the swap file is managed in blocks of different sizes: * blocks used for swapped programs and variable sets, and free blocks. * Adjacent free blocks are concatenated, of course. The block structure * of the file is mirrored in memory by a linked list of associated data * structures - this simple structure proved to be efficient enough. * * When allocating a new block, the swapper follows one of two strategies: * * swap_compact_mode == TRUE: * * In this mode, the swap file is kept short, but possibly heavily * fragmented. * * The swapper first searches a suitable free block in the whole file * and extends the swap file only if no existing free block can be * found. * * swap_compact_mode == FALSE: * * In this mode, the swapper tries to keep a "healthy" balance between * free and used blocks. For the price of a larger swap file the * fragmentation and search times are lower. * * The swapper extends the file for new blocks until more * than half of the swap file is unused. At that point the swapper * starts recycling the free blocks as in the compact mode, but only * until the free blocks occupy only 1/4th of the swap file - then * the swapper switches back to immediate extension. * *--------------------------------------------------------------------------- */ #include "driver.h" #include "typedefs.h" #include #include #include #include #include "swap.h" #include "array.h" #include "backend.h" #include "closure.h" #include "comm.h" #include "gcollect.h" #include "interpret.h" #include "main.h" #include "mapping.h" #include "mempools.h" #include "mstrings.h" #include "object.h" #include "otable.h" #include "prolang.h" #include "random.h" #include "simulate.h" #include "simul_efun.h" #include "stdstrings.h" #include "strfuns.h" #ifdef USE_STRUCTS #include "structs.h" #endif /* USE_STRUCTS */ #include "svalue.h" #include "wiz_list.h" #include "xalloc.h" #include "../mudlib/sys/debug_info.h" /*-------------------------------------------------------------------------*/ typedef struct swap_block_s swap_block_t; typedef struct varblock_s varblock_t; typedef struct free_swapped_mapping_locals_s free_swapped_mapping_locals_t; /* --- struct swap_block_s * * A linked list of these structures describes the use of the * swapfile. Every structure describes one continguous block * in the swapfile with the given size. A positive size denotes * a free block, a negative size a used block. */ struct swap_block_s { swap_block_t *next; mp_int size; }; /* --- struct varblock_s * * Varblocks are used to store the data from variables. * The storage area is located _before_ the varblock_t so that * data-storing functions need to know only the storage pointer .current * and the remaining size .rest. */ struct varblock_s { /* unsigned char data[]: Allocated memory */ unsigned char *current; /* First free byte */ mp_int rest; /* Number of free bytes */ char *start; /* Start of the memory area */ }; /* --- struct free_swapped_mapping_locals_s * * Auxiliary datastructure to free a mapping after swapping out. */ struct free_swapped_mapping_locals_s { p_int num_values; /* width of the mapping */ unsigned char *p; /* current position in buffer */ }; #define LOW_WATER_MARK (swapfile_size >> 2) #define HIGH_WATER_MARK (swapfile_size >> 1) /* The two limits for non-compact swapping. */ #define SWAP_ABS(a) ((a)>0 ? (a) : (-a)) /*-------------------------------------------------------------------------*/ Bool swap_compact_mode = MY_FALSE; /* When true, the swapper tries to keep the swapfile short. */ static char file_name[MAXPATHLEN+1] = ""; /* Name of the swap file. * Defaults to "SWAP_FILE.". */ #ifdef USE_SWAP static FILE *swap_file = NULL; /* The swapfile - it is kept open all the time. */ static Bool recycle_free_space = MY_FALSE; /* True when freespace should be re-used, false if not */ static swap_block_t swap_list; /* Headblock of the list of swap blocks. */ static swap_block_t *swap_rover = NULL; /* Pointer to the current swap_block. By anchoring searches * at the rover, we potentially increase the locality of * file accesses. */ static swap_block_t *swap_previous = &swap_list; /* One prior to swap_rover. */ static /* TODO: offset_t */ mp_int current_offset; /* File offset corresponding to swap_rover. */ static mp_int swapfile_size = 0; /* Total size of the swapfile. */ #endif mp_int total_bytes_swapfree = 0; /* Free bytes in the swapfile. */ #ifdef USE_SWAP static unsigned char *last_variable_block; /* Swap during a and by the GC: address of the last variable * block to be written. */ static mp_int last_variable_swap_num; /* Swap during a and by the GC: swap number of the last variable * block to be written. */ static char *last_changed_swapped_svalue; /* Swap during a and by the GC: The last stored svalue * free_swapped_svalues() had to change. */ #endif /* Statistics (some are accessed directly from the outside) */ static mp_int num_swap_structs = 0; /* Number of swap block structs allocated. */ mp_int num_swapped = 0; /* Number of program blocks used in the swapfile (including unswapped * blocks). */ mp_int num_unswapped = 0; /* Number of program blocks read back in from the swapfile (but still * marked as allocated). */ mp_int total_bytes_swapped = 0; /* Number of program bytes stored in the swapfile (including unswapped * bytes). */ mp_int total_bytes_unswapped = 0; /* Number of program bytes read back in from the swapfile (but still * marked as allocated). */ #ifdef USE_SWAP static mp_int num_swapfree = 0; /* Number of free swap blocks. */ #endif mp_int num_vb_swapped = 0; /* Number of variables blocks in the swapfile. */ mp_int total_vb_bytes_swapped = 0; /* Total size of variables stored in the swapfile. */ #ifdef USE_SWAP static mp_int total_swap_reused = 0; /* Size of bytes reused from previously freed blocks. */ static long swap_num_searches; /* Number of searches for a free block to allocate (as opposed to * simply allocating it). */ static long swap_total_searchlength; /* Sum of search steps done when allocating a new block. */ static long swap_free_searches; /* Number of searches for a block to free. */ static long swap_free_searchlength; /* Sum of search steps done when freeing a block. */ #endif mp_int total_num_prog_blocks; /* Number of program blocks in memory. */ mp_int total_prog_block_size; /* Total size of program blocks in memory. */ #ifdef USE_SWAP /*-------------------------------------------------------------------------*/ /* Forward declarations */ static varblock_t *swap_svalues(svalue_t *, mp_int, varblock_t *); static unsigned char *free_swapped_svalues(svalue_t *, mp_int, unsigned char *); static unsigned char * dump_swapped_values (mp_int num, unsigned char * p, int indent); /*-------------------------------------------------------------------------*/ static Bool locate_out (program_t *prog) /* Prepare program for swap out: all pointers within the program * memory block are changed into offsets relative to the start of the * area. * * Return TRUE on success. */ { char *p = NULL; /* keep cc happy */ if (!prog) return MY_FALSE; #define MAKEOFFSET(type, name) (type)&p[(char *)prog->name - (char *)prog] prog->program = MAKEOFFSET(bytecode_p, program); prog->functions = MAKEOFFSET(uint32*, functions); prog->function_names = MAKEOFFSET(unsigned short *, function_names); prog->strings = MAKEOFFSET(string_t**, strings); prog->variables = MAKEOFFSET(variable_t *, variables); prog->inherit = MAKEOFFSET(inherit_t *, inherit); #ifdef USE_STRUCTS prog->struct_defs = MAKEOFFSET(struct_def_t *, struct_defs); #endif /* USE_STRUCTS */ prog->includes = MAKEOFFSET(include_t *, includes); if (prog->type_start) { prog->argument_types = MAKEOFFSET(vartype_t *, argument_types); prog->type_start = MAKEOFFSET(unsigned short *, type_start); } return MY_TRUE; #undef MAKEOFFSET } /* locate_out() */ /*-------------------------------------------------------------------------*/ static Bool locate_in (program_t *prog) /* After was swapped in, restore the intra-block pointers * from the stored offsets. * .line_numbers is not modified, and the program will get a new * id-number. * * Return TRUE on success. */ { char *p = (char *)prog; if (!prog) return MY_FALSE; prog->id_number = ++current_id_number ? current_id_number : renumber_programs(); #define MAKEPTR(type, name) (type)&p[(char *)prog->name - (char *)0] prog->program = MAKEPTR(bytecode_p, program); prog->functions = MAKEPTR(uint32*, functions); prog->function_names = MAKEPTR(unsigned short *, function_names); prog->strings = MAKEPTR(string_t**, strings); prog->variables = MAKEPTR(variable_t*, variables); prog->inherit = MAKEPTR(inherit_t*, inherit); #ifdef USE_STRUCTS prog->struct_defs = MAKEPTR(struct_def_t*, struct_defs); #endif /* USE_STRUCTS */ prog->includes = MAKEPTR(include_t*, includes); if (prog->type_start) { prog->argument_types = MAKEPTR(vartype_t *, argument_types); prog->type_start = MAKEPTR(unsigned short *, type_start); } return MY_TRUE; #undef MAKEPTR } /* locate_in() */ /*-------------------------------------------------------------------------*/ static mp_int swap_alloc (mp_int size) /* Find a free block of sufficient in the swap file and allocate * it. If there is none, add one at the end of the file. * Return the offset of the block from the beginning of the file, * and let the swap_rover point to the swap_block_t. */ { swap_block_t *mark, *last; int save_privilege; /* Make sure the size is something even, to meet the requirement * that all swap offsets are even. */ size = (size + sizeof(char*)-1) & ~(sizeof(char*)-1); save_privilege = malloc_privilege; malloc_privilege = MALLOC_SYSTEM; if (!swap_compact_mode) { /* Determine the allocation mode */ if (!recycle_free_space) { if (total_bytes_swapfree < HIGH_WATER_MARK) goto alloc_new_space; recycle_free_space = MY_TRUE; } else { if (total_bytes_swapfree < LOW_WATER_MARK) { recycle_free_space = MY_FALSE; goto alloc_new_space; } } } /* if (swap_compact_mode) */ swap_num_searches++; /* Search for a free block, and if necessary allocated it */ mark = swap_rover; for (;;) { swap_total_searchlength++; /* Wrap-around the end of the list? */ if (!swap_rover) { swap_rover = &swap_list; swap_previous = NULL; current_offset = 0; } if (size <= swap_rover->size) { /* Found a suitable block */ total_bytes_swapfree -= size; total_swap_reused += size; /* perfect fit? */ if (size == swap_rover->size) { swap_rover->size = -size; num_swapfree--; malloc_privilege = save_privilege; return current_offset; } /* Unperfect fit: split the block in two. * num_swapfree remains unchanged. */ num_swap_structs++; mark = pxalloc(sizeof(swap_block_t)); mark->size = swap_rover->size - size; swap_rover->size = -size; mark->next = swap_rover->next; swap_rover->next = mark; malloc_privilege = save_privilege; return current_offset; } /* Block too small: try the next */ current_offset += SWAP_ABS(swap_rover->size); swap_previous = swap_rover; swap_rover = swap_rover->next; if (swap_rover == mark) /* Once around the list without success */ { alloc_new_space: /* Allocate a new block and add it to the swap file */ last = swap_previous; while ( NULL != (mark = last->next) ) last = mark; num_swap_structs++; mark = pxalloc(sizeof(swap_block_t)); mark->next = NULL; last->next = mark; mark->size = -size; if (!swap_rover) swap_rover = mark; swapfile_size += size; malloc_privilege = save_privilege; return swapfile_size - size; } } /* for() */ /* NOTREACHED */ } /* swap_alloc() */ /*-------------------------------------------------------------------------*/ static void swap_free (mp_int offset) /* Free the swap block at the given . */ { swap_free_searches++; /* If we are already after the block, reset the rover */ if (offset < current_offset) { swap_rover = swap_list.next; swap_previous = &swap_list; current_offset = 0; } /* Set the rover on the block to free */ while (current_offset < offset && swap_rover) { swap_free_searchlength++; swap_previous = swap_rover; current_offset += SWAP_ABS(swap_rover->size); swap_rover = swap_rover->next; } /* Sanity checks */ if (current_offset != offset || !swap_rover) fatal("Bad swapfile offset.\n"); if (swap_rover->size > 0) fatal("Freeing non-allocated block within swap file.\n"); swap_rover->size = -swap_rover->size; /* Make the size positive */ total_bytes_swapfree += swap_rover->size; num_swapfree++; /* first skip any allocated block adjacent to the one just freed */ if (swap_previous->size <= 0) { swap_previous = swap_rover; current_offset += swap_rover->size; swap_rover = swap_rover->next; } /* now collapse adjacent free blocks */ while (swap_rover && swap_rover->size > 0) { swap_previous->size += swap_rover->size; current_offset += swap_rover->size; swap_previous->next = swap_rover->next; num_swap_structs--; pfree(swap_rover); num_swapfree--; swap_rover = swap_previous->next; } } /* swap_free() */ /*-------------------------------------------------------------------------*/ static p_int store_swap_block (void * buffer, mp_int size) /* Store the memory block of bytes into the swapfile * and return the offset at which it was stored. * Return -1 on a failure. * * The swapfile is opened it necessary. */ { mp_int offset; /* Make sure the swap file is open. */ if (swap_file == NULL) { if (*file_name == '\0') { sprintf(file_name, "%s.%s", SWAP_FILE, query_host_name()); } swap_file = fopen(file_name, "w+b"); /* Leave this file pointer open! */ if (swap_file == NULL) { debug_message("%s Couldn't open swap file.\n", time_stamp()); return -1; } } /* Find a free swap block */ offset = swap_alloc(size); /* Seek and write the data */ if (fseek(swap_file, offset, 0) == -1) { debug_message("%s Couldn't seek the swap file, errno %d, " "offset %"PRIdMPINT".\n", time_stamp(), errno, offset); return -1; } if (fwrite((char *)buffer, size, 1, swap_file) != 1) { debug_message("%s I/O error in swap.\n", time_stamp()); return -1; } return offset; } /* store_swap_block() */ /*-------------------------------------------------------------------------*/ static p_int store_swap_block2 ( void * buffer1, mp_int size1 , void * buffer2, mp_int size2 ) /* Store the memory blocks of bytes and of * bytes into one block in the swapfile and return the offset at * which it was stored. * Return -1 on a failure. * * The swapfile is opened it necessary. */ { mp_int offset; /* Make sure the swap file is open. */ if (swap_file == NULL) { if (*file_name == '\0') { sprintf(file_name, "%s.%s", SWAP_FILE, query_host_name()); } swap_file = fopen(file_name, "w+b"); /* Leave this file pointer open! */ if (swap_file == NULL) { debug_message("%s Couldn't open swap file.\n", time_stamp()); return -1; } } /* Find a free swap block */ offset = swap_alloc(size1 + size2); /* Seek and write the data */ if (fseek(swap_file, offset, 0) == -1) { debug_message("%s Couldn't seek the swap file, errno %d, " "offset %"PRIdMPINT".\n", time_stamp(), errno, offset); return -1; } if (fwrite((char *)buffer1, size1, 1, swap_file) != 1) { debug_message("%s I/O error in swap.\n", time_stamp()); return -1; } if (fwrite((char *)buffer2, size2, 1, swap_file) != 1) { debug_message("%s I/O error in swap.\n", time_stamp()); return -1; } return offset; } /* store_swap_block2() */ /*-------------------------------------------------------------------------*/ Bool swap_program (object_t *ob) /* Swap out the program of object . This is only possible if the * program has only one reference, ie. is neither cloned nor inherited. * * Result is TRUE if the program could be swapped. */ { program_t *prog; p_int swap_num; if (d_flag > 1) { debug_message("%s Swap object %s (obj ref %"PRIdPINT ", prog ref %"PRIdPINT")\n", time_stamp(), get_txt(ob->name), ob->ref, ob->prog->ref); } prog = ob->prog; /* May we swap? */ if (prog->ref > 1) { if (d_flag > 1) { debug_message ("%s Program not swapped - cloned or inherited.\n" , time_stamp()); } return MY_FALSE; } /* Has this object already been swapped, and read in again ? * Then it is very easy to swap it out again. */ if (prog->swap_num >= 0) { total_bytes_unswapped -= prog->total_size; if (prog->line_numbers) total_bytes_unswapped -= prog->line_numbers->size; ob->prog = (program_t *)(prog->swap_num | 1); free_prog(prog, MY_FALSE); /* Do not free the strings or blueprint */ ob->flags |= O_SWAPPED; num_unswapped--; return MY_TRUE; } /* relocate the internal pointers */ locate_out(prog); swap_num = store_swap_block2(prog, prog->total_size , prog->line_numbers, prog->line_numbers->size); if (swap_num == -1) { locate_in(prog); return MY_FALSE; } total_bytes_swapped += prog->total_size + prog->line_numbers->size; num_swapped++; /* Free the program */ free_prog(prog, MY_FALSE); /* Don't free the shared strings or the blueprint */ /* Mark the program as swapped */ ob->prog = (program_t *)(swap_num | 1); ob->flags |= O_SWAPPED; return MY_TRUE; } /* swap_program() */ /*-------------------------------------------------------------------------*/ static varblock_t * reallocate_block (unsigned char *p, mp_int rest, mp_int count) /* Reallocate the varblock for address

and remaining size * to hold space for at least bytes more. * * Result is the new varblock_t, or NULL on failure. */ { varblock_t *tmp; char *start1, *start2; mp_int size, size2; /* Get the varblock structure */ tmp = (varblock_t *)(p + rest); start1 = tmp->start; size = (char *)tmp - start1; /* Compute the required allocation size */ size2 = size; do { rest += size2; size2 <<= 1; } while (rest < count); /* Allocate the new memory area and copy the data stored so far. */ if ( !(start2 = mb_realloc(mbSwap, size2 + sizeof(varblock_t))) ) return NULL; /* Set up the new varblock */ tmp = (varblock_t *)(start2 + size2); tmp->current = (unsigned char *)tmp - rest; tmp->rest = rest; tmp->start = start2; return tmp; } /* reallocate_block() */ /*-------------------------------------------------------------------------*/ static void swap_mapping_filter (svalue_t *key, svalue_t *values, void *extra) /* Filter to swap one mapping entry. is the varblock_t** for * the varblock to store the data in, and may be changed during the * course of this function. */ { varblock_t *block = *((varblock_t **)extra); if (block->current) { block = swap_svalues(key, 1, block); } if (block->current) { block = swap_svalues(values, *((p_int *)block->start), block); } *((varblock_t **)extra) = block; } /* swap_mapping_filter() */ /*-------------------------------------------------------------------------*/ static varblock_t * swap_svalues (svalue_t *svp, mp_int num, varblock_t *block) /* Store the svalues starting at into the varblock . * Return the (possibly reallocated) varblock. * * The data values are added to the end of the varblock in the following * formats: * * swtype := (ph_int)svp->type | TYPE_MOD_SWAPPED. * * STRING, SYMBOL: * swtype, (ph_int)svp->x, (mp_int)mstrsize(string), string * * For strings, svp->x.generic is 1 for untabled strings, and 0 for * tabled string. * POINTER, STRUCTS: * swtype, (size_t) swapsize, (size_t)size, (wiz_list_t*) user, values... * * QUOTED_ARRAY: * swtype, (ph_int)quotes, (size_t) swapsize, (size_t)size, (wiz_list_t*) user, values... * * STRUCT: * swtype, (size_t) swapsize, (struct_type_t *)type, (wiz_list_t*)user, values... * * MAPPING: * swtype, (size_t) swapsize, (p_int)width, (p_int)size, (wiz_list_t*) user, entries... * * Opaque, NUMBER, FLOAT, OBJECT, CLOSURE * svp->type, svp->x, svp->u * * Opaque are: contents of alists, empty arrays, structs/arrays/mappings with * more than one ref (this also protects against recursive data structures), * and mappings with closure or object keys (see the comment in the T_MAPPING * case for the reason). * * The 'swapsize' is the size of the data block starting from the first * byte of the 'swapsize' value. This value is used for sanity checks. */ { static Bool swapping_alist = MY_FALSE; unsigned char *p; mp_int rest; size_t swapsize; /* the swapsize value */ mp_int ss_loc; /* the location of the swapsize entry */ # define CHECK_SPACE(count) \ if (rest < (mp_int)(count)) { \ varblock_t *CStmp; \ if ( !(CStmp = reallocate_block(p, rest, count)) ) {\ CStmp = (varblock_t *)(p + rest); \ CStmp->current = NULL; \ return CStmp; \ } \ p = CStmp->current; \ rest = CStmp->rest; \ } # define ADD_TO_BLOCK(var) \ memcpy(p, &var, sizeof(var)); \ p += sizeof(var); \ rest -= sizeof(var); \ # define SWAP_SVALUES(svp, num) {\ varblock_t *tmp; \ tmp = (varblock_t *)(p + rest); \ tmp->current = p; \ tmp->rest = rest; \ tmp = swap_svalues(svp, num, tmp); \ if ( !(p = tmp->current) ) { \ return tmp; \ } \ rest = tmp->rest; \ } # define INIT_SWAPSIZE() \ { \ varblock_t *ustmp; \ ustmp = (varblock_t *)(p + rest); \ ss_loc = p - (unsigned char *)ustmp->start; \ } \ swapsize = 0; \ ADD_TO_BLOCK(swapsize); # define UPDATE_SWAPSIZE() \ { \ varblock_t *ustmp; \ ustmp = (varblock_t *)(p + rest); \ swapsize = (p - (unsigned char *)ustmp->start) - ss_loc; \ } \ memcpy(p - swapsize, &swapsize, sizeof(swapsize)); p = block->current; rest = block->rest; /* Loop over all values */ for (; --num >= 0; svp++) { switch(svp->type) { case T_STRING: /* Use x.generic as flag whether the string is tabled or not */ svp->x.generic = (mstr_tabled(svp->u.str) ? 0 : 1); /* FALL THROUGH */ case T_SYMBOL: { mp_int len, size; if (swapping_alist) goto swap_opaque; len = mstrsize(svp->u.str); size = 1 + sizeof svp->x + sizeof(len) + len; CHECK_SPACE(size) rest -= size; *p++ = svp->type | T_MOD_SWAPPED; memcpy(p, &svp->x, sizeof(svp->x)); p += sizeof svp->x; memcpy(p, &len, sizeof(len)); p += sizeof len; memcpy(p, get_txt(svp->u.str), len); p += len; break; } case T_POINTER: { size_t size; size = VEC_SIZE(svp->u.vec); if (svp->u.vec->ref > 1 || !size || swapping_alist) goto swap_opaque; if (size > 1 && is_ordered(svp->u.vec)) swapping_alist = MY_TRUE; CHECK_SPACE(1 + sizeof(swapsize) + sizeof(size) + sizeof(wiz_list_t *)) *p++ = svp->type | T_MOD_SWAPPED; rest--; INIT_SWAPSIZE(); ADD_TO_BLOCK(size) ADD_TO_BLOCK(svp->u.vec->user) SWAP_SVALUES(svp->u.vec->item, size) UPDATE_SWAPSIZE(); swapping_alist = MY_FALSE; break; } #ifdef USE_STRUCTS case T_STRUCT: { struct_t *st = svp->u.strct; size_t size; size = struct_size(st); if (st->ref > 1 || swapping_alist) goto swap_opaque; CHECK_SPACE(1 + sizeof(swapsize) + sizeof(struct_type_t *) + sizeof(wiz_list_t *)) *p++ = svp->type | T_MOD_SWAPPED; rest--; INIT_SWAPSIZE(); ADD_TO_BLOCK(st->type) ADD_TO_BLOCK(st->user) SWAP_SVALUES(st->member, size) UPDATE_SWAPSIZE(); swapping_alist = MY_FALSE; break; } #endif /* USE_STRUCTS */ case T_QUOTED_ARRAY: { size_t size; size = VEC_SIZE(svp->u.vec); if (svp->u.vec->ref > 1 || swapping_alist) goto swap_opaque; CHECK_SPACE( 1 + sizeof(swapsize)+ sizeof svp->x.quotes + sizeof size + sizeof(wiz_list_t *) ) *p++ = T_QUOTED_ARRAY | T_MOD_SWAPPED; rest--; ADD_TO_BLOCK(svp->x.quotes) INIT_SWAPSIZE(); /* The odd order of swapsize makes the swap-in code simpler */ ADD_TO_BLOCK(size) ADD_TO_BLOCK(svp->u.vec->user) SWAP_SVALUES(svp->u.vec->item, size) UPDATE_SWAPSIZE(); break; } case T_MAPPING: { mapping_t *m = svp->u.map; p_int num_entries, num_values, save; varblock_t *tmp; if (m->ref > 1 || swapping_alist) goto swap_opaque; /* Mappings with object or closure keys can get stale, which * necessiates special treatment in garbage_collection(). * The GC however requires all such mappings to be resident, * so they can't be swapped. * This alleviates the swapper from the need to check the * mapping for destructed object keys. */ if (mapping_references_objects(m)) { goto swap_opaque; } CHECK_SPACE( 1 + sizeof(swapsize)+ sizeof num_values + sizeof num_entries + sizeof m->user ) *p++ = T_MAPPING | T_MOD_SWAPPED; rest--; INIT_SWAPSIZE(); num_values = m->num_values; /* The type of num_values might be wider than m->num_values. */ ADD_TO_BLOCK(num_values); num_entries = MAP_SIZE(m); ADD_TO_BLOCK(num_entries); ADD_TO_BLOCK(m->user); tmp = (varblock_t *)(p + rest); tmp->current = p; tmp->rest = rest; save = *((p_int *)tmp->start); *((p_int *)tmp->start) = m->num_values; walk_mapping(m, swap_mapping_filter, &tmp); *((p_int *)tmp->start) = save; if ( !(p = tmp->current) ) { return tmp; } rest = tmp->rest; UPDATE_SWAPSIZE(); break; } case T_NUMBER: case T_FLOAT: case T_OBJECT: case T_CLOSURE: swap_opaque: /* opaque swapped data must be prevented from recursive freeing */ CHECK_SPACE(sizeof(*svp)) *p++ = svp->type; rest--; ADD_TO_BLOCK(svp->x) ADD_TO_BLOCK(svp->u) break; default: fatal("bad type %d in swap_svalues()\n", svp->type); } } /* for() */ /* All saved - construct the varblock to return */ { varblock_t *tmp; tmp = (varblock_t *)(p + rest); tmp->current = p; tmp->rest = rest; return tmp; } # undef SWAP_SVALUES # undef ADD_TO_BLOCK # undef CHECK_SPACE # undef INIT_SWAPSIZE # undef UPDATE_SWAPSIZE } /* swap_svalues() */ /*-------------------------------------------------------------------------*/ #if 0 static unsigned char * check_swapped_values (mp_int num, unsigned char * p) /* Check the content of the swap buffer starting at

, supposedly holding * svalues. * Result is a pointer to the first byte after the data block, or NULL * on an error. */ { size_t swapsize; unsigned char * start; /* Start value of

for swapsize checks */ # define GET_SWAPSIZE() \ start = p; \ memcpy(&swapsize, p, sizeof(swapsize)); \ p += sizeof(swapsize); # define CHECK_SWAPSIZE() \ if (start + swapsize != p) \ { \ fprintf(stderr \ , "--- Incorrect swapsize on check: expected %zu bytes, " \ "read %zu (%p .. %p)\n" \ , swapsize \ , (size_t)(p - start), start, p \ ); \ return NULL; \ } /* For all values yadda yadda... */ while (--num >= 0) { svalue_t sv; sv.type = *p & ~T_MOD_SWAPPED; /* get the original type */ switch(*p++) { case T_STRING | T_MOD_SWAPPED: case T_SYMBOL | T_MOD_SWAPPED: { mp_int len; memcpy(&sv.x, p, sizeof sv.x); p += sizeof sv.x; memcpy(&len, p, sizeof len); p += sizeof len; p += len; break; } case T_QUOTED_ARRAY | T_MOD_SWAPPED: memcpy(&sv.x, p, sizeof sv.x); p += sizeof sv.x; /* FALLTHROUGH */ case T_POINTER | T_MOD_SWAPPED: { size_t size; wiz_list_t *user; GET_SWAPSIZE(); memcpy(&size, p, sizeof size); p += sizeof size; memcpy(&user, p, sizeof user); p += sizeof user; p = check_swapped_values(size, p); if (!p) return NULL; CHECK_SWAPSIZE(); break; } #ifdef USE_STRUCTS case T_STRUCT | T_MOD_SWAPPED: { wiz_list_t *user; struct_type_t *stt; GET_SWAPSIZE(); memcpy(&stt, p, sizeof stt); p += sizeof stt; memcpy(&user, p, sizeof user); p += sizeof user; p = check_swapped_values(struct_t_size(stt), p); if (!p) return NULL; CHECK_SWAPSIZE(); break; } #endif /* USE_STRUCTS */ case T_MAPPING | T_MOD_SWAPPED: { p_int num_values; wiz_list_t *user; p_int num_keys; GET_SWAPSIZE(); memcpy(&num_values, p, sizeof num_values); p += sizeof num_values; memcpy(&num_keys, p, sizeof num_keys); p += sizeof num_keys; memcpy(&user, p, sizeof user); p += sizeof user; p = check_swapped_values(num_keys*(1+num_values), p); if (!p) return NULL; CHECK_SWAPSIZE(); break; } case T_STRING: case T_SYMBOL: case T_POINTER: #ifdef USE_STRUCTS case T_STRUCT: #endif /* USE_STRUCTS */ case T_QUOTED_ARRAY: case T_MAPPING: case T_NUMBER: case T_FLOAT: case T_OBJECT: case T_CLOSURE: p += sizeof sv.x; p += sizeof sv.u; break; default: return NULL; } } /* for() */ return p; # undef GET_SWAPSIZE # undef CHECK_SWAPSIZE } /* check_swapped_values() */ #endif /*-------------------------------------------------------------------------*/ static unsigned char * dump_swapped_values (mp_int num, unsigned char * p, int indent) /* Dump the content of the swap buffer starting at

, supposedly holding * svalues, to stderr. * Result is a pointer to the first byte after the data block, or NULL * on an irrecoverable error. * * This function is called when free_swapped_values() or * read_unswapped_values() detect an inconsistency. */ { mp_int max_num = num; unsigned char * block = p; size_t swapsize; unsigned char * start; /* Start value of

for swapsize checks */ # define GET_SWAPSIZE() \ start = p; \ memcpy(&swapsize, p, sizeof(swapsize)); \ p += sizeof(swapsize); # define CHECK_SWAPSIZE() \ if (start + swapsize != p) \ { \ fprintf(stderr \ , "%.*s--- Incorrect swapsize: expected %zu bytes, " \ "read %zu (%p .. %p)\n" \ , indent, " " \ , swapsize \ , (size_t)(p - start), start, p \ ); \ } /* For all values yadda yadda... */ while (--num >= 0) { svalue_t sv; sv.type = *p & ~T_MOD_SWAPPED; /* get the original type */ fprintf(stderr, "%.*s%16p (%6zu) [%3"PRIdMPINT"]: type %d" , indent, " " , p, (size_t)(p - block) , max_num - num - 1 , sv.type ); switch(*p++) { case T_STRING | T_MOD_SWAPPED: case T_SYMBOL | T_MOD_SWAPPED: { mp_int len; memcpy(&sv.x, p, sizeof sv.x); p += sizeof sv.x; memcpy(&len, p, sizeof len); p += sizeof len; fprintf(stderr, " string (%d) : '%.*s'\n" , (int)sv.x.generic, (int)len, p ); p += len; break; } case T_QUOTED_ARRAY | T_MOD_SWAPPED: memcpy(&sv.x, p, sizeof sv.x); p += sizeof sv.x; /* FALLTHROUGH */ case T_POINTER | T_MOD_SWAPPED: { size_t size; wiz_list_t *user; GET_SWAPSIZE(); memcpy(&size, p, sizeof size); p += sizeof size; memcpy(&user, p, sizeof user); p += sizeof user; fprintf(stderr, " array: %zu values\n", size); p = dump_swapped_values(size, p, indent+2); if (!p) return NULL; CHECK_SWAPSIZE(); break; } #ifdef USE_STRUCTS case T_STRUCT | T_MOD_SWAPPED: { wiz_list_t *user; struct_type_t *stt; GET_SWAPSIZE(); memcpy(&stt, p, sizeof stt); p += sizeof stt; memcpy(&user, p, sizeof user); p += sizeof user; fprintf(stderr, " struct '%s': %ld values\n" , get_txt(stt->name), (long)struct_t_size(stt)); p = dump_swapped_values(struct_t_size(stt), p, indent+2); if (!p) return NULL; CHECK_SWAPSIZE(); break; } #endif /* USE_STRUCTS */ case T_MAPPING | T_MOD_SWAPPED: { p_int num_values; wiz_list_t *user; p_int num_keys; GET_SWAPSIZE(); memcpy(&num_values, p, sizeof num_values); p += sizeof num_values; memcpy(&num_keys, p, sizeof num_keys); p += sizeof num_keys; memcpy(&user, p, sizeof user); p += sizeof user; fprintf(stderr, " mapping: %"PRIdPINT" keys, %"PRIdPINT" values\n" , num_keys, num_values); p = dump_swapped_values(num_keys*(1+num_values), p, indent+2); if (!p) return NULL; CHECK_SWAPSIZE(); break; } case T_STRING: case T_SYMBOL: case T_POINTER: #ifdef USE_STRUCTS case T_STRUCT: #endif /* USE_STRUCTS */ case T_QUOTED_ARRAY: case T_MAPPING: case T_NUMBER: case T_FLOAT: case T_OBJECT: case T_CLOSURE: p += sizeof sv.x; p += sizeof sv.u; fprintf(stderr, " opaque\n"); break; default: fprintf(stderr, " bad type\n"); return NULL; } } /* for() */ return p; # undef GET_SWAPSIZE # undef CHECK_SWAPSIZE } /* dump_swapped_values() */ /*-------------------------------------------------------------------------*/ static void free_swapped_mapping_filter (svalue_t *key, svalue_t *values, void *extra) /* Filterfunction to free a swapped-out mapping. * is a (free_swapped_mapping_locals_t *), and ->p is updated. */ { free_swapped_mapping_locals_t *l; unsigned char *p; l = (free_swapped_mapping_locals_t *)extra; p = l->p; p = free_swapped_svalues(key, 1, p); p = free_swapped_svalues(values, l->num_values, p); l->p = p; } /* free_swapped_mapping_filter */ /*-------------------------------------------------------------------------*/ static unsigned char * free_swapped_svalues (svalue_t *svp, mp_int num, unsigned char *p) /* Free the svalues starting at after they have been swapped. *

is the pointer into the buffer where this block of svalues has * been stored in. * * The algorithm is that this function mirrors the saving algorithm * of swap_svalues(), just that it reads what swap_svalues() has stored * in the buffer and frees all svalues with a T_MOD_SWAPPED flag. * * The function acknowledges that it may be called from the garbage * collector, which means that some of the object and closure values * stored with swap_svalues() might have become invalid meanwhile. * In those cases, the stored data is adjusted. * * Take care to not interfere with a garbage_collection in progress! */ { mp_int max_num = num; unsigned char * block = p; size_t swapsize; unsigned char * start; # define GET_SWAPSIZE() \ start = p; \ memcpy(&swapsize, p, sizeof(swapsize)); \ p += sizeof(swapsize); # define CHECK_SWAPSIZE() \ if (start + swapsize != p) \ { \ dump_swapped_values(max_num, block, 0); \ fatal("svalue type %d: expected %zu bytes, read %zu (%p .. %p)\n" \ , (int)svp->type, swapsize \ , (size_t)(p - start), start, p \ ); \ } for (; --num >= 0; svp++) { switch(*p) { case T_STRING | T_MOD_SWAPPED: case T_SYMBOL | T_MOD_SWAPPED: { mp_int strsize; p += 1 + sizeof(svp->x); memcpy(&strsize, p, sizeof(strsize)); p += sizeof(strsize) + strsize; if (!gc_status) free_mstring(svp->u.str); } break; case T_QUOTED_ARRAY | T_MOD_SWAPPED: p += sizeof svp->x; /* FALLTHROUGH */ case T_POINTER | T_MOD_SWAPPED: p += 1; GET_SWAPSIZE(); p += sizeof(size_t) + sizeof(wiz_list_t *); p = free_swapped_svalues(svp->u.vec->item, VEC_SIZE(svp->u.vec), p); free_empty_vector(svp->u.vec); CHECK_SWAPSIZE(); break; #ifdef USE_STRUCTS case T_STRUCT | T_MOD_SWAPPED: { p += 1; GET_SWAPSIZE(); p += sizeof(struct_type_t *) + sizeof(wiz_list_t *); p = free_swapped_svalues(svp->u.strct->member, struct_size(svp->u.strct), p); struct_free_empty(svp->u.strct); CHECK_SWAPSIZE(); break; } #endif /* USE_STRUCTS */ case T_MAPPING | T_MOD_SWAPPED: { /* beware: a mapping can get unswappable when it is entered * in the stale_mapping list. Or the stale_mapping list has to * be recoded to include swapped mappings. */ free_swapped_mapping_locals_t l; p += 1; GET_SWAPSIZE(); p += sizeof(p_int) + sizeof(p_int) + sizeof(wiz_list_t *); l.num_values = svp->u.map->num_values; l.p = p; walk_mapping(svp->u.map, free_swapped_mapping_filter, &l); p = l.p; free_empty_mapping(svp->u.map); CHECK_SWAPSIZE(); break; } case T_OBJECT: if (svp->type == T_NUMBER) { /* Object was destructed */ *p++ = T_NUMBER; memcpy(p, &svp->x, sizeof svp->x); p += sizeof svp->x; memcpy(p, &svp->u, sizeof svp->u); p += sizeof svp->u; last_changed_swapped_svalue = (char*)p; break; } goto advance; case T_CLOSURE: /* the garbage collector replaces closures bound to destructed * objects by F_UNDEF */ if (is_undef_closure(svp)) /* this shouldn't happen */ { if ( memcmp(p+1, &svp->x, sizeof svp->x) || memcmp(p+1+sizeof svp->x, &svp->u, sizeof svp->u)) { p++; memcpy(p, (char *)&svp->x, sizeof svp->x); p += sizeof svp->x; memcpy(p, (char *)&svp->u, sizeof svp->u); p += sizeof svp->u; last_changed_swapped_svalue = (char*)p; break; } } case T_STRING: case T_SYMBOL: case T_POINTER: #ifdef USE_STRUCTS case T_STRUCT: #endif /* USE_STRUCTS */ case T_QUOTED_ARRAY: case T_MAPPING: case T_NUMBER: case T_FLOAT: advance: /* Opaque storage: skip it */ p += 1 + sizeof svp->x + sizeof svp->u; break; default: dump_swapped_values(max_num, block, 0); fatal("bad type %d in free_swapped_svalues()\n", *p); } } return p; # undef GET_SWAPSIZE # undef CHECK_SWAPSIZE } /* free_swapped_svalues() */ /*-------------------------------------------------------------------------*/ Bool swap_variables (object_t *ob) /* Swap the variables of object into the swap file. * The simul_efun object is not swapped. * * This function might be called recursively through the garbagecollector. * In that case it completes the swap operation begun before. * * Return TRUE on success, FALSE else. */ { char *start; p_int total_size; varblock_t *block; p_int swap_num; unsigned short num_variables; #define VARBLOCK_STARTSIZE 0x800 if (!ob->variables) return MY_TRUE; if (ob == simul_efun_object) return MY_TRUE; if (gc_status) { /* During a GC, the swapper is called in close swap-in/swap-out * sequences. To minimized interaction with the allocator, * the swap out only has to check if svalues changed due to * the GC; otherwise it is sufficient to just pretend to swap * out and free the associated memory. */ num_variables = ob->prog->num_variables; last_changed_swapped_svalue = NULL; (void)free_swapped_svalues( ob->variables, num_variables, last_variable_block ); if (last_changed_swapped_svalue) { if (fseek(swap_file, last_variable_swap_num + sizeof(p_int), 0) == -1) { fatal("Couldn't seek the swap file, errno %d, offset %" PRIdMPINT".\n", errno, last_variable_swap_num + sizeof(p_int)); } if (fwrite( last_variable_block, last_changed_swapped_svalue - (char *)last_variable_block, 1, swap_file) != 1) { fatal("I/O error in swap.\n"); } } mb_free(mbSwap); xfree(ob->variables); ob->variables = (svalue_t *)(last_variable_swap_num | 1); ob->flags |= O_SWAPPED; #ifdef CHECK_OBJECT_STAT if (check_object_stat) { fprintf(stderr, "DEBUG: OSTAT: (%ld:%ld) swapout( %p '%s') gc %d " "vars : %ld -> (%ld:%ld)\n", tot_alloc_object, tot_alloc_object_size, ob, ob->name ? get_txt(ob->name) : "", num_variables, (long)(num_variables * sizeof (svalue_t)), tot_alloc_object, tot_alloc_object_size - (num_variables * sizeof (svalue_t)) ); } #endif tot_alloc_object_size -= num_variables * sizeof (svalue_t); return MY_TRUE; } /* Get the number of variables from the program without * swapping it in. */ swap_num = (p_int)ob->prog; if (swap_num & 1) { swap_num &= ~1; swap_num += offsetof(program_t, num_variables); if (swapfile_size <= swap_num) fatal("Attempt to swap in from beyond the end of the swapfile.\n"); if (fseek(swap_file, swap_num, 0) == -1) fatal("Couldn't seek the swap file, errno %d, offset %" PRIdPINT".\n", errno, swap_num); if (fread(&num_variables, sizeof num_variables, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } } else { num_variables = ob->prog->num_variables; } /* Allocate the initial varblock and initialize it */ start = mb_alloc(mbSwap, VARBLOCK_STARTSIZE + sizeof(varblock_t)); if (!start) return MY_FALSE; block = (varblock_t *)(start + VARBLOCK_STARTSIZE); block->current = (unsigned char *)start + sizeof total_size; block->rest = VARBLOCK_STARTSIZE - sizeof total_size; block->start = start; /* Store the values */ block = swap_svalues(ob->variables, num_variables, block); if (!block->current) { /* Oops */ mb_free(mbSwap); return MY_FALSE; } /* Store the block's total size in the first word of the block */ *(p_int*)block->start = total_size = (((char *)block->current - block->start) + (sizeof(p_int) - 1)) & (~(sizeof(p_int) - 1)); /* Write the values */ swap_num = store_swap_block(block->start, total_size); if (swap_num == -1) { mb_free(mbSwap); return MY_FALSE; } /* Free the swapped values */ (void)free_swapped_svalues( ob->variables, num_variables , (unsigned char *)block->start + sizeof total_size ); num_vb_swapped++; total_vb_bytes_swapped += total_size - sizeof total_size; mb_free(mbSwap); xfree(ob->variables); #ifdef CHECK_OBJECT_STAT if (check_object_stat) { fprintf(stderr, "DEBUG: OSTAT: (%ld:%ld) swapout( %p '%s') %d " "vars : %ld -> (%ld:%ld)\n", tot_alloc_object, tot_alloc_object_size, ob, ob->name ? get_txt(ob->name) : "", num_variables, (long)(num_variables * sizeof (svalue_t)), tot_alloc_object, tot_alloc_object_size - (num_variables * sizeof (svalue_t)) ); } #endif tot_alloc_object_size -= num_variables * sizeof (svalue_t); /* Mark the variables as swapped */ ob->variables = (svalue_t *)(swap_num | 1); ob->flags |= O_SWAPPED; return MY_TRUE; #undef VARBLOCK_STARTSIZE } /* swap_variables() */ /*-------------------------------------------------------------------------*/ Bool swap (object_t *ob, int mode) /* Swap the object according to : * * & 0x01: swap program * & 0x02: swap variables * * The same flags are returned by load_ob_from_swap(). * * Result is TRUE if all requested swaps succeeded. */ { Bool result = MY_TRUE; if (ob->flags & O_DESTRUCTED) return MY_FALSE; if (mode & 2) { result = swap_variables(ob) && result; } if (mode & 1) { result = swap_program(ob) && result; } return result; } /* swap() */ /*-------------------------------------------------------------------------*/ static INLINE void clear_svalues (svalue_t *svp, mp_int num) /* Auxiliary function: clear all svalues starting at to 0. */ { for (; --num >= 0;) { *svp++ = const0; } } /* clear_svalues() */ /*-------------------------------------------------------------------------*/ static unsigned char * read_unswapped_svalues (svalue_t *svp, mp_int num, unsigned char *p) /* Restore the swapped values into , reading from the buffer * position

. * * Note: when garbage collection is done, restoring strings would give nothing * but trouble, thus, a dummy number is inserted instead. * * Return the next byte to read, or NULL when out of memory. */ { mp_int max_num = num; unsigned char * block = p; size_t swapsize; unsigned char * start; /* Start value of

for swapsize checks */ # define GET_SWAPSIZE() \ start = p; \ memcpy(&swapsize, p, sizeof(swapsize)); \ p += sizeof(swapsize); # define CHECK_SWAPSIZE() \ if (start + swapsize != p) \ { \ dump_swapped_values(max_num, block, 0); \ fatal("svalue type %d: expected %zu bytes, read %zu (%p .. %p)\n" \ , (int)svp->type, swapsize \ , (size_t)(p - start), start, p \ ); \ } /* For all values yadda yadda... */ for (;--num >= 0; svp++) { svp->type = *p & ~T_MOD_SWAPPED; /* get the original type */ switch(*p++) { case T_STRING | T_MOD_SWAPPED: case T_SYMBOL | T_MOD_SWAPPED: { string_t *s; mp_int len; memcpy(&svp->x, p, sizeof svp->x); p += sizeof svp->x; memcpy(&len, p, sizeof len); p += sizeof len; if (gc_status) { svp->type = T_NUMBER; } else { if (svp->type == T_STRING && svp->x.generic != 0) { s = new_n_mstring((char *)p, len); } else { s = new_n_tabled((char *)p, len); } if (!s) { clear_svalues(svp, num + 1); return NULL; } svp->u.str = s; } p += len; break; } case T_QUOTED_ARRAY | T_MOD_SWAPPED: memcpy(&svp->x, p, sizeof svp->x); p += sizeof svp->x; /* FALLTHROUGH */ case T_POINTER | T_MOD_SWAPPED: { size_t size; wiz_list_t *user; vector_t *v; GET_SWAPSIZE(); memcpy(&size, p, sizeof size); p += sizeof size; memcpy(&user, p, sizeof user); p += sizeof user; current_object->user = user; v = allocate_array_unlimited(size); svp->u.vec = v; if (!v) { clear_svalues(svp, num + 1); return NULL; } p = read_unswapped_svalues(v->item, size, p); if (!p) { clear_svalues(svp + 1, num); return NULL; } CHECK_SWAPSIZE(); #ifdef GC_SUPPORT if (gc_status == gcCountRefs) { /* Pretend that this memory block already existing * in the clear phase. */ clear_memory_reference(v); v->ref = 0; } #endif break; } #ifdef USE_STRUCTS case T_STRUCT | T_MOD_SWAPPED: { wiz_list_t *user; struct_t *st; struct_type_t *stt; GET_SWAPSIZE(); memcpy(&stt, p, sizeof stt); p += sizeof stt; memcpy(&user, p, sizeof user); p += sizeof user; current_object->user = user; st = struct_new(stt); (void)deref_struct_type(stt); /* just reactivate the old ref */ svp->u.strct = st; if (!st) { clear_svalues(svp, num + 1); return NULL; } p = read_unswapped_svalues(st->member, struct_size(st), p); if (!p) { clear_svalues(svp + 1, num); return NULL; } CHECK_SWAPSIZE(); #ifdef GC_SUPPORT if (gc_status == gcCountRefs) { /* Pretend that this memory block was already existing * in the clear phase. */ clear_memory_reference(st); st->ref = 0; } #endif break; } #endif /* USE_STRUCTS */ case T_MAPPING | T_MOD_SWAPPED: { mapping_t *m; p_int num_values; wiz_list_t *user; p_int num_keys; GET_SWAPSIZE(); memcpy(&num_values, p, sizeof num_values); p += sizeof num_values; memcpy(&num_keys, p, sizeof num_keys); p += sizeof num_keys; memcpy(&user, p, sizeof user); p += sizeof user; if (gc_status) { /* The garbage collector is not prepared to handle hash * mappings. On the other hand, the order of keys does * not matter here. * We can assume here that all allocation functions succeed * because the garbage collector runs with * malloc_privilege == MALLOC_SYSTEM . */ mapping_cond_t *cm; mp_int size; svalue_t *data, *svp2; m = allocate_cond_mapping(user, num_keys, num_values); svp->u.map = m; if (m->cond) { cm = m->cond; svp2 = &(cm->data[0]); size = cm->size; data = COND_DATA(cm, 0, num_values); while ( --size >= 0) { p = read_unswapped_svalues(svp2++, 1, p); p = read_unswapped_svalues(data, num_values, p); data += num_values; } #ifdef GC_SUPPORT if (gc_status == gcCountRefs) { /* Pretend that this memory block was already existing * in the clear phase. */ clear_memory_reference(m); clear_memory_reference(cm); m->ref = 0; } #endif } } else { mp_int i; wiz_list_t *save; save = current_object->user; current_object->user = user; m = allocate_mapping(num_keys, num_values); current_object->user = save; if (!m) { clear_svalues(svp, num + 1); return NULL; } svp->u.map = m; for (i = num_keys; --i >= 0;) { svalue_t sv, *data; p = read_unswapped_svalues(&sv, 1, p); /* adds 1 ref */ if (!p) break; data = get_map_lvalue_unchecked(m, &sv); /* adds another ref */ free_svalue(&sv); if (!data) break; p = read_unswapped_svalues(data, num_values, p); if (!p) break; } /* for() */ if (!p) { clear_svalues(svp + 1, num); return NULL; } } CHECK_SWAPSIZE(); break; } case T_STRING: case T_SYMBOL: case T_POINTER: #ifdef USE_STRUCTS case T_STRUCT: #endif /* USE_STRUCTS */ case T_QUOTED_ARRAY: case T_MAPPING: case T_NUMBER: case T_FLOAT: case T_OBJECT: case T_CLOSURE: memcpy(&svp->x, p, sizeof svp->x); p += sizeof svp->x; memcpy(&svp->u, p, sizeof svp->u); p += sizeof svp->u; break; default: dump_swapped_values(max_num, block, 0); fatal("bad type %d in read_unswapped_svalues()\n", svp->type); } } /* for() */ return p; # undef GET_SWAPSIZE # undef CHECK_SWAPSIZE } /* read_unswapped_svalues() */ /*-------------------------------------------------------------------------*/ static void dummy_handler(const char * fmt UNUSED, ...) /* Dummy error handler for array allocations in a swap-in. */ { #ifdef __MWERKS__ # pragma unused(fmt) #endif } /* dummy_handler() */ /*-------------------------------------------------------------------------*/ int load_ob_from_swap (object_t *ob) /* Load an object from the swap, both variables and program (without * the linenumbers). * The swap block for the variables is removed, the program swap block * is kept. * * Results are: 0: object was not swapped out * > 0: object swapped in * < 0: out of memory. * * The result (both positive and negative) can be interpreted * more detailed as well: * * result & 0x01: program swapped in * result & 0x02: variables swapped in * * The same flags are accepted by swap() as argument. */ { p_int swap_num; int result; result = 0; swap_num = (p_int)ob->prog; if (swap_num & 1) { /* Swap in the program */ program_t tmp_prog, *prog; swap_num &= ~1; if (swapfile_size <= swap_num) fatal("Attempt to swap in from beyond the end of the swapfile.\n"); if (fseek(swap_file, swap_num, 0) == -1) fatal("Couldn't seek the swap file, errno %d, offset %" PRIdPINT".\n", errno, swap_num); if (d_flag > 1) { debug_message("%s Unswap object %s (ref %"PRIdPINT")\n", time_stamp(), get_txt(ob->name), ob->ref); } /* The size of the program is unkown, so read first part to * find out. For greater efficiency we read in the full program_t * structure. */ if (fread(&tmp_prog, sizeof tmp_prog, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } tmp_prog.swap_num = swap_num; /* Allocate the memory for the program, except for the * line numbers. */ if ( !(prog = xalloc(tmp_prog.total_size)) ) return -0x80; memcpy(prog, &tmp_prog, sizeof tmp_prog); /* Read in the rest of the program */ if (tmp_prog.total_size - sizeof tmp_prog) { if (fread( ((char *)prog) + sizeof tmp_prog, tmp_prog.total_size - sizeof tmp_prog, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } } ob->prog = prog; locate_in (prog); /* relocate the internal pointers */ prog->line_numbers = NULL; /* The reference count will already be 1 ! */ total_bytes_unswapped += ob->prog->total_size; num_unswapped++; total_prog_block_size += ob->prog->total_size; total_num_prog_blocks += 1; result = 1; } swap_num = (p_int)ob->variables; if (swap_num & 1) { /* Swap in the variables */ p_int total_size; unsigned char *block; mp_int size; svalue_t *variables; object_t dummy; object_t *save_current = current_object; void (*save_handler)(const char *, ...); swap_num &= ~1; if (swapfile_size <= swap_num) fatal("Attempt to swap in from beyond the end of the swapfile.\n"); if (fseek(swap_file, swap_num, 0) == -1) fatal("Couldn't seek the swap file, errno %d, offset %" PRIdPINT".\n", errno, swap_num); if (d_flag > 1) { debug_message("%s Unswap variables of %s\n", time_stamp() , get_txt(ob->name)); } /* Read the size of the block from the file */ if (fread(&total_size, sizeof total_size, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } size = total_size - sizeof total_size; /* Allocate the memory buffer */ block = (unsigned char *) mb_alloc(mbSwap, size); if ( !block) return result | -0x80; /* Allocate the variable space */ if ( !(variables = xalloc( sizeof(svalue_t) * ob->prog->num_variables )) ) { mb_free(mbSwap); return result | -0x80; } fread(block, size, 1, swap_file); /* Prepare to restore */ current_object = &dummy; #ifdef MALLOC_LPC_TRACE dummy.name = ob->name; dummy.prog = ob->prog; #endif save_handler = allocate_array_error_handler; allocate_array_error_handler = dummy_handler; if (read_unswapped_svalues(variables, ob->prog->num_variables, block)) { ob->variables = variables; result |= 2; #ifdef CHECK_OBJECT_STAT if (check_object_stat) { fprintf(stderr, "DEBUG: OSTAT: (%ld:%ld) swapin( %p '%s') %d " "vars : %ld -> (%ld:%ld)\n", tot_alloc_object, tot_alloc_object_size, ob, ob->name ? get_txt(ob->name) : "", ob->prog->num_variables, (long)(ob->prog->num_variables * sizeof (svalue_t)), tot_alloc_object, tot_alloc_object_size + (ob->prog->num_variables * sizeof (svalue_t)) ); } #endif tot_alloc_object_size += ob->prog->num_variables * sizeof (svalue_t); if (gc_status) { /* During a GC, the swapper is called in close swap-in/swap-out * sequences. To minimized interaction with the allocator, * the memory blocks are kept for now, and freed during the * 'swap out'. */ last_variable_block = block; last_variable_swap_num = swap_num; } else { mb_free(mbSwap); swap_free(swap_num); num_vb_swapped--; total_vb_bytes_swapped -= total_size - sizeof total_size; } } else { /* Out of memory */ #ifdef CHECK_OBJECT_STAT if (check_object_stat) { fprintf(stderr, "DEBUG: OSTAT: (%ld:%ld) swapin( %p '%s') %d " "vars failed\n", tot_alloc_object, tot_alloc_object_size, ob, ob->name ? get_txt(ob->name) : "", ob->prog->num_variables); } #endif mb_free(mbSwap); result |= -0x80; } current_object = save_current; allocate_array_error_handler = save_handler; } if (!result) fatal("Loading unswapped object from swap.\n"); /* Update the object flags */ ob->flags &= ~O_SWAPPED; return result; } /* load_ob_from_swap() */ /*-------------------------------------------------------------------------*/ Bool load_line_numbers_from_swap (program_t *prog) /* Load the line numbers for program from the swap. * Result is TRUE on success. */ { linenumbers_t tmp_numbers; linenumbers_t *lines; p_int swap_num; swap_num = prog->swap_num + prog->total_size; if (swapfile_size <= swap_num) fatal("Attempt to swap in from beyond the end of the swapfile.\n"); if (fseek(swap_file, swap_num, 0) == -1) fatal("Couldn't seek the swap file, errno %d, offset %" PRIdPINT".\n", errno, swap_num); if (fread((char *)&tmp_numbers, sizeof tmp_numbers, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } if ( !(lines = xalloc(tmp_numbers.size)) ) return MY_FALSE; *lines = tmp_numbers; if (tmp_numbers.size > sizeof(tmp_numbers)) { fread(lines+1, tmp_numbers.size - sizeof(tmp_numbers) , 1, swap_file); } prog->line_numbers = lines; total_prog_block_size += lines->size; total_bytes_unswapped += lines->size; return MY_TRUE; } /* load_line_numbers_from_swap() */ /*-------------------------------------------------------------------------*/ void remove_prog_swap (program_t *prog, Bool load_line_numbers) /* Program is going to be deleted or has been changed - remove its * swapfile entry if it has one. If is true, load * the line number information back into memory before removing the swap entry. */ { p_int swap_num; swap_num = prog->swap_num; if (swap_num == -1) /* then program not swapped */ return; /* This test is good not only for debugging, but also when the * processor is on fire, to stop subsequent damage. */ if (swapfile_size <= swap_num) fatal("Attempt to remove swap entry beyond the end of the swapfile.\n"); if (!prog->line_numbers && load_line_numbers) { if (!load_line_numbers_from_swap(prog)) fatal("Can't unswap the line numbers.\n"); } if (!prog->line_numbers) { /* The linenumber information has not been unswapped, thus * we need to read the linenumber size directly from the * swapfile. */ linenumbers_t tmp_lines; if (fseek(swap_file, swap_num + prog->total_size, 0 ) == -1) fatal("Couldn't seek the swap file, errno %d, offset %" PRIdPINT".\n", errno, swap_num); if (fread(&tmp_lines, sizeof tmp_lines, 1, swap_file) != 1) { fatal("Couldn't read the swap file.\n"); } total_bytes_swapped -= tmp_lines.size; } else { total_bytes_unswapped -= prog->line_numbers->size; total_bytes_swapped -= prog->line_numbers->size; } swap_free(prog->swap_num); total_bytes_unswapped -= prog->total_size; total_bytes_swapped -= prog->total_size; num_unswapped--; num_swapped--; prog->swap_num = -1; } /* remove_prog_swap() */ #endif /*-------------------------------------------------------------------------*/ void name_swap_file (const char *name) /* Set the swap file name to a copy of . */ { xstrncpy(file_name, name, sizeof file_name); file_name[sizeof file_name - 1] = '\0'; } /* name_swap_file()*/ #ifdef USE_SWAP /*-------------------------------------------------------------------------*/ void unlink_swap_file (void) /* Called at shutdown: Remove the swap file. */ { if (swap_file == NULL) return; unlink(file_name); fclose(swap_file); } /* unlink_swap_file() */ /*-------------------------------------------------------------------------*/ #endif size_t swap_overhead (void) /* Return the overhead size of the swap structures. */ { return num_swap_structs * sizeof(swap_block_t); } /* swap_overhead() */ #ifdef USE_SWAP /*-------------------------------------------------------------------------*/ void swap_status (strbuf_t *sbuf) /* Add the information for "status swap" to the stringbuffer . */ { /* maximum seen so far: 28574 var blocks swapped, 32754860 bytes */ strbuf_addf(sbuf, "%10"PRIdMPINT" prog blocks swapped, %13"PRIdMPINT" bytes\n" "%10"PRIdMPINT" prog blocks unswapped,%12"PRIdMPINT" bytes\n" "%10"PRIdMPINT" var blocks swapped,%15"PRIdMPINT" bytes\n" "%10"PRIdMPINT" free blocks in swap,%14"PRIdMPINT" bytes\n" "Swapfile size:%31"PRIdMPINT" bytes\n" , num_swapped - num_unswapped , total_bytes_swapped - total_bytes_unswapped , num_unswapped, total_bytes_unswapped , num_vb_swapped, total_vb_bytes_swapped , num_swapfree, total_bytes_swapfree , swapfile_size ); strbuf_addf(sbuf, "Total reused space:%26"PRIdMPINT" bytes\n\n" , total_swap_reused); strbuf_addf(sbuf , "Swap: searches: %10ld average search length: %3.1f\n" "Free: searches: %10ld average search length: %3.1f\n" , swap_num_searches , (double)swap_total_searchlength / ( swap_num_searches ? swap_num_searches : 1 ) , swap_free_searches , (double)swap_free_searchlength / ( swap_free_searches ? swap_free_searches : 1 ) ); strbuf_addf(sbuf, "Overhead: %"PRIdMPINT" blocks using %" PRIdMPINT" bytes.\n", num_swap_structs, num_swap_structs * sizeof(swap_block_t) ); strbuf_addf(sbuf, "Mode: %s - Freespace recycling: %s\n" , swap_compact_mode ? "compact" : "non-compact" , (recycle_free_space || !swap_compact_mode) ? "on" : "off" ); } /* swap_status() */ /*-------------------------------------------------------------------------*/ void swap_dinfo_data (svalue_t *svp, int value) /* Fill in the data for debug_info(DINFO_DATA, DID_SWAP) * into the svalue block . * If is -1, points indeed to a value block; other it is * the index of the desired value and points to a single svalue. */ { #define ST_NUMBER(which,code) \ if (value == -1) svp[which].u.number = code; \ else if (value == which) svp->u.number = code ST_NUMBER(DID_SW_PROGS, num_swapped - num_unswapped); ST_NUMBER(DID_SW_PROG_SIZE, total_bytes_swapped - total_bytes_unswapped); ST_NUMBER(DID_SW_PROG_UNSWAPPED, num_unswapped); ST_NUMBER(DID_SW_PROG_U_SIZE, total_bytes_unswapped); ST_NUMBER(DID_SW_VARS, num_vb_swapped); ST_NUMBER(DID_SW_VAR_SIZE, total_vb_bytes_swapped); ST_NUMBER(DID_SW_FREE, num_swapfree); ST_NUMBER(DID_SW_FREE_SIZE, total_bytes_swapfree); ST_NUMBER(DID_SW_FILE_SIZE, swapfile_size); ST_NUMBER(DID_SW_REUSED, total_swap_reused); ST_NUMBER(DID_SW_SEARCHES, swap_num_searches); ST_NUMBER(DID_SW_SEARCH_LEN, swap_total_searchlength); ST_NUMBER(DID_SW_F_SEARCHES, swap_free_searches); ST_NUMBER(DID_SW_F_SEARCH_LEN, swap_free_searchlength); ST_NUMBER(DID_SW_COMPACT, swap_compact_mode); ST_NUMBER(DID_SW_RECYCLE_FREE, recycle_free_space); #undef ST_NUMBER } /* swap_dinfo_data() */ /***************************************************************************/ #endif