#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "config.h"
#include "exec.h"
#ifdef AMIGA
#include "hosts/amiga/ixfile.h"
#include "hosts/amiga/socket.h"
#endif
/*
* Swap out programs from objects.
* Every block, free or allocated, is at least of size sizeof(struct program) .
* The start can be interpreted as a struct program that habe at least a valid
* total_size and ref field. ref == 0 marks a free block.
*/
/* How many times should swapbuf be refilled when trying to swap an object
* before we conclude that it's better to use fresh space?
*/
#define REFILL_MAX (2)
mp_int num_swapped = 0;
mp_int total_bytes_swapped = 0;
mp_int num_swapfree = 0;
mp_int total_bytes_swapfree = 0;
mp_int swapfile_size = 0;
static mp_int swapfile_scannable = 0;
/* Number of bytes that have been reused from freed swap prog blocks */
mp_int total_swap_reused = 0;
char file_name[100];
FILE *swap_file = (FILE *) 0; /* The swap file is opened once */
mp_int total_num_prog_blocks, total_prog_block_size;
extern int d_flag;
static char *swapbuf = (char *)0;
/* Pointer to a buffer that is used to scan for free blocks.
* The first sizeof(struct program) bytes are used to hold
* the end of the previous block.
*/
static mp_int swapbuf_offs = 0;
/* Offset into swapfile to the block that is loaded into
* swapbuf + sizeof(struct program)
* It is a multiple of SCAN_SWAP_BUFSIZE, in order to map
* efficiently to disk blocks.
*/
static mp_int free_swap_offs = sizeof(struct program);
/* Rover offset used to scan for free blocks in swapbuf.
* It is relative to the start of swapbuf, thus, you have
* swapbuff_offs + free_swap_offs - sizeof(struct program)
* as the corresponding position in the swapfile.
*/
static mp_int free_swap_start;
/* Offset into swap file to current run of free blocks */
static mp_int free_swap_size = 0;
/* Total size of free blocks in current run */
static mp_int free_swap_num;
/* Number of free blocks in current run */
static int free_swap_terminated = 1;
/* The run of free blocks has been completely scanned */
#ifndef BUG_FREE
static mp_int debug_free_swap_offs = -1;
#endif
/*
* marion - adjust pointers for swap out and later relocate on swap in
* program
* line_numbers
* functions
* strings
* variable_names
* inherit
* argument_types
* type_start
*/
int
locate_out (prog) struct program *prog; {
char *p = 0; /* keep cc happy */
if (!prog) return 0;
if (d_flag > 1) {
debug_message ("locate_out: %lX %lX %lX %lX %lX %lX %lX %lX\n",
prog->program, prog->line_numbers, prog->functions,
prog->strings, prog->variable_names, prog->inherit,
prog->argument_types, prog->type_start);
}
prog->program = &p[prog->program - (char *)prog];
prog->line_numbers = (char *)
&p[(char *)prog->line_numbers - (char *)prog];
prog->functions = (uint32 *)
&p[(char *)prog->functions - (char *)prog];
prog->function_names= (unsigned short *)
&p[(char *)prog->function_names - (char *)prog];
prog->strings = (char **)
&p[(char *)prog->strings - (char *)prog];
prog->variable_names= (struct variable *)
&p[(char *)prog->variable_names - (char *)prog];
prog->inherit = (struct inherit *)
&p[(char *)prog->inherit - (char *)prog];
if (prog->type_start) {
prog->argument_types = (unsigned short *)
&p[(char *)prog->argument_types - (char *)prog];
prog->type_start = (unsigned short *)
&p[(char *)prog->type_start - (char *)prog];
}
return 1;
}
/*
* marion - relocate pointers after swap in
* program
* line_numbers
* functions
* strings
* variable_names
* inherit
* argument_types
* type_start
*/
int
locate_in (prog) struct program *prog; {
extern int32 current_id_number;
char *p = (char *)prog;
if (!prog) return 0;
prog->id_number =
++current_id_number ? current_id_number : renumber_programs();
prog->program = &p[prog->program - (char *)0];
prog->line_numbers = (char *)
&p[(char *)prog->line_numbers - (char *)0];
prog->functions = (uint32 *)
&p[(char *)prog->functions - (char *)0];
prog->function_names= (unsigned short *)
&p[(char *)prog->function_names - (char *)0];
prog->strings = (char **)
&p[(char *)prog->strings - (char *)0];
prog->variable_names= (struct variable *)
&p[(char *)prog->variable_names - (char *)0];
prog->inherit = (struct inherit *)
&p[(char *)prog->inherit - (char *)0];
if (prog->type_start) {
prog->argument_types = (unsigned short *)
&p[(char *)prog->argument_types - (char *)0];
prog->type_start = (unsigned short *)
&p[(char *)prog->type_start - (char *)0];
}
if (d_flag > 1) {
debug_message ("locate_in: %lX %lX %lX %lX %lX %lX %lX\n",
prog->program, prog->line_numbers, prog->functions,
prog->strings, prog->variable_names, prog->inherit,
prog->argument_types, prog->type_start);
}
return 1;
}
/* The argument refilled is a pointer to a counter which holds the number
* of refills. It is usually initialised to Zero by thye caller before the
* first call. Well, set_swapbuf() sets it to -1 to indicate that the buffer
* contents are invalid.
* Don't call if there is no swapbuf or
* swapfile_scannable < sizeof(struct program)
* Returns success status, which only depends on *refilled.
*/
int refill_swapbuf(refilled)
int *refilled;
{
mp_int offs = free_swap_offs;
/* extra: Extra bytes we have to read. Is zero when we can reuse *
* the old end of the buffer, sizeof(struct program) if we can't. */
int extra = 0;
#ifndef BUG_FREE
if (offs & (sizeof(p_int) - 1))
fatal("Bad alignment of free_swap_offs: %x\n", (p_int)offs);
debug_free_swap_offs = offs;
#endif
if (++*refilled >= REFILL_MAX)
return 0;
if (swapbuf_offs + offs - sizeof(struct program) >= swapfile_scannable) {
swapbuf_offs = 0;
free_swap_start = 0;
free_swap_size = 0;
free_swap_num = 0;
offs = sizeof(struct program);
free_swap_terminated = 1;
} else {
while (offs >= SCAN_SWAP_BUFSIZE) {
offs -= SCAN_SWAP_BUFSIZE;
}
swapbuf_offs += free_swap_offs - offs;
if (offs < sizeof(struct program) ) {
/* *refilled was incremented above, so test for 0 */
if (*refilled && free_swap_offs - offs == SCAN_SWAP_BUFSIZE) {
memcpy(
swapbuf,
swapbuf+SCAN_SWAP_BUFSIZE,
sizeof(struct program)
);
} else {
extra = sizeof(struct program);
}
}
}
/* This test is good not only for debugging, but also when the disk is on
* fire, to stop subsequent damage.
*/
if (swapfile_scannable < swapbuf_offs)
fatal("Scan attempt to reuse space beyond the end of the swapfile.\n");
if (fseek(swap_file, swapbuf_offs - extra, 0) == -1) {
fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
errno, swapbuf_offs - extra);
}
if (fread(
swapbuf + sizeof(struct program) - extra,
swapfile_scannable - swapbuf_offs < SCAN_SWAP_BUFSIZE ?
swapfile_scannable - swapbuf_offs + extra :
SCAN_SWAP_BUFSIZE + extra,
1,
swap_file
) != 1) {
fatal("Couldn't read the swap file, errno %d, offset %d, size %d.\n",
errno, swapbuf_offs - extra,
swapfile_scannable - swapbuf_offs < SCAN_SWAP_BUFSIZE ?
swapfile_scannable - swapbuf_offs + extra :
SCAN_SWAP_BUFSIZE + extra);
}
if (swapfile_scannable - swapbuf_offs + sizeof(struct program) <=
SCAN_SWAP_BUFSIZE)
{
/* The last bytes of the buffer are invalid. Their number is in the
* range sizeof(struct program) .. SCAN_SWAP_BUFSIZE .
* We fake an allocated block to mask them.
* It is possible that the swapfile grows later beyond the area
* shadowed by the buffer, or has already grown, thus
* swapfile_scannable does not only save i/o, but is also necessary
* for consistence.
*/
struct program *fake_block =
(struct program *)(swapbuf+swapfile_scannable-swapbuf_offs) + 1;
fake_block->ref = 1;
fake_block->total_size = SCAN_SWAP_BUFSIZE;
}
free_swap_offs = offs;
return 1;
}
void set_swapbuf(buf)
char *buf;
{
int refilled = -1;
swapbuf = buf;
if (buf && swapfile_size) {
swapfile_scannable = swapfile_size;
refill_swapbuf(&refilled);
}
}
/*
* Swap out an object. Only the program is swapped, not the 'struct object'.
*
* marion - the swap seems to corrupt the function table
*/
int swap(ob)
struct object *ob;
{
struct program *prog;
mp_int swap_num;
char *write_buffer;
int write_size;
int refilled;
if (ob->flags & O_DESTRUCTED)
return 0;
if (d_flag > 1) { /* marion */
debug_message("Swap object %s (ref %d)\n", ob->name, ob->ref);
}
if (swap_file == 0) {
#ifndef MSDOS
char host[50];
gethostname(host, sizeof host);
sprintf(file_name, "%s.%s", SWAP_FILE, host);
swap_file = fopen(file_name, "w+b");
#else
swap_file = fopen(strcpy(file_name,"LPMUD.SWAP"),"w+b");
#endif
/* Leave this file pointer open ! */
if (swap_file == 0)
return 0;
}
if ( !(prog = ob->prog) )
{
fprintf(stderr, "warning:no program in object %s, don't swap it\n",
ob->name);
/* It's no good freeing a NULL pointer */
return 0;
}
if ((ob->flags & O_HEART_BEAT) || (ob->flags & O_CLONE)) {
if (d_flag > 1) {
debug_message (" object not swapped - heart beat or cloned.\n");
}
return 0;
}
if (prog->ref > 1 || ob->interactive) {
if (d_flag > 1) {
debug_message (" object not swapped - inherited or interactive.\n");
}
return 0;
}
/*
* Has this object already been swapped, and read in again ?
* Then it is very easy to swap it out again.
*/
if (ob->swap_num >= 0) {
total_bytes_swapped += prog->total_size;
free_prog(prog, 0); /* Do not free the strings */
ob->prog = 0;
ob->flags |= O_SWAPPED;
num_swapped++;
return 1;
}
#ifndef BUG_FREE
if (free_swap_offs & (sizeof(p_int) - 1))
fatal("Bad alignment of free_swap_offs: %x\n", (p_int)free_swap_offs);
debug_free_swap_offs = free_swap_offs;
#endif
refilled = 0;
if (!swapbuf || total_bytes_swapfree < prog->total_size)
refilled = REFILL_MAX + 1; /* Don't refill */
while (
refilled <= REFILL_MAX &&
free_swap_size != prog->total_size &&
free_swap_size < prog->total_size + sizeof(struct program)
) {
if (free_swap_terminated) {
while (
(free_swap_offs < SCAN_SWAP_BUFSIZE || refill_swapbuf(&refilled)) &&
((struct program *)(swapbuf+free_swap_offs))->ref )
{
free_swap_offs +=
((struct program *)(swapbuf+free_swap_offs))->total_size;
#ifndef BUG_FREE
if (free_swap_offs & (sizeof(p_int) - 1))
fatal(
"Bad alignment of free_swap_offs: %x\n",
(p_int)free_swap_offs
);
#endif
}
free_swap_terminated = 0;
free_swap_start =
swapbuf_offs + free_swap_offs - sizeof(struct program);
/* free_swap_start might point now beyond the end of the swapfile.
* This is harmless as long as free_swap_size == 0. It will be
* fixed by refill_swapbuf(), unless refilled is exhausted.
*/
free_swap_size = 0;
free_swap_num = 0;
}
while (free_swap_offs < SCAN_SWAP_BUFSIZE || refill_swapbuf(&refilled) )
{
int size;
if ( ((struct program *)(swapbuf+free_swap_offs))->ref ) {
free_swap_terminated = 1;
#ifndef BUG_FREE
size = ((struct program *)(swapbuf+free_swap_offs))->total_size;
if (size >
swapfile_scannable + SCAN_SWAP_BUFSIZE -
(swapbuf_offs + free_swap_offs - sizeof(struct program)) ||
size & (sizeof(p_int) - 1))
fatal("Bad allocated block size: %x\n", size);
#endif
break;
}
size = ((struct program *)(swapbuf+free_swap_offs))->total_size;
free_swap_size += size;
free_swap_num++;
free_swap_offs += size;
}
#ifndef BUG_FREE
/* These extra tests allow to notice bugs earlier, thus giving
* 'better' cores.
*/
if (swapfile_scannable < free_swap_start + free_swap_size &&
free_swap_size)
{
fatal("Attempt to reuse space beyond the end of the swapfile.\n");
}
#endif
}
/* relocate the internal pointers */
locate_out ((struct program *)prog);
write_buffer = (char *)prog;
write_size = prog->total_size;
if (free_swap_size == write_size || (
free_swap_size >= write_size + sizeof(struct program) && (
write_buffer = alloca(write_size+sizeof(struct program)),
memcpy(write_buffer, (char*)prog, write_size),
((struct program *)((char*)write_buffer+write_size))->ref = 0,
((struct program *)((char*)write_buffer+write_size))->total_size =
free_swap_size - write_size,
write_size += sizeof(struct program),
num_swapfree++,
MY_TRUE)
) ) {
swap_num = free_swap_start;
free_swap_start += prog->total_size;
free_swap_size -= prog->total_size;
num_swapfree -= free_swap_num;
total_bytes_swapfree -= prog->total_size;
total_swap_reused += prog->total_size;
free_swap_num = 1;
/* This test is good not only for debugging, but also when the disk is
* on fire, to stop subsequent damage.
*/
if (swapfile_scannable <= swap_num)
fatal("Attempt to reuse space beyond the end of the swapfile.\n");
} else {
swap_num = swapfile_size;
swapfile_size += write_size;
}
if (fseek(swap_file, swap_num, 0) == -1) {
fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
errno, swap_num);
}
if (swap_num != ftell(swap_file)) fatal("seek failure\n");
/*
* marion - it is more efficient to write one item the size of the
* program to the file than many items of size one. besides, it's
* much more reasonable, as the fwrite only fails for the whole
* block and not for a part of it.
*/
ob->swap_num = swap_num;
if (fwrite(write_buffer, write_size, 1, swap_file) != 1) {
debug_message("I/O error in swap.\n");
ob->swap_num = -1;
return 0;
}
total_bytes_swapped += prog->total_size;
num_swapped++;
free_prog(prog, 0); /* Don't free the shared strings */
ob->prog = 0;
ob->flags |= O_SWAPPED;
return 1;
}
void load_ob_from_swap(ob)
struct object *ob;
{
extern int errno;
struct program tmp_prog;
#ifndef BUG_FREE
if (ob->swap_num == -1)
fatal("Loading not swapped object.\n");
#endif
/* This test is good not only for debugging, but also when the disk is on
* fire, to stop subsequent damage.
*/
if (swapfile_size <= ob->swap_num)
fatal("Attempt to swap in from beyond the end of the swapfile.\n");
if (fseek(swap_file, ob->swap_num, 0) == -1)
fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
errno, ob->swap_num);
if (d_flag > 1) { /* marion */
debug_message("Unswap object %s (ref %d)\n", ob->name, ob->ref);
}
/*
* The size of the program is unkown, so read first part to
* find out.
*
* marion - again, the read in a block is more efficient
*/
if (fread((char *)&tmp_prog, sizeof tmp_prog, 1, swap_file) != 1) {
fatal("Couldn't read the swap file.\n");
}
ob->prog = (struct program *)xalloc(tmp_prog.total_size);
memcpy((char *)ob->prog, (char *)&tmp_prog, sizeof tmp_prog);
fread((char *)ob->prog + sizeof tmp_prog,
tmp_prog.total_size - sizeof tmp_prog, 1, swap_file);
/*
* to be relocated:
* program
* line_numbers
* functions
* strings
* variable_names
* inherit
* argument_types
* type_start
* to be replaced: id_number
*/
locate_in (ob->prog); /* relocate the internal pointers */
/* The reference count will already be 1 ! */
ob->flags &= ~O_SWAPPED;
if (!(ob->flags & O_DESTRUCTED) && function_exists("clean_up",ob)) {
ob->flags |= O_WILL_CLEAN_UP;
}
total_bytes_swapped -= ob->prog->total_size;
num_swapped--;
total_prog_block_size += ob->prog->total_size;
total_num_prog_blocks += 1;
}
int remove_swap_file(ob)
struct object *ob;
{
static p_int null_ref = 0;
#ifdef DEBUG
if (ob->swap_num == -1)
fatal("removing non-existant swap file.\n");
#endif
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
#ifndef BUG_FREE
if (!ob->prog) fatal("removing swap file whith no program in object\n");
#endif
/* This test is good not only for debugging, but also when the
* processor is on fire, to stop subsequent damage.
*/
if (swapfile_size <= ob->swap_num)
fatal("Attempt to remove swap entry beyond the end of the swapfile.\n");
if (fseek(
swap_file,
ob->swap_num +
((char *)&((struct program *)0)->ref - (char *)((struct program *)0)),
0
) == -1)
fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
errno, ob->swap_num);
if (fwrite((char *)&null_ref, sizeof null_ref, 1, swap_file) != 1) {
debug_message("I/O error in swap.\n");
return 0;
}
total_bytes_swapfree += ob->prog->total_size;
num_swapfree++;
ob->swap_num = -1;
return 1;
}
/*
* This one is called at shutdown. Remove the swap file.
*/
void unlink_swap_file() {
if (swap_file == 0)
return;
#ifndef MSDOS
unlink(file_name);
fclose(swap_file);
#else
fclose(swap_file);
unlink(file_name);
#endif
}