#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 }