2009-03-03 03:27:01 +00:00
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
* 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.<hostname>". 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 <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#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.<hostname>".
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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 <prog> 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 <prog> 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 <size> 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 <offset>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
{
|
|
|
|
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 <buffer> of <size> 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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
debug_message("%s Couldn't seek the swap file, errno %d, "
|
|
|
|
"offset %"PRIdMPINT".\n", time_stamp(), errno, offset);
|
2009-03-03 03:27:01 +00:00
|
|
|
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 <buffer1> of <size1> bytes and <buffer2> of
|
|
|
|
* <size2> 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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
debug_message("%s Couldn't seek the swap file, errno %d, "
|
|
|
|
"offset %"PRIdMPINT".\n", time_stamp(), errno, offset);
|
2009-03-03 03:27:01 +00:00
|
|
|
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 <ob>. 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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
debug_message("%s Swap object %s (obj ref %"PRIdPINT
|
|
|
|
", prog ref %"PRIdPINT")\n",
|
|
|
|
time_stamp(), get_txt(ob->name), ob->ref, ob->prog->ref);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 <p> and remaining size <rest>
|
|
|
|
* to hold space for at least <count> 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. <extra> 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 <num> svalues starting at <svp> into the varblock <block>.
|
|
|
|
* 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 <p>, supposedly holding
|
|
|
|
* <num> 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 <p> 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 \
|
2009-05-21 22:41:07 +00:00
|
|
|
, "--- Incorrect swapsize on check: expected %zu bytes, " \
|
|
|
|
"read %zu (%p .. %p)\n" \
|
|
|
|
, swapsize \
|
|
|
|
, (size_t)(p - start), start, p \
|
2009-03-03 03:27:01 +00:00
|
|
|
); \
|
|
|
|
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 <p>, supposedly holding
|
|
|
|
* <num> 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 <p> 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 \
|
2009-05-21 22:41:07 +00:00
|
|
|
, "%.*s--- Incorrect swapsize: expected %zu bytes, " \
|
|
|
|
"read %zu (%p .. %p)\n" \
|
2009-03-03 03:27:01 +00:00
|
|
|
, indent, " " \
|
2009-05-21 22:41:07 +00:00
|
|
|
, swapsize \
|
|
|
|
, (size_t)(p - start), start, p \
|
2009-03-03 03:27:01 +00:00
|
|
|
); \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For all values yadda yadda... */
|
|
|
|
while (--num >= 0)
|
|
|
|
{
|
|
|
|
svalue_t sv;
|
|
|
|
|
|
|
|
sv.type = *p & ~T_MOD_SWAPPED; /* get the original type */
|
|
|
|
|
2009-05-21 22:41:07 +00:00
|
|
|
fprintf(stderr, "%.*s%16p (%6zu) [%3"PRIdMPINT"]: type %d"
|
2009-03-03 03:27:01 +00:00
|
|
|
, indent, " "
|
2009-05-21 22:41:07 +00:00
|
|
|
, p, (size_t)(p - block)
|
2009-03-03 03:27:01 +00:00
|
|
|
, 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;
|
2009-05-21 22:41:07 +00:00
|
|
|
fprintf(stderr, " array: %zu values\n", size);
|
2009-03-03 03:27:01 +00:00
|
|
|
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;
|
2009-05-21 22:41:07 +00:00
|
|
|
fprintf(stderr, " mapping: %"PRIdPINT" keys, %"PRIdPINT" values\n"
|
|
|
|
, num_keys, num_values);
|
2009-03-03 03:27:01 +00:00
|
|
|
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.
|
|
|
|
* <extra> is a (free_swapped_mapping_locals_t *), and <extra>->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 <num> svalues starting at <svp> after they have been swapped.
|
|
|
|
* <p> 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); \
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("svalue type %d: expected %zu bytes, read %zu (%p .. %p)\n" \
|
|
|
|
, (int)svp->type, swapsize \
|
|
|
|
, (size_t)(p - start), start, p \
|
2009-03-03 03:27:01 +00:00
|
|
|
); \
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <ob> 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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdMPINT".\n",
|
|
|
|
errno, last_variable_swap_num + sizeof(p_int));
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
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) : "<null>",
|
|
|
|
num_variables, (long)(num_variables * sizeof (svalue_t)),
|
|
|
|
tot_alloc_object,
|
|
|
|
tot_alloc_object_size - (num_variables * sizeof (svalue_t))
|
|
|
|
);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
#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)
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdPINT".\n", errno, swap_num);
|
2009-03-03 03:27:01 +00:00
|
|
|
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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
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) : "<null>",
|
|
|
|
num_variables, (long)(num_variables * sizeof (svalue_t)),
|
|
|
|
tot_alloc_object,
|
|
|
|
tot_alloc_object_size - (num_variables * sizeof (svalue_t))
|
|
|
|
);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
#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 <ob> according to <mode>:
|
|
|
|
*
|
|
|
|
* <mode> & 0x01: swap program
|
|
|
|
* <mode> & 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 <num> svalues starting at <svp> 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 <num> swapped values into <svp>, reading from the buffer
|
|
|
|
* position <p>.
|
|
|
|
*
|
|
|
|
* 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 <p> 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); \
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("svalue type %d: expected %zu bytes, read %zu (%p .. %p)\n" \
|
|
|
|
, (int)svp->type, swapsize \
|
|
|
|
, (size_t)(p - start), start, p \
|
2009-03-03 03:27:01 +00:00
|
|
|
); \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 <ob> 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)
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdPINT".\n", errno, swap_num);
|
2009-03-03 03:27:01 +00:00
|
|
|
if (d_flag > 1)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
debug_message("%s Unswap object %s (ref %"PRIdPINT")\n",
|
|
|
|
time_stamp(), get_txt(ob->name), ob->ref);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdPINT".\n", errno, swap_num);
|
2009-03-03 03:27:01 +00:00
|
|
|
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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
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) : "<null>",
|
|
|
|
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))
|
|
|
|
);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
#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)
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
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) : "<null>",
|
|
|
|
ob->prog->num_variables);
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
#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 <prog> 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)
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdPINT".\n", errno, swap_num);
|
2009-03-03 03:27:01 +00:00
|
|
|
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 <prog> is going to be deleted or has been changed - remove its
|
|
|
|
* swapfile entry if it has one. If <load_line_numbers> 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)
|
2009-05-21 22:41:07 +00:00
|
|
|
fatal("Couldn't seek the swap file, errno %d, offset %"
|
|
|
|
PRIdPINT".\n", errno, swap_num);
|
2009-03-03 03:27:01 +00:00
|
|
|
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 <name>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
{
|
|
|
|
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 <sbuf>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
{
|
2009-05-21 22:41:07 +00:00
|
|
|
/* 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"
|
2009-03-03 03:27:01 +00:00
|
|
|
, 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
|
|
|
|
);
|
2009-05-21 22:41:07 +00:00
|
|
|
strbuf_addf(sbuf, "Total reused space:%26"PRIdMPINT" bytes\n\n"
|
2009-03-03 03:27:01 +00:00
|
|
|
, total_swap_reused);
|
|
|
|
strbuf_addf(sbuf
|
2009-05-21 22:41:07 +00:00
|
|
|
, "Swap: searches: %10ld average search length: %3.1f\n"
|
|
|
|
"Free: searches: %10ld average search length: %3.1f\n"
|
2009-03-03 03:27:01 +00:00
|
|
|
, 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 )
|
|
|
|
);
|
2009-05-21 22:41:07 +00:00
|
|
|
strbuf_addf(sbuf, "Overhead: %"PRIdMPINT" blocks using %"
|
|
|
|
PRIdMPINT" bytes.\n",
|
|
|
|
num_swap_structs, num_swap_structs * sizeof(swap_block_t) );
|
2009-03-03 03:27:01 +00:00
|
|
|
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 <svp>.
|
|
|
|
* If <value> is -1, <svp> points indeed to a value block; other it is
|
|
|
|
* the index of the desired value and <svp> 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
|