/* (c) Copyright by Anders Chrigstroem 1993, All rights reserved */
/* Permission is granted to use this source code and any executables
* created from this source code as part of the CD Gamedriver as long
* as it is not used in any way whatsoever for monetary gain. */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "exec.h"
#include "mudstat.h"
#include "mapping.h"
#include <alloca.h>
#define SWAP_FILE "LP_SWAP.3"
#define BUFFER_DATA_SIZE 256
#define BUFFER_PTR_SIZE (BUFFER_DATA_SIZE / sizeof(unsigned int))
#define NUM_FREE 8
static struct buffer
{
unsigned int bufnum;
unsigned short offset;
unsigned short flags;
#define B_DIRTY 1
#define B_INUSE 2
union
{
char data[BUFFER_DATA_SIZE];
unsigned int addr[BUFFER_PTR_SIZE];
} u;
} free_blocks[NUM_FREE], zero;
static unsigned short cur_free;
static unsigned int maxblock = 0;
static int swap_fd;
static char swap_file[80];
void
init_swap()
{
char hn[80];
static int initialized = 0;
int i;
if (initialized)
return;
initialized = 1;
gethostname(hn, sizeof(hn));
sprintf(swap_file, "%s.%s.%d", SWAP_FILE, hn, getpid());
swap_fd = open(swap_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (swap_fd < 0)
fatal("Can't create swapfile.\n");
for (i = 0; i < NUM_FREE; i++)
free_blocks[i].flags = 0;
#ifdef HAS_PREAD
pwrite(swap_fd, zero.u.data, BUFFER_DATA_SIZE,
maxblock * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, maxblock * BUFFER_DATA_SIZE,
SEEK_SET);
write(swap_fd, zero.u.data, BUFFER_DATA_SIZE);
#endif
cur_free = 0;
free_blocks[0].flags |= B_INUSE;
free_blocks[0].u.addr[0] = 0;
free_blocks[0].offset = 1;
}
void
unlink_swap_file()
{
unlink(swap_file);
}
unsigned int
alloc_swap(void)
{
unsigned int ret;
if (free_blocks[cur_free].offset > 1) /* Blocks exists in free list */
ret = free_blocks[cur_free].u.addr[--free_blocks[cur_free].offset];
else if (free_blocks[cur_free].u.addr[0]) /* End of current block */
{
ret = free_blocks[cur_free].u.addr[0];
free_blocks[cur_free].flags &= ~B_INUSE;
cur_free = (cur_free + (NUM_FREE - 1)) % NUM_FREE;
if ((free_blocks[cur_free].flags & B_INUSE) == 0)
{
/* Read next block of free list */
#ifdef HAS_PREAD
pread(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE,
ret * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, ret * BUFFER_DATA_SIZE, SEEK_SET);
read(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE);
#endif
free_blocks[cur_free].flags |= B_INUSE;
}
free_blocks[cur_free].offset = BUFFER_PTR_SIZE;
}
else
{
/* End of blocks */
/* Extend the file */
maxblock++;
#ifdef HAS_PREAD
pwrite(swap_fd, zero.u.data, BUFFER_DATA_SIZE,
maxblock * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, maxblock * BUFFER_DATA_SIZE,
SEEK_SET);
write(swap_fd, zero.u.data, BUFFER_DATA_SIZE);
#endif
ret = maxblock;
}
return ret;
}
void
free_swap(unsigned int addr)
{
if (addr == 0)
return;
if (free_blocks[cur_free].offset >= BUFFER_PTR_SIZE)
{
free_blocks[cur_free].bufnum = addr;
cur_free = (cur_free + 1) % NUM_FREE;
if (free_blocks[cur_free].flags & B_INUSE)
{
#ifdef HAS_PREAD
pwrite(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE,
free_blocks[cur_free].bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, free_blocks[cur_free].bufnum * BUFFER_DATA_SIZE,
SEEK_SET);
write(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE);
#endif
}
free_blocks[cur_free].flags |= B_INUSE;
free_blocks[cur_free].offset = 0;
}
free_blocks[cur_free].u.addr[free_blocks[cur_free].offset++] = addr;
}
struct buffer *
start_read(unsigned int addr)
{
struct buffer *ret;
/* Allocate a buffer */
ret = (struct buffer *)xalloc(sizeof(struct buffer));
ret->flags = 0;
ret->bufnum = addr;
/* Read first block */
#ifdef HAS_PREAD
pread(swap_fd, ret->u.data, BUFFER_DATA_SIZE, addr * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, addr * BUFFER_DATA_SIZE, SEEK_SET);
read(swap_fd, ret->u.data, BUFFER_DATA_SIZE);
#endif
ret->offset = sizeof(unsigned int);
return ret;
}
int
swap_read(struct buffer *buf, char *dest, unsigned int size)
{
unsigned int csize, len;
char *cdest;
csize = size;
cdest = dest;
while (csize > (len = BUFFER_DATA_SIZE - buf->offset))
{
/* Empty buffer */
memcpy(cdest, buf->u.data + buf->offset, len);
cdest += len;
csize -= len;
buf->offset += len;
if (buf->u.addr[0] == 0) /* Return if end of blocks */
return size - csize;
/* Read next block */
buf->bufnum = buf->u.addr[0];
#ifdef HAS_PREAD
pread(swap_fd, buf->u.data, BUFFER_DATA_SIZE,
buf->bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET);
read(swap_fd, buf->u.data, BUFFER_DATA_SIZE);
#endif
buf->offset = sizeof(unsigned int);
}
/* Get the last needed from the buffer */
memcpy(cdest, buf->u.data + buf->offset, csize);
buf->offset += csize;
return size;
}
void
end_read(struct buffer *buf)
{
/* Free the buffer */
free((char *)buf);
}
struct buffer *
start_write(unsigned int *addrp)
{
struct buffer *ret;
/* Allocate a buffer */
ret = (struct buffer *)xalloc(sizeof(struct buffer));
if (*addrp == 0)
{
/* Allocate a new blocks */
*addrp = alloc_swap();
}
ret->bufnum = *addrp;
ret->flags = 0;
/* Read next pointer from first block */
#ifdef HAS_PREAD
pread(swap_fd, ret->u.data, sizeof(unsigned int),
ret->bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, ret->bufnum * BUFFER_DATA_SIZE, SEEK_SET);
read(swap_fd, ret->u.data, sizeof(unsigned int));
#endif
ret->offset = sizeof(unsigned int);
return ret;
}
int
swap_write(struct buffer *buf, char *dest, unsigned int size)
{
unsigned int csize, len;
char *cdest;
csize = size;
cdest = dest;
while (csize > (len = BUFFER_DATA_SIZE - buf->offset))
{
/* Fill the buffer with data */
memcpy(buf->u.data + buf->offset, cdest, len);
cdest += len;
csize -= len;
/* Allocate new blocks if we need to */
if (buf->u.addr[0] == 0)
buf->u.addr[0] = alloc_swap();
/* Flush the block to disk */
#ifdef HAS_PREAD
pwrite(swap_fd, buf->u.data, BUFFER_DATA_SIZE,
buf->bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET);
write(swap_fd, buf->u.data, BUFFER_DATA_SIZE);
#endif
/* Read in next pointer from next block */
buf->bufnum = buf->u.addr[0];
#ifdef HAS_PREAD
pread(swap_fd, buf->u.data, sizeof(unsigned int),
buf->bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET);
read(swap_fd, buf->u.data, sizeof(unsigned int));
#endif
buf->offset = sizeof(unsigned int);
}
/* Copy the remaining data to the buffer */
memcpy(buf->u.data + buf->offset, cdest, csize);
buf->offset += csize;
return size;
}
void
end_write(struct buffer *buf)
{
/* Truncate the block list */
free_swap(buf->u.addr[0]);
buf->u.addr[0] = 0;
/* Flush current buffer to disk */
#ifdef HAS_PREAD
pwrite(swap_fd, buf->u.data, buf->offset,
buf->bufnum * BUFFER_DATA_SIZE);
#else
lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET);
write(swap_fd, buf->u.data, buf->offset);
#endif
/* Free the buffer */
free((char *)buf);
return;
}
#define align(x) ( ((x) + (sizeof(void *)-1) ) & ~(sizeof(void *)-1) )
struct object *swap_ob = 0;
struct program *swap_prog = 0;
int max_swap_memory = 0x40000001;
int min_swap_memory = 0x40000000;
int min_swap_time = 0x40000000;
int max_swap_time = 0x40000001;
extern struct program *prog_list;
extern struct object *obj_list;
extern int current_time;
int used_memory;
extern int tot_alloc_object_size;
int total_lineno_swapped = 0;
int obj_swapped = 0;
int obj_bytes_swapped = 0;
int num_swapped;
int total_bytes_swapped;
int total_num_prog_blocks = 0, total_prog_block_size = 0;
int prog_code_size = 0, prog_func_size = 0, prog_var_size = 0,
prog_inherit_size = 0, prog_string_size = 0, prog_line_size = 0;
int program_bytes_swapped = 0, total_program_size = 0, programs_swapped = 0;
int last_address = 0, first_hole = 0, allocated_swap = 0, allocated_swap_blocks = 0, current_hole = 0;
int tot_alloc_variable_size = 0;
int swap_out_prog, swap_out_obj;
int swap_in_prog, swap_in_obj;
int swap_out = 0, swap_in = 0;
extern int d_flag;
int num_swapped_arrays, size_swapped_arrays;
int num_swapped_mappings, size_swapped_mappings;
int num_strings_swapped, size_strings_swapped;
extern struct svalue const0;
static void
swap_svalue(struct buffer *buf, struct svalue *arg)
{
switch(arg->type)
{
case T_OBJECT:
if (arg->u.ob->flags & O_DESTRUCTED)
free_svalue(arg);
case T_NUMBER:
case T_FLOAT:
arg->type |= T_LVALUE;
swap_write(buf, (char *)arg, sizeof(struct svalue));
*arg = const0;
break;
case T_STRING:
{
char *str;
int len;
str = arg->u.string;
len = arg->u.number = strlen(str) + 1;
swap_write(buf, (char *)arg, sizeof(struct svalue));
swap_write(buf, str, len);
num_strings_swapped++;
size_strings_swapped += len;
switch(arg->string_type)
{
case STRING_MALLOC:
free(str);
break;
case STRING_SHARED:
free_string(str);
break;
case STRING_CONSTANT:
break;
default:
fatal("Invalid variable value.\n");
}
total_bytes_swapped += len;
*arg = const0;
break;
}
case T_POINTER:
{
struct vector *v;
v = arg->u.vec;
if (v->ref != 1)
{
arg->type |= T_LVALUE;
swap_write(buf, (char *)arg, sizeof(struct svalue));
*arg = const0;
}
else
{
int i;
arg->u.number = v->size;
swap_write(buf, (char *)arg, sizeof(struct svalue));
for (i = 0; i < v->size; i++)
swap_svalue(buf, &v->item[i]);
*arg = const0;
num_swapped_arrays++;
size_swapped_arrays += sizeof(struct vector) +
sizeof(struct svalue) * v->size - 1;
total_bytes_swapped += sizeof(struct svalue) * v->size;
free_vector(v);
}
break;
}
case T_MAPPING:
{
struct mapping *m;
struct apair *pair;
int i, j;
m = arg->u.map;
if (m->ref != 1)
{
arg->type |= T_LVALUE;
swap_write(buf, (char *)arg, sizeof(struct svalue));
*arg = const0;
}
else
{
int size;
size = arg->u.number = m->card;
arg->string_type = m->size;
swap_write(buf, (char *)arg, sizeof(struct svalue));
for (i = j = 0; i < m->size; i++)
for(pair = m->pairs[i]; pair; pair = pair->next)
{
swap_svalue(buf, &pair->arg);
swap_svalue(buf, &pair->val);
j++;
}
if (j != size)
fatal("Wrong cardinality of mapping.\n");
num_swapped_mappings++;
size_swapped_mappings += sizeof(struct mapping) +
m->card * sizeof(struct apair) +
m->size * sizeof(struct apair *);
total_bytes_swapped += size * 2 * sizeof(struct svalue);
free_mapping(m);
*arg = const0;
}
break;
}
default:
fatal("Invalid variable type.\n");
}
}
static void
unswap_svalue(struct buffer *buf, struct svalue *arg)
{
swap_read(buf, (char *)arg, sizeof(struct svalue));
if (arg->type & T_LVALUE)
{
arg->type &= ~T_LVALUE;
if (arg->type == T_OBJECT && arg->u.ob->flags & O_DESTRUCTED)
free_svalue(arg);
if (!(arg->type &
(T_OBJECT | T_NUMBER | T_FLOAT | T_POINTER | T_MAPPING)))
fatal("Invalid variable lvalue.\n");
return;
}
switch(arg->type)
{
case T_STRING:
{
int len;
len = arg->u.number;
#ifdef ALWAYS_SHARE
arg->u.string = alloca(len);
#else
arg->u.string = xalloc(len);
#endif
swap_read(buf, (char *)arg->u.string, len);
#ifdef ALWAYS_SHARE
arg->string_type = STRING_SHARED;
arg->u.string = make_shared_string(arg->u.string);
#else
arg->string_type = STRING_MALLOC;
#endif
num_strings_swapped--;
size_strings_swapped -= len;
total_bytes_swapped -= len;
break;
}
case T_POINTER:
{
int size, i;
struct vector *v;
size = arg->u.number;
v = allocate_array(size);
for (i = 0; i < size; i++)
unswap_svalue(buf, &v->item[i]);
arg->u.vec = v;
num_swapped_arrays--;
size_swapped_arrays -= sizeof(struct vector) +
sizeof(struct svalue) * size - 1;
total_bytes_swapped -= sizeof(struct svalue) * v->size;
break;
}
case T_MAPPING:
{
int size, i;
struct svalue marg, *mval;
size = arg->u.number;
arg->u.map = allocate_map(size);
for (i = 0; i < size; i++)
{
unswap_svalue(buf, &marg);
mval = get_map_lvalue(arg->u.map, &marg, 1);
unswap_svalue(buf, mval);
free_svalue(&marg);
}
num_swapped_mappings--;
size_swapped_mappings -= sizeof(struct mapping) +
size * sizeof(struct apair) +
arg->string_type * sizeof(struct apair *);
total_bytes_swapped -= size * 2 * sizeof(struct svalue);
break;
}
default:
fatal("Invalid variable value.\n");
}
}
/*
* Swap out an object. Only the program is swapped, not the struct object.
*
*/
int
swap_object(struct object *ob)
{
int size;
int num_vars, i;
struct buffer *buf;
if (ob->flags & (O_DESTRUCTED | O_SWAPPED))
return 0;
if (d_flag & DEBUG_SWAP)
{
fprintf(stderr, "Swap object %s (ref %d) from 0x%x\n",
ob->name, ob->ref, ob->variables);
}
if (!ob->variables)
{
return 0;
}
ob->variables--;
num_vars = ob->prog->inherit[ob->prog->num_inherited - 1].
variable_index_offset + ob->prog->num_variables + 1;
size = num_vars * sizeof(struct svalue);
buf = start_write(&ob->swap_num);
if (ob->swap_num == -1)
{
ob->variables++;
end_write(buf);
return 0;
}
for (i = 0; i < num_vars; i++)
swap_svalue(buf, &ob->variables[i]);
end_write(buf);
free((char *)ob->variables);
ob->variables = (struct svalue *)-1;
obj_bytes_swapped += size;
obj_swapped++;
tot_alloc_variable_size -= size;
total_bytes_swapped += size;
num_swapped++;
swap_out_obj++;
ob->flags |= O_SWAPPED;
return 1;
}
void
load_ob_from_swap(struct object *ob)
{
int i, size;
int num_var;
struct buffer *buf;
if (!(ob->flags & O_SWAPPED))
fatal("Swapping in not swapped out object!\n");
if (d_flag & DEBUG_SWAP)
{
fprintf(stderr,"Unswap object %s (ref %d) from 0x%x\n", ob->name, ob->ref, ob->variables);
}
size = (num_var = ob->prog->inherit[ob->prog->num_inherited - 1].variable_index_offset +
ob->prog->num_variables + 1) * sizeof(struct svalue);
ob->variables = (struct svalue *)xalloc(size);
buf = start_read(ob->swap_num);
for (i = 0; i < num_var; i++)
unswap_svalue(buf, &ob->variables[i]);
end_read(buf);
ob->flags &= ~O_SWAPPED;
obj_bytes_swapped -= size;
obj_swapped--;
tot_alloc_variable_size += size;
swap_in_obj++;
total_bytes_swapped -= size;
num_swapped--;
ob->variables++;
}
static int
swap_segment(char *hdr, struct segment_desc *seg)
{
struct section_desc *sect;
char *block = *(char **)(hdr + seg->ptr_offset);
int i;
if (*(int *)(hdr + seg->swap_idx_offset) == 0)
{
struct buffer *buf;
buf = start_write((int *)(hdr + seg->swap_idx_offset));
swap_write(buf, block, *(int *)(hdr + seg->size_offset));
end_write(buf);
}
if (*(int *)(hdr + seg->swap_idx_offset) == -1)
fatal("error while swapping segment.\n");
for (sect = seg->sections, i = 0; sect->section != -1; sect++, i++)
if (sect->ptr_offset != -1)
*(long *)(hdr + sect->ptr_offset) =
*(char **)(hdr + sect->ptr_offset) - block;
free(block);
return *(int *)(hdr + seg->size_offset);
}
static int
unswap_segment(char *hdr, struct segment_desc *seg)
{
struct section_desc *sect;
char *block;
int i;
struct buffer *buf;
block = xalloc(*(int *)(hdr + seg->size_offset));
buf = start_read(*(int *)(hdr + seg->swap_idx_offset));
swap_read(buf, block, *(int *)(hdr + seg->size_offset));
end_read(buf);
for (sect = seg->sections, i = 0; sect->section != -1; sect++, i++)
if (sect->ptr_offset != -1)
*(char **)(hdr + sect->ptr_offset) =
block + *(long *)(hdr + sect->ptr_offset);
*(char **)(hdr + seg->ptr_offset) = block;
return *(int *)(hdr + seg->size_offset);
}
/*
* Swap out lineno info for a program to the swap file
* The linenoinfo is separated from the programblock thereby saving memory.
* This is called directly after compilation from epilog() in postlang.y
*/
int
swap_lineno(struct program *prog)
{
int size;
if (d_flag & DEBUG_SWAP) /* marion */
debug_message("Swap lineno for %s (ref %d)\n", prog->name);
if (!prog->line_numbers)
return 1;
size = swap_segment((char *)prog, segm_desc + S_DBG);
total_bytes_swapped += size;
total_lineno_swapped += size;
num_swapped++;
return 1;
}
/*
Load the lineno info, this is only done when a runtimeerror occurs
*/
void
load_lineno_from_swap(struct program *prog)
{
int size;
if (prog->line_numbers)
return;
if (prog->swap_lineno_index == 0)
fatal("Loading not swapped linenoinfo.\n");
size = unswap_segment((char *)prog, segm_desc + S_DBG);
total_lineno_swapped -= size;
total_bytes_swapped -= size;
num_swapped--;
}
static int
swap_program(struct program *prog)
{
int size;
if (prog->line_numbers)
swap_lineno(prog);
if (prog->program == (char *)0)
return 0;
if (d_flag & DEBUG_SWAP) { /* marion */
debug_message("Swap program %s (ref %d)\n", prog->name, prog->ref);
}
size = swap_segment((char *)prog, segm_desc + S_EXEC);
total_program_size -= size;
program_bytes_swapped += size;
programs_swapped++;
swap_out_prog++;
total_bytes_swapped += size;
num_swapped++;
return 1;
}
void
load_prog_from_swap(struct program *prog)
{
int size;
if (prog->program != (char *)0 || prog->swap_num == -1)
return;
if (d_flag & DEBUG_SWAP) { /* marion */
debug_message("Unswap program %s (ref %d)\n", prog->name, prog->ref);
}
size = unswap_segment((char *)prog, segm_desc + S_EXEC);
swap_in_prog++;
total_program_size += size;
program_bytes_swapped -= size;
programs_swapped--;
total_bytes_swapped -= size;
num_swapped--;
}
void
remove_ob_from_swap(struct object *ob)
{
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
if (ob->swap_num > 0)
{
free_swap(ob->swap_num);
ob->swap_num = 0;
}
return;
}
void
remove_prog_from_swap(struct program *prog)
{
if (prog->swap_num > 0)
{
if (prog->program == (char *)0)
load_prog_from_swap(prog);
free_swap(prog->swap_num);
prog->swap_num = 0;
}
if (prog->swap_lineno_index > 0)
{
free_swap(prog->swap_lineno_index);
prog->swap_lineno_index = 0;
}
return;
}
void
try_to_swap(volatile int *interupted)
{
int swap_time;
if (swap_ob && swap_prog && used_memory >= min_swap_memory)
while (swap_ob != obj_list && swap_prog != prog_list)
{
swap_time = (used_memory < max_swap_memory) ?
max_swap_time : min_swap_time;
if (swap_ob->time_of_ref > swap_prog->time_of_ref)
{
if (current_time - swap_prog->time_of_ref < swap_time)
break;
swap_program(swap_prog);
swap_prog = swap_prog->prev_all;
}
else
{
if (current_time - swap_ob->time_of_ref < swap_time)
break;
swap_object(swap_ob);
swap_ob = swap_ob->prev_all;
}
if (*interupted)
return;
}
}