#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "object.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, num_unswapped = 0;
mp_int total_bytes_swapped = 0, total_bytes_unswapped = 0;
mp_int num_swapfree = 0;
mp_int total_bytes_swapfree = 0;
mp_int swapfile_size = 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;
/*
* Free space is kept in a simple linked list. The size field is < 0
* for allocated blocks or > 0 for free blocks. swap_rover walks the
* list when allocating and freeing blocks. This is an attempt to
* reduce head movement in comparison to normal first fit algorithms.
*/
struct swap_block
{
struct swap_block *next;
mp_int size;
};
struct swap_block swap_list;
struct swap_block *swap_rover = 0; /* pointer to the current swap_block */
struct swap_block *swap_previous = &swap_list; /* one prior to swap_rover */
static mp_int current_offset; /* file offset corresponding to swap_rover */
/*
* 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;
}
#define SWAP_ABS(a) ((a)>0 ? (a) : (-a))
/*
* 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 from the beginning of the file.
*/
int swap_alloc(size)
mp_int size;
{
struct swap_block *mark,*last;
extern int malloc_privilege;
int save_privilege;
save_privilege = malloc_privilege;
malloc_privilege = MALLOC_SYSTEM;
num_swapped++;
total_bytes_swapped += size;
mark = swap_rover;
for (;;)
{
if (!swap_rover)
{
swap_rover = &swap_list;
swap_previous = 0;
current_offset = 0;
}
if (size <= swap_rover->size)
{
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;
}
/* split the block in two */
/* num_swapfree remains unchanged */
mark = (struct swap_block *) permanent_xalloc(sizeof(struct swap_block));
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;
}
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 */
{
last = swap_previous;
while (mark = last->next)
last = mark;
mark = (struct swap_block *) permanent_xalloc(sizeof(struct swap_block));
mark->next = 0;
last->next = mark;
mark->size = -size;
if (!swap_rover)
swap_rover = mark;
swapfile_size += size;
malloc_privilege = save_privilege;
return swapfile_size - size;
}
}
}
void swap_free(offset)
mp_int offset;
{
num_swapped--;
if (offset < current_offset)
{
swap_rover = swap_list.next;
swap_previous = &swap_list;
current_offset = 0;
}
while (current_offset < offset && swap_rover)
{
swap_previous = swap_rover;
current_offset += SWAP_ABS(swap_rover->size);
swap_rover = swap_rover->next;
}
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;
total_bytes_swapped -= 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;
pfree((char *)swap_rover);
num_swapfree--;
swap_rover = swap_previous->next;
}
}
void set_swapbuf(buf)
char *buf;
{
/* This space intentionally left blank */
}
/*
* 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;
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_unswapped -= prog->total_size;
num_unswapped--;
free_prog(prog, 0); /* Do not free the strings */
ob->prog = 0;
ob->flags |= O_SWAPPED;
return 1;
}
locate_out ((struct program *)prog);
write_buffer = (char *)prog;
write_size = prog->total_size;
swap_num = swap_alloc(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;
locate_in(prog);
return 0;
}
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_unswapped += ob->prog->total_size;
num_unswapped++;
total_prog_block_size += ob->prog->total_size;
total_num_prog_blocks += 1;
}
int remove_swap_file(ob)
struct object *ob;
{
#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");
total_bytes_unswapped -= ob->prog->total_size;
num_unswapped--;
swap_free(ob->swap_num);
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
}