#include <setjmp.h>
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "wiz_list.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.
*/
/*
* Modified to use absolute times, when storing call outs. The original
* implementation used a complex system of relative times (delta times)
* that caused a lot of overhead and some hard to trace bugs.
*
* Modifications made by: Mika Liljeberg (liljeberg@hylk.helsinki.fi)
* Modification date: 8.12.1990
*/
#define CHUNK_SIZE 20
extern char *xalloc(), *string_copy();
extern jmp_buf error_recovery_context;
extern int eval_cost, current_time, call_depth;
extern int error_recovery_context_exists;
struct call {
int abstime; /* Why muck about with deltas? */
char *function;
struct object *ob;
struct svalue v;
struct call *next;
};
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);
free(cop->function);
cop->function = 0;
/* Add it into the free list */
cop->next = call_list_free;
call_list_free = cop;
}
/*
* Setup a new call out.
*/
void new_call_out(ob, fun, call_time, arg)
struct object *ob;
char *fun;
int call_time;
struct value *arg;
{
struct call *cop, **copp;
struct value *aqwer;
call_time += current_time;
if (call_list_free == 0) {
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->ob = ob;
cop->abstime = call_time;
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)->abstime <= call_time;
copp = &(*copp)->next) ;
cop->next = *copp;
*copp = cop;
}
/*
* See if there are any call outs to be called.
*/
void call_out() {
struct value v;
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_object;
struct object *save_current_object;
save_command_giver = command_giver;
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->abstime <= current_time) {
/*
* Move the first call_out out of the chain.
*/
cop = call_list;
call_list = call_list->next;
if (!cop->ob->destructed) {
/* We have to catch an error here, locally.
* It is not good if the error is catched globally, as the current
* call out wouldn't be removed.
*/
if (setjmp(error_recovery_context)) {
extern void clear_state();
clear_state();
debug_message("Error in call out.\n");
} else {
/* struct value v;*/
extern struct value const0;
if (cop->ob->enable_commands)
command_giver = cop->ob;
else
command_giver = 0;
v.type = cop->v.type;
v.u = cop->v.u;
if (v.type == T_OBJECT && v.u.ob->destructed) {
v.type = T_NUMBER;
v.u.number = 0;
}
if(cop->ob->wl)
cop->ob->wl->call_outs++;
save_current_object = current_object;
current_object = cop->ob;
eval_cost = 0;
call_depth = 0;
apply(cop->function, cop->ob, &v);
current_object = save_current_object;
}
}
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;
for (copp = &call_list; *copp; copp = &(*copp)->next)
if ((*copp)->ob == ob && strcmp((*copp)->function, fun) == 0) {
cop = *copp;
*copp = cop->next;
delay = cop->abstime - current_time;
free_call(cop);
return delay;
}
return -1;
}
int find_call_out(ob, fun)
struct object *ob;
char *fun;
{
struct call **copp, *cop;
int delay = 0;
for (copp = &call_list; *copp; copp = &(*copp)->next)
if ((*copp)->ob == ob && strcmp((*copp)->function, fun) == 0) {
cop = *copp;
delay = cop->abstime - current_time;
return delay;
}
return -1;
}
int print_call_out_usage() {
int i;
struct call *cop;
for (i=0, cop = call_list; cop; cop = cop->next)
i++;
add_message("call out %6d %6d (length %d)\n", num_call,
num_call * sizeof (struct call), i);
return num_call * sizeof (struct call);
}