#include <setjmp.h> #include <memory.h> #include <string.h> #include "lint.h" #include "interpret.h" #include "object.h" /* * This file implements delayed calls of functions. * Static functions can not be called this way. * * Allocate the structures several in one chunk, to get rid of malloc * overhead. */ #define CHUNK_SIZE 20 extern char *xalloc(), *string_copy(); extern jmp_buf error_recovery_context; extern int error_recovery_context_exists; struct call { int delta; char *function; struct object *ob; struct svalue v; struct call *next; struct object *command_giver; }; static struct call *call_list, *call_list_free; static int num_call; /* * Free a call out structure. */ static void free_call(cop) struct call *cop; { free_svalue(&cop->v); cop->next = call_list_free; free(cop->function); cop->function = 0; free_object(cop->ob, "free_call"); if (cop->command_giver) free_object(cop->command_giver, "free_call"); cop->ob = 0; call_list_free = cop; } /* * Setup a new call out. */ void new_call_out(ob, fun, delay, arg) struct object *ob; char *fun; int delay; struct svalue *arg; { struct call *cop, **copp; if (delay < 1) delay = 1; if (!call_list_free) { int i; call_list_free = (struct call *)xalloc(CHUNK_SIZE * sizeof (struct call)); for (i=0; i<CHUNK_SIZE - 1; i++) call_list_free[i].next = &call_list_free[i+1]; call_list_free[CHUNK_SIZE-1].next = 0; num_call += CHUNK_SIZE; } cop = call_list_free; call_list_free = call_list_free->next; cop->function = string_copy(fun); cop->command_giver = command_giver; /* save current player context */ if (command_giver) add_ref(command_giver, "new_call_out"); /* Bump its ref */ cop->ob = ob; add_ref(ob, "call_out"); cop->v.type = T_NUMBER; cop->v.u.number = 0; if (arg) assign_svalue(&cop->v, arg); for (copp = &call_list; *copp; copp = &(*copp)->next) { if ((*copp)->delta >= delay) { (*copp)->delta -= delay; cop->delta = delay; cop->next = *copp; *copp = cop; return; } delay -= (*copp)->delta; } *copp = cop; cop->delta = delay; cop->next = 0; } /* * See if there are any call outs to be called. Set the 'command_giver' * if it is a living object. Check for shadowing objects, which may also * be living objects. */ void call_out() { struct call *cop; jmp_buf save_error_recovery_context; int save_rec_exists; struct object *save_command_giver; extern struct object *command_giver; extern struct object *current_interactive; extern int current_time; static int last_time; if (call_list == 0) { last_time = current_time; return; } if (last_time == 0) last_time = current_time; save_command_giver = command_giver; current_interactive = 0; call_list->delta -= current_time - last_time; last_time = current_time; memcpy((char *) save_error_recovery_context, (char *) error_recovery_context, sizeof error_recovery_context); save_rec_exists = error_recovery_context_exists; error_recovery_context_exists = 1; while (call_list && call_list->delta <= 0) { /* * Move the first call_out out of the chain. */ cop = call_list; call_list = call_list->next; /* * A special case: * If a lot of time has passed, so that current call out was missed, * then it will have a negative delta. This negative delta implies * that the next call out in the list has to be adjusted. */ if (call_list && cop->delta < 0) call_list->delta += cop->delta; if (!(cop->ob->flags & O_DESTRUCTED)) { if (setjmp(error_recovery_context)) { extern void clear_state(); clear_state(); debug_message("Error in call out.\n"); } else { struct svalue v; struct object *ob; ob = cop->ob; while(ob->shadowing) ob = ob->shadowing; command_giver = 0; if (cop->command_giver && !(cop->command_giver->flags & O_DESTRUCTED)) { command_giver = cop->command_giver; } else if (ob->flags & O_ENABLE_COMMANDS) { command_giver = ob; } v.type = cop->v.type; v.u = cop->v.u; v.string_type = cop->v.string_type; /* Not always used */ if (v.type == T_OBJECT && (v.u.ob->flags & O_DESTRUCTED)) { v.type = T_NUMBER; v.u.number = 0; } push_svalue(&v); (void)apply(cop->function, cop->ob, 1); } } free_call(cop); } memcpy((char *) error_recovery_context, (char *) save_error_recovery_context, sizeof error_recovery_context); error_recovery_context_exists = save_rec_exists; command_giver = save_command_giver; } /* * Throw away a call out. First call to this function is discarded. * The time left until execution is returned. * -1 is returned if no call out pending. */ int remove_call_out(ob, fun) struct object *ob; char *fun; { struct call **copp, *cop; int delay = 0; for (copp = &call_list; *copp; copp = &(*copp)->next) { delay += (*copp)->delta; if ((*copp)->ob == ob && strcmp((*copp)->function, fun) == 0) { cop = *copp; if (cop->next) cop->next->delta += cop->delta; *copp = cop->next; free_call(cop); return delay; } } return -1; } int find_call_out(ob, fun) struct object *ob; char *fun; { struct call **copp; int delay = 0; for (copp = &call_list; *copp; copp = &(*copp)->next) { delay += (*copp)->delta; if ((*copp)->ob == ob && strcmp((*copp)->function, fun) == 0) { return delay; } } return -1; } int print_call_out_usage(verbose) int verbose; { int i; struct call *cop; for (i=0, cop = call_list; cop; cop = cop->next) i++; if (verbose) { add_message("\nCall out information:\n"); add_message("---------------------\n"); add_message("Number of allocated call outs: %8d, %8d bytes\n", num_call, num_call * sizeof (struct call)); add_message("Current length: %d\n", i); } else { add_message("call out:\t\t\t%8d %8d (current length %d)\n", num_call, num_call * sizeof (struct call), i); } return num_call * sizeof (struct call); } #ifdef DEBUG void count_ref_from_call_outs() { struct call *cop; for (cop = call_list; cop; cop = cop->next) { switch(cop->v.type) { case T_POINTER: cop->v.u.vec->extra_ref++; break; case T_OBJECT: cop->v.u.ob->extra_ref++; break; } cop->ob->extra_ref++; } } #endif /* * Construct an array of all pending call_outs. Every item in the array * consists of 4 items (but only if the object not is destructed): * 0: The object. * 1: The function (string). * 2: The delay. * 3: The argument. */ struct vector *get_all_call_outs() { int i, next_time; struct call *cop; struct vector *v; for (i=0, cop = call_list; cop; i++, cop = cop->next) ; v = allocate_array(i); next_time = 0; /* * Take for granted that all items in an array are initialized to * number 0. */ for (i=0, cop = call_list; cop; i++, cop = cop->next) { struct vector *vv; next_time += cop->delta; if (cop->ob->flags & O_DESTRUCTED) continue; vv = allocate_array(4); vv->item[0].type = T_OBJECT; vv->item[0].u.ob = cop->ob; add_ref(cop->ob, "get_all_call_outs"); vv->item[1].type = T_STRING; vv->item[1].string_type = STRING_SHARED; vv->item[1].u.string = make_shared_string(cop->function); vv->item[2].u.number = next_time; assign_svalue_no_free(&vv->item[3], &cop->v); v->item[i].type = T_POINTER; v->item[i].u.vec = vv; /* Ref count is already 1 */ } return v; }