# include <kernel/kernel.h>
# include <kernel/rsrc.h>
# include <kernel/objreg.h>
# include <type.h>
# define CO_OBJ 0 /* callout object */
# define CO_OWNER 1 /* owner */
# define CO_HANDLE 2 /* handle in object */
# define CO_RELHANDLE 3 /* release handle */
# define CO_PREV 4 /* previous callout */
# define CO_NEXT 5 /* next callout */
mapping resources; /* registered resources */
mapping owners; /* resource owners */
int downtime; /* shutdown time */
mapping suspended; /* suspended callouts */
mixed *first_suspended; /* first suspended callout */
mixed *last_suspended; /* last suspended callout */
object suspender; /* object that suspended callouts */
int suspend; /* callouts suspended */
/*
* NAME: create()
* DESCRIPTION: initialize resource mappings
*/
static void create()
{
/* initial resources */
resources = ([
"objects" : ({ -1, 0, 0 }),
"events" : ({ -1, 0, 0 }),
"callouts" : ({ -1, 0, 0 }),
"stack" : ({ -1, 0, 0 }),
"ticks" : ({ -1, 0, 0 }),
"tick usage" : ({ -1, 10, 3600 }),
"filequota" : ({ -1, 0, 0 }),
"editors" : ({ -1, 0, 0 }),
"create stack" : ({ -1, 0, 0 }),
"create ticks" : ({ -1, 0, 0 }),
]);
owners = ([ ]); /* no resource owners yet */
}
/*
* NAME: add_owner()
* DESCRIPTION: add a new resource owner
*/
void add_owner(string owner)
{
if (KERNEL() && !owners[owner]) {
object obj;
rlimits (-1; -1) {
obj = clone_object(RSRCOBJ);
catch {
owners[owner] = obj;
obj->set_owner(owner);
owners["System"]->rsrc_incr("objects", nil, 1,
resources["objects"], TRUE);
} : {
destruct_object(obj);
}
}
if (!obj) {
error("Too many resource owners");
}
}
}
/*
* NAME: remove_owner()
* DESCRIPTION: remove a resource owner
*/
void remove_owner(string owner)
{
object obj;
string *names;
mixed **rsrcs, *rsrc;
int i;
if (previous_program() == API_RSRC && (obj=owners[owner])) {
names = map_indices(resources);
rsrcs = map_values(resources);
for (i = sizeof(names); --i >= 0; ) {
rsrc = obj->rsrc_get(names[i], rsrcs[i]);
if (rsrc[RSRC_DECAY] == 0 && (int) rsrc[RSRC_USAGE] != 0) {
error("Removing owner with non-zero resources");
}
}
destruct_object(obj);
}
}
/*
* NAME: query_owners()
* DESCRIPTION: return a list of resource owners
*/
string *query_owners()
{
if (previous_program() == API_RSRC) {
return map_indices(owners);
}
}
/*
* NAME: set_rsrc()
* DESCRIPTION: set the maximum, decay percentage and decay period of a
* resource
*/
void set_rsrc(string name, int max, int decay, int period)
{
if (KERNEL()) {
mixed *rsrc;
rsrc = resources[name];
if (rsrc) {
/*
* existing resource
*/
if ((rsrc[GRSRC_DECAY] == 0) != (decay == 0)) {
error("Cannot change resource decay");
}
rlimits (-1; -1) {
rsrc[GRSRC_MAX] = max;
rsrc[GRSRC_DECAY] = decay;
rsrc[GRSRC_PERIOD] = period;
}
} else {
/* new resource */
resources[name] = ({ max, decay, period });
}
}
}
/*
* NAME: remove_rsrc()
* DESCRIPTION: remove a resource
*/
void remove_rsrc(string name)
{
int *rsrc, i;
object *objects;
if (previous_program() == API_RSRC && (rsrc=resources[name])) {
objects = map_values(owners);
i = sizeof(objects);
rlimits (-1; -1) {
while (i != 0) {
objects[--i]->remove_rsrc(name);
}
resources[name] = nil;
}
}
}
/*
* NAME: query_rsrc()
* DESCRIPTION: get usage and limits of a resource
*/
mixed *query_rsrc(string name)
{
if (previous_program() == API_RSRC) {
mixed *rsrc, usage;
object *objects;
int i;
rsrc = resources[name][..];
objects = map_values(owners);
usage = (rsrc[GRSRC_DECAY] == 0) ? 0 : 0.0;
for (i = sizeof(objects); --i >= 0; ) {
usage += objects[i]->rsrc_get(name, resources[name])[RSRC_USAGE];
}
return ({ usage, rsrc[GRSRC_MAX], 0 }) +
rsrc[GRSRC_DECAY .. GRSRC_PERIOD];
}
}
/*
* NAME: query_resources()
* DESCRIPTION: return a list of resources
*/
string *query_resources()
{
if (previous_program() == API_RSRC) {
return map_indices(resources);
}
}
/*
* NAME: rsrc_set_limit()
* DESCRIPTION: set individual resource limit
*/
void rsrc_set_limit(string owner, string name, int max)
{
if (previous_program() == API_RSRC) {
object obj;
if (!(obj=owners[owner])) {
error("No such resource owner: " + owner);
}
obj->rsrc_set_limit(name, max, resources[name][GRSRC_DECAY]);
}
}
/*
* NAME: rsrc_get()
* DESCRIPTION: get individual resource usage
*/
mixed *rsrc_get(string owner, string name)
{
if (KERNEL()) {
object obj;
if (!(obj=owners[owner])) {
error("No such resource owner: " + owner);
}
return obj->rsrc_get(name, resources[name]);
}
}
/*
* NAME: rsrc_incr()
* DESCRIPTION: increment or decrement a resource, returning TRUE if succeeded,
* FALSE if failed
*/
int rsrc_incr(string owner, string name, mixed index, int incr,
varargs int force)
{
if (KERNEL()) {
object obj;
if (!(obj=owners[owner])) {
error("No such resource owner: " + owner);
}
return obj->rsrc_incr(name, index, incr, resources[name], force);
}
}
/*
* NAME: call_limits()
* DESCRIPTION: handle stack and tick limits for _F_call_limited
*/
mixed *call_limits(mixed *limits, string owner, int stack, int ticks)
{
if (previous_program() == AUTO) {
return owners[owner]->call_limits(limits, stack, ticks,
resources["stack"],
resources["ticks"],
resources["tick usage"]);
}
}
/*
* NAME: update_ticks()
* DESCRIPTION: update ticks after execution
*/
int update_ticks(mixed *limits, int ticks)
{
if (KERNEL()) {
if (limits[LIM_MAXTICKS] > 0 &&
(!limits[LIM_NEXT] ||
limits[LIM_OWNER] != limits[LIM_NEXT][LIM_OWNER])) {
owners[limits[LIM_OWNER]]->update_ticks(ticks = limits[LIM_MAXTICKS]
- ticks);
ticks = (limits[LIM_TICKS] >= 0) ?
limits[LIM_NEXT][LIM_MAXTICKS] -= ticks : -1;
}
return ticks;
}
}
/*
* NAME: suspend_callouts()
* DESCRIPTION: suspend all callouts
*/
void suspend_callouts()
{
if (SYSTEM() && suspend >= 0) {
mixed *callout;
rlimits (-1; -1) {
if (suspend > 0) {
callout = first_suspended;
do {
if (callout[CO_RELHANDLE] != 0) {
remove_call_out(callout[CO_RELHANDLE]);
callout[CO_RELHANDLE] = 0;
}
callout = callout[CO_NEXT];
} while (callout);
} else {
suspended = ([ ]);
}
suspender = previous_object();
suspend = -1;
}
}
}
/*
* NAME: release_callouts()
* DESCRIPTION: release suspended callouts
*/
void release_callouts()
{
if (SYSTEM() && suspend < 0) {
rlimits (-1; -1) {
suspender = nil;
if (first_suspended) {
mixed *callout;
callout = first_suspended;
do {
if (callout[CO_RELHANDLE] == 0) {
callout[CO_RELHANDLE] = call_out("release", 0);
}
callout = callout[CO_NEXT];
} while (callout);
suspend = 1;
} else {
suspended = nil;
suspend = 0;
}
}
}
}
/*
* NAME: suspended()
* DESCRIPTION: return TRUE if callouts are suspended, otherwise return FALSE
* and decrease # of callouts by 1
*/
int suspended(object obj, string owner)
{
if (previous_program() == AUTO) {
if (suspend != 0 && obj != suspender) {
return TRUE;
}
owners[owner]->rsrc_incr("callouts", obj, -1, resources["callouts"],
FALSE);
return FALSE;
}
}
/*
* NAME: suspend()
* DESCRIPTION: suspend a callout
*/
void suspend(object obj, string owner, int handle)
{
if (previous_program() == AUTO) {
mixed *callout;
callout = ({ obj, owner, handle, 0, last_suspended, nil });
if (suspend > 0) {
callout[CO_RELHANDLE] = call_out("release", 0);
}
if (last_suspended) {
last_suspended[CO_NEXT] = callout;
} else {
first_suspended = callout;
}
last_suspended = callout;
if (suspended[obj]) {
suspended[obj][handle] = callout;
} else {
suspended[obj] = ([ handle : callout ]);
}
}
}
/*
* NAME: remove_callout()
* DESCRIPTION: decrease amount of callouts, and possibly remove callout from
* list of suspended calls
*/
int remove_callout(object obj, string owner, int handle)
{
if (previous_program() == AUTO && obj != this_object()) {
mapping callouts;
mixed *callout;
owners[owner]->rsrc_incr("callouts", obj, -1, resources["callouts"],
FALSE);
if (suspended && (callouts=suspended[obj]) &&
(callout=callouts[handle])) {
if (callout != first_suspended) {
callout[CO_PREV][CO_NEXT] = callout[CO_NEXT];
} else {
first_suspended = callout[CO_NEXT];
}
if (callout != last_suspended) {
if (callout[CO_RELHANDLE] != 0) {
remove_call_out(last_suspended[CO_RELHANDLE]);
last_suspended[CO_RELHANDLE] = callout[CO_RELHANDLE];
}
callout[CO_NEXT][CO_PREV] = callout[CO_PREV];
} else {
if (callout[CO_RELHANDLE] != 0) {
remove_call_out(callout[CO_RELHANDLE]);
}
last_suspended = callout[CO_PREV];
}
callouts[handle] = nil;
return TRUE; /* delayed call */
}
}
return FALSE;
}
/*
* NAME: remove_callouts()
* DESCRIPTION: remove callouts from an object about to be destructed
*/
void remove_callouts(object obj, string owner, int n)
{
if (previous_program() == AUTO) {
mixed **callouts, *callout;
int i;
owners[owner]->rsrc_incr("callouts", obj, -n, resources["callouts"],
FALSE);
if (suspended && suspended[obj]) {
callouts = map_values(suspended[obj]);
for (i = sizeof(callouts); --i >= 0; ) {
callout = callouts[i];
if (callout != first_suspended) {
callout[CO_PREV][CO_NEXT] = callout[CO_NEXT];
} else {
first_suspended = callout[CO_NEXT];
}
if (callout != last_suspended) {
if (callout[CO_RELHANDLE] != 0) {
remove_call_out(last_suspended[CO_RELHANDLE]);
last_suspended[CO_RELHANDLE] = callout[CO_RELHANDLE];
}
callout[CO_NEXT][CO_PREV] = callout[CO_PREV];
} else {
if (callout[CO_RELHANDLE] != 0) {
remove_call_out(callout[CO_RELHANDLE]);
}
last_suspended = callout[CO_PREV];
}
}
suspended[obj] = nil;
}
}
}
/*
* NAME: release()
* DESCRIPTION: release a callout
*/
static void release()
{
mixed *callout;
object obj;
int handle;
callout = first_suspended;
obj = callout[CO_OBJ];
handle = callout[CO_HANDLE];
if ((first_suspended=callout[CO_NEXT])) {
first_suspended[CO_PREV] = nil;
suspended[obj][handle] = nil;
} else {
last_suspended = nil;
suspended = nil;
suspend = 0;
}
owners[callout[CO_OWNER]]->rsrc_incr("callouts", obj, -1,
resources["callouts"], FALSE);
obj->_F_release(handle);
}
/*
* NAME: prepare_reboot()
* DESCRIPTION: prepare for a reboot
*/
void prepare_reboot()
{
if (previous_program() == DRIVER) {
downtime = time();
}
}
/*
* NAME: reboot()
* DESCRIPTION: recover from a reboot
*/
void reboot()
{
if (previous_program() == DRIVER) {
object *objects;
int i;
objects = OBJREGD->remove_editors();
for (i = sizeof(objects); --i >= 0; ) {
owners[objects[i]->query_owner()]->rsrc_incr("editors", objects[i],
-1,
resources["editors"],
TRUE);
}
downtime = time() - downtime;
objects = map_values(owners);
for (i = sizeof(objects); --i >= 0; ) {
objects[i]->reboot(downtime);
}
}
}