/**
* handler to remove junk lost in null-space from the mud
* so they wont waste memory, and (if they have a heart_beat)
* cpu anymore.
* @changed Sandoz, 09/09/2002
* Changed to not consider doors/chatters with no my_room junk.
* The refs count should still get those.
* @changed Sandoz, 16/10/2002
* Added a new cleanup scheme, that will hold back on heaps of rooms
* calling out real_clean at the same time, and that will call
* reclaim_objects() on more sensible times.
*/
// Minimum time between reboots (28800 == 8 hours).
#define MIN_TIME 28800
// Maximum no. of faults per timer interval.
#define FAULT_RATE 8500
// How long a room has to be idle before cleaning up.
#define ROOM_IDLE_DELAY 1800
// If a room has been idle for this long don't clean it up again since
// it is probably not going to go away.
#define ROOM_STABLE_DELAY 10800
// The interval at which to do housekeeping.
#define HOUSEKEEP_INTERVAL 600
// Whether or not we don't want to automatically reboot when major page
// faults get too high.
#define AUTO_REBOOT
private int lag, maxlag, minlag;
private int faults_prev, stime_prev, utime_prev, uptime_prev;
private int rate_exceeded, times_run;
// The new clean up puts the rooms to be cleaned up in an array,
// and then calls real_clean on some of them every second.
// Once the array is empty, we call reclaim_objects().
// - Sandoz
#define NEW_CLEAN_UP
#ifdef NEW_CLEAN_UP
// sub_arr_size is the size of rooms in each subarray.
// If sub_arr_size is 0, then the rooms array contains objects,
// instead of arrays of objects.
private int sub_arr_size;
// rooms_left stores the number of room currently on the clean up list,
// to avoid calling sizeof() every time a room is being cleaned up.
// This value may be off by sub_arr_size - 1, because it is set to
// sizeof( rooms ) * sub_arr_size, whereas the last member of rooms can be
// semi-filled. The value will be correct if the rooms array is an array
// of objects though.
private int rooms_left;
// This can be either an array of room objects,
// or an array of arrays of room objects.
private mixed rooms;
#endif
private void cycle();
private void reclaim_and_log();
private void housekeeping();
private void tidy_up();
private void check_reboot();
/** @ignore yes */
private void create() {
mapping map;
map = rusage();
faults_prev = map["majflt"];
stime_prev = map["stime"];
utime_prev = map["utime"];
uptime_prev = uptime();
#ifdef NEW_CLEAN_UP
sub_arr_size = -1;
#endif
call_out( (: housekeeping :), HOUSEKEEP_INTERVAL / 2 );
call_out( (: cycle :), 1 );
} /* create() */
#ifdef NEW_CLEAN_UP
private void clean_up_room( object ob ) {
if( ob ) {
int i;
if( catch( i = ob->real_clean() ) && ( !i && ob ) ) {
tell_creator("sandoz", "Failed real_clean() on %O",
file_name(ob) );
catch( ob->clean_up() );
}
}
} /* clean_up_room() */
private void finish_clean_up() {
// Let the rubbish room do what it does, and then reclaim.
sub_arr_size = -1;
call_out( (: reclaim_and_log :), 10 );
} /* finish_clean_up() */
#endif
/** @ignore yes */
private void cycle() {
int diff;
diff = real_time() - time();
if( diff > 2 && ( lag += ( diff -= 2 ) ) > maxlag )
maxlag = diff;
else if( diff < minlag )
minlag = diff;
call_out( (: cycle :), 1 );
#ifdef NEW_CLEAN_UP
switch( sub_arr_size ) {
case -1:
break;
case 0:
clean_up_room( rooms[0] );
rooms = rooms[1..];
if( !( --rooms_left ) )
finish_clean_up();
break;
default:
diff = sub_arr_size;
while( diff-- )
clean_up_room( rooms[0][diff] );
rooms = rooms[1..];
if( !( rooms_left -= sub_arr_size ) )
finish_clean_up();
}
#endif
} /* cycle() */
private void reclaim_and_log() {
string log;
int rmem, ocount;
rmem = memory_info();
ocount = reclaim_objects();
rmem -= memory_info();
log = sprintf("Reclaimed %d object%s (%d byte%s)", ocount,
( ocount > 1 ? "s" : ""), rmem, ( rmem > 1 ? "s" : "") );
event( users(), "inform", log, "cpu");
log_file("RECLAIM", "%s - %s.\n", ctime(time()), log );
} /* reclaim_and_log() */
/**
* Filter function for the objects() efun. We only want clones without
* references from other objects that are not shadowing stuff,
* not in a room and are not rooms themselves.
*/
int get_junk( object ob ) {
return ( clonep(ob) && refs(ob) == 2 && !query_shadowing(ob) &&
!ENV(ob) && !function_exists("query_last_visited", ob ) );
} /* get_junk() */
/**
* @ignore yes
* A word of explanation from Ceres.
* The system reboots based on the rate of major page faults
* ie. the rate of paging. To handle updating of the drum
* it will not reboot if prev_faults is 0 and just in case,
* there is a minimum uptime as well.
*/
private void check_reboot() {
int faults_now, stime_now, utime_now, reboot;
mapping map;
string log;
map = rusage();
stime_now = map["stime"];
utime_now = map["utime"];
faults_now = map["majflt"];
log = "CPU usage for period = "+
(((( utime_now - utime_prev )+( stime_now - stime_prev ) ) / 10.0 ) /
( uptime() - uptime_prev ) )+"% Fault rate is "+
( faults_now - faults_prev );
event( users(), "inform", log, "cpu");
log_file("CPU", ctime(time())+" - "+log+".\n");
log = "Machine lag is "+( lag / 600 )+" seconds (average) "+
minlag+" seconds (min) "+maxlag+" seconds (max)";
event( users(), "inform", log, "cpu");
log_file("CPU", log+".\n");
// If we're already rebooting then don't do anything.
if( SHUTDOWN_H->query_shutdown() < 10 )
return;
#ifdef AUTO_REBOOT
if( uptime() > MIN_TIME && faults_now > ( faults_prev + FAULT_RATE ) ) {
if( rate_exceeded )
reboot = 1;
else
rate_exceeded = 1;
} else {
rate_exceeded = 0;
}
#endif
if( reboot ) {
SHUTDOWN_H->init_shutdown( 10, 1 );
log_file("REBOOT", ctime(time())+" Auto; Faults: "+
faults_now+", "+faults_prev+"; uptime: "+uptime()+"; CPU: "+
(((( utime_now - utime_prev ) +
( stime_now - stime_prev ) ) / 10.0 ) /
( uptime() - uptime_prev ) )+"\n");
// Get a dump of the objects before we go down.
// dumpallobj();
return;
}
faults_prev = faults_now;
uptime_prev = uptime();
stime_prev = stime_now;
utime_prev = utime_now;
lag = 0;
maxlag = 0;
minlag = 600;
} /* check_reboot() */
/**
* @ignore yes
* Do memory saving things.
*/
private void tidy_up() {
times_run++;
#ifndef NEW_CLEAN_UP
// Perform reclamation of unreferenced objects.
if( !( times_run % 3 ) )
reclaim_and_log();
#endif
// This does a manual call of clean_up on rooms since the driver
// doesn't do it for a couple of reasons like references,
// which rooms have more than 1.
// We do it every half hour after we've been up at least 1 hour.
if( times_run > 6 && ( times_run % 3 == 1 ) ) {
object *obs;
#ifdef NEW_CLEAN_UP
int sz;
#endif
obs = objects( (: function_exists("query_last_visited", $1 ) :) );
if( ( times_run % 18 ) != 1 ) {
// We're just doing rooms that haven't been visited for a while
// (but not too long) and that don't want to keep loaded.
obs = filter( obs, (: !$1->query_keep_room_loaded() &&
$1->query_last_visited() < ( $2 - ROOM_IDLE_DELAY ) ||
$1->query_last_visited() > ( $2 - ROOM_STABLE_DELAY ) :),
time() );
} else {
// Every three hours give the stable rooms a poke too.
obs = filter( obs, (: !$1->query_keep_room_loaded() &&
$1->query_last_visited() < ( $2 - ROOM_IDLE_DELAY ) :),
time() );
}
#ifndef NEW_CLEAN_UP
foreach( object ob in obs )
catch( ob->clean_up() );
#else
// Divide them into arrays of objects.
if( ( sz = sizeof(obs) ) > HOUSEKEEP_INTERVAL ) {
int i, j, k, cells;
sub_arr_size = sz / HOUSEKEEP_INTERVAL + 1;
cells = sz / sub_arr_size;
if( sz % ( cells * sub_arr_size ) )
cells++;
rooms = allocate( cells, (: allocate( sub_arr_size ) :) );
for( i = 0, k = 0; i < cells - 1; i++ )
for( j = 0; j < sub_arr_size; j++ )
rooms[i][j] = obs[k++];
for( j = 0; k < sz; k++ )
rooms[i][j++] = obs[k];
rooms_left = cells * sub_arr_size;
} else {
if( rooms_left = sizeof( rooms = obs ) )
sub_arr_size = 0;
else
sub_arr_size = -1;
}
#endif
log_file("CLEAN_UP", "%s Cleaning %d idle rooms. Current "
"memory %.2fMB\n", ctime(time()), sizeof(obs),
memory_info() / 1024000.0 );
event( users(), "inform", sprintf("Cleaning %d idle rooms",
sizeof(obs) ), "cpu");
}
} /* tidy_up() */
/** @ignore yes */
private void housekeeping() {
object *junk;
call_out( (: check_reboot :), 10 );
call_out( (: tidy_up :), 20 );
call_out( (: housekeeping :), HOUSEKEEP_INTERVAL );
junk = objects( (: get_junk :) );
foreach( object ob in junk ) {
log_file("NULLSPACEJUNK", "%s - %O (%O) cloned by %O.\n",
ctime(time()), ob, ob->query_short(), ob->query_cloned_by() );
catch( ob->dest_me() );
if( ob )
destruct(ob);
}
} /* housekeeping() */
/** @ignore yes */
int query_faults_prev() { return faults_prev; }
/** @ignore yes */
mixed stats() {
return ({
({"lag", lag }),
({"maxlag", maxlag }),
({"minlag", minlag }),
({"previous faults", faults_prev }),
({"previous stime", stime_prev }),
({"previous utime", utime_prev }),
({"previous uptime", uptime_prev }),
({"rate exceeded", rate_exceeded }),
({"times run", times_run }),
#ifdef NEW_CLEAN_UP
({"array size", ( sub_arr_size != -1 ? sub_arr_size : 0 ) }),
({"rooms left", rooms_left }),
#endif
});
} /* stats() */