diff --git a/btime.dll b/btime.dll new file mode 100644 index 00000000000..36dcbe3af75 Binary files /dev/null and b/btime.dll differ diff --git a/code/__DEFINES/btime.dm b/code/__DEFINES/btime.dm new file mode 100644 index 00000000000..1e9cda3258f --- /dev/null +++ b/code/__DEFINES/btime.dm @@ -0,0 +1,21 @@ +// Comment this out if the external btime library is unavailable +#define PRECISE_TIMER_AVAILABLE + +#ifdef PRECISE_TIMER_AVAILABLE +var/global/__btime__lastTimeOfHour = 0 +var/global/__btime__callCount = 0 +var/global/__btime__lastTick = 0 +var/global/__btime__libName = "btime.[world.system_type==MS_WINDOWS?"dll":"so"]" +#define TimeOfHour (__extern__timeofhour) +#define __extern__timeofhour text2num(call(__btime__libName, "gettime")()) +/hook/startup/proc/checkbtime() + try + // This will always return 1 unless the btime library cannot be accessed + if(TimeOfHour || 1) return 1 + catch(var/exception/e) + world.log << "PRECISE_TIMER_AVAILABLE is defined in btime.dm, but calling the btime library failed: [e]" + world.log << "This is a fatal error. The world will now shut down." + del(world) +#else +#define TimeOfHour (world.timeofday % 36000) +#endif diff --git a/code/controllers/ProcessScheduler/core/_define.dm b/code/__DEFINES/process_scheduler.dm similarity index 63% rename from code/controllers/ProcessScheduler/core/_define.dm rename to code/__DEFINES/process_scheduler.dm index f0165c98708..af859e314ff 100644 --- a/code/controllers/ProcessScheduler/core/_define.dm +++ b/code/__DEFINES/process_scheduler.dm @@ -11,7 +11,9 @@ #define PROCESS_DEFAULT_HANG_ALERT_TIME 600 // 60 seconds #define PROCESS_DEFAULT_HANG_RESTART_TIME 900 // 90 seconds #define PROCESS_DEFAULT_SCHEDULE_INTERVAL 50 // 50 ticks -#define PROCESS_DEFAULT_SLEEP_INTERVAL 2 // 2 ticks -#define PROCESS_DEFAULT_CPU_THRESHOLD 90 // 90% +#define PROCESS_DEFAULT_SLEEP_INTERVAL 8 // 1/8th of a tick -//#define UPDATE_QUEUE_DEBUG \ No newline at end of file +// SCHECK macros +// This references src directly to work around a weird bug with try/catch +#define SCHECK_EVERY(this_many_calls) if(++src.calls_since_last_scheck >= this_many_calls) sleepCheck() +#define SCHECK SCHECK_EVERY(50) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index f465da33c85..cd38e6e34d8 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -4,9 +4,7 @@ var/global/obj/effect/overlay/slmaster = null // nanomanager, the manager for Nano UIs var/datum/nanomanager/nanomanager = new() // Event Manager, the manager for events. -var/datum/event_manager/event_manager = new() -// Alarm Manager, the manager for alarms. -var/datum/subsystem/alarm/alarm_manager = new() +var/datum/event_manager/event_manager = new() // Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. var/global/obj/item/device/radio/intercom/global_announcer = create_global_announcer() // Load order issues means this can't be new'd until other code runs diff --git a/code/controllers/ProcessScheduler/ProcessScheduler.dme b/code/controllers/ProcessScheduler/ProcessScheduler.dme deleted file mode 100644 index bf17734cc28..00000000000 --- a/code/controllers/ProcessScheduler/ProcessScheduler.dme +++ /dev/null @@ -1,32 +0,0 @@ -// DM Environment file for ProcessScheduler.dme. -// All manual changes should be made outside the BEGIN_ and END_ blocks. -// New source code should be placed in .dm files: choose File/New --> Code File. - -// BEGIN_INTERNALS -// END_INTERNALS - -// BEGIN_FILE_DIR -#define FILE_DIR . -// END_FILE_DIR - -// BEGIN_PREFERENCES -// END_PREFERENCES - -// BEGIN_INCLUDE -#include "core\_define.dm" -#include "core\_stubs.dm" -#include "core\process.dm" -#include "core\processScheduler.dm" -#include "core\updateQueue.dm" -#include "core\updateQueueWorker.dm" -#include "test\processSchedulerView.dm" -#include "test\testDyingUpdateQueueProcess.dm" -#include "test\testHarness.dm" -#include "test\testHungProcess.dm" -#include "test\testNiceProcess.dm" -#include "test\testSlowProcess.dm" -#include "test\testUpdateQueue.dm" -#include "test\testUpdateQueueProcess.dm" -#include "test\testZombieProcess.dm" -// END_INCLUDE - diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm index 0cfec361b3f..0a2143a3e14 100644 --- a/code/controllers/ProcessScheduler/core/process.dm +++ b/code/controllers/ProcessScheduler/core/process.dm @@ -48,7 +48,7 @@ // This controls how often the process will yield (call sleep(0)) while it is running. // Every concurrent process should sleep periodically while running in order to allow other // processes to execute concurrently. - var/tmp/sleep_interval = PROCESS_DEFAULT_SLEEP_INTERVAL + var/tmp/sleep_interval // hang_warning_time - this is the time (in 1/10 seconds) after which the server will begin to show "maybe hung" in the context window var/tmp/hang_warning_time = PROCESS_DEFAULT_HANG_WARNING_TIME @@ -59,20 +59,20 @@ // hang_restart_time - After this much time(in 1/10 seconds), the server will automatically kill and restart the process. var/tmp/hang_restart_time = PROCESS_DEFAULT_HANG_RESTART_TIME - // cpu_threshold - if world.cpu >= cpu_threshold, scheck() will call sleep(1) to defer further work until the next tick. This keeps a process from driving a tick into overtime (causing perceptible lag) - var/tmp/cpu_threshold = PROCESS_DEFAULT_CPU_THRESHOLD - // How many times in the current run has the process deferred work till the next tick? var/tmp/cpu_defer_count = 0 + // How many SCHECKs have been skipped (to limit btime calls) + var/tmp/calls_since_last_scheck = 0 + /** * recordkeeping vars */ - // Records the time (server ticks) at which the process last finished sleeping + // Records the time (1/10s timeofday) at which the process last finished sleeping var/tmp/last_slept = 0 - // Records the time (s-ticks) at which the process last began running + // Records the time (1/10s timeofday) at which the process last began running var/tmp/run_start = 0 // Records the number of times this process has been killed and restarted @@ -98,7 +98,7 @@ idle() name = "process" schedule_interval = 50 - sleep_interval = 2 + sleep_interval = world.tick_lag / PROCESS_DEFAULT_SLEEP_INTERVAL last_slept = 0 run_start = 0 ticks = 0 @@ -106,11 +106,12 @@ last_object = null /datum/controller/process/proc/started() + var/timeofhour = TimeOfHour // Initialize last_slept so we can know when to sleep - last_slept = world.timeofday + last_slept = timeofhour // Initialize run_start so we can detect hung processes. - run_start = world.timeofday + run_start = timeofhour // Initialize defer count cpu_defer_count = 0 @@ -162,16 +163,16 @@ setStatus(PROCESS_STATUS_HUNG) /datum/controller/process/proc/handleHung() + var/timeofhour = TimeOfHour var/datum/lastObj = last_object var/lastObjType = "null" if(istype(lastObj)) lastObjType = lastObj.type - // If world.timeofday has rolled over, then we need to adjust. - if (world.timeofday < run_start) - run_start -= 864000 - - var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(world.timeofday - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]" + // If timeofhour has rolled over, then we need to adjust. + if (timeofhour < run_start) + run_start -= 36000 + var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(timeofhour - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]" logTheThing("debug", null, null, msg) logTheThing("diary", null, null, msg, "debug") message_admins(msg) @@ -188,33 +189,36 @@ // Allow inheritors to clean up if needed onKill() - killed = TRUE - // This should del del(src) -/datum/controller/process/proc/scheck(var/tickId = 0) +// Do not call this directly - use SHECK or SCHECK_EVERY +/datum/controller/process/proc/sleepCheck(var/tickId = 0) + calls_since_last_scheck = 0 if (killed) // The kill proc is the only place where killed is set. // The kill proc should have deleted this datum, and all sleeping procs that are // owned by it. CRASH("A killed process is still running somehow...") + if (hung) + // This will only really help if the doWork proc ends up in an infinite loop. + handleHung() + CRASH("Process [name] hung and was restarted.") - // For each tick the process defers, it increments the cpu_defer_count so we don't - // defer indefinitely - if (world.cpu >= (cpu_threshold + cpu_defer_count * world.tick_lag * 10)) - sleep(world.tick_lag) + if (main.getCurrentTickElapsedTime() > main.timeAllowance) + sleep(world.tick_lag*1) cpu_defer_count++ - last_slept = world.timeofday + last_slept = TimeOfHour else - // If world.timeofday has rolled over, then we need to adjust. - if (world.timeofday < last_slept) - last_slept -= 864000 + var/timeofhour = TimeOfHour + // If timeofhour has rolled over, then we need to adjust. + if (timeofhour < last_slept) + last_slept -= 36000 - if (world.timeofday > last_slept + sleep_interval) - // If we haven't slept in sleep_interval ticks, sleep to allow other work to proceed. + if (timeofhour > last_slept + sleep_interval) + // If we haven't slept in sleep_interval deciseconds, sleep to allow other work to proceed. sleep(0) - last_slept = world.timeofday + last_slept = TimeOfHour /datum/controller/process/proc/update() // Clear delta @@ -223,17 +227,22 @@ var/elapsedTime = getElapsedTime() - if (elapsedTime > hang_restart_time) + if (hung) + handleHung() + return + else if (elapsedTime > hang_restart_time) hung() else if (elapsedTime > hang_alert_time) setStatus(PROCESS_STATUS_PROBABLY_HUNG) else if (elapsedTime > hang_warning_time) setStatus(PROCESS_STATUS_MAYBE_HUNG) + /datum/controller/process/proc/getElapsedTime() - if (world.timeofday < run_start) - return world.timeofday - (run_start - 864000) - return world.timeofday - run_start + var/timeofhour = TimeOfHour + if (timeofhour < run_start) + return timeofhour - (run_start - 36000) + return timeofhour - run_start /datum/controller/process/proc/tickDetail() return @@ -316,6 +325,8 @@ /datum/controller/process/proc/enable() disabled = 0 +/datum/controller/process/proc/getAverageRunTime() + return main.averageRunTime(src) /datum/controller/process/proc/getLastRunTime() return main.getProcessLastRunTime(src) @@ -326,7 +337,10 @@ return ticks /datum/controller/process/proc/statProcess() - stat("[name]", "T#[getTicks()]|LR [getLastRunTime()]|HR [getHighestRunTime()]|D [cpu_defer_count]") + var/averageRunTime = round(getAverageRunTime(), 0.1)/10 + var/lastRunTime = round(getLastRunTime(), 0.1)/10 + var/highestRunTime = round(getHighestRunTime(), 0.1)/10 + stat("[name]", "T#[getTicks()]|AR [averageRunTime]|LR [lastRunTime]|HR [highestRunTime]|D [cpu_defer_count]") /datum/controller/process/proc/catchException(var/exception/e, var/thrower) var/etext = "[e]" @@ -351,3 +365,8 @@ world.log << "This exception will now be ignored for ten minutes." spawn(6000) exceptions[eid] = 0 + +/datum/controller/process/proc/catchBadType(var/datum/caught) + if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed)) + return // Only bother with types we can identify and that don't belong + catchException("Type [caught.type] does not belong in process' queue") \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm index 8ea141ff1aa..4bb34183eb2 100644 --- a/code/controllers/ProcessScheduler/core/processScheduler.dm +++ b/code/controllers/ProcessScheduler/core/processScheduler.dm @@ -17,7 +17,10 @@ var/global/datum/controller/processScheduler/processScheduler // Process name -> process object map var/tmp/datum/controller/process/list/nameToProcessMap = new - // Process last start times + // Process last queued times (world time) + var/tmp/datum/controller/process/list/last_queued = new + + // Process last start times (real time) var/tmp/datum/controller/process/list/last_start = new // Process last run durations @@ -29,8 +32,8 @@ var/global/datum/controller/processScheduler/processScheduler // Process highest run time var/tmp/datum/controller/process/list/highest_run_time = new - // Sleep 1 tick -- This may be too aggressive. - var/tmp/scheduler_sleep_interval = 1 + // How long to sleep between runs (set to tick_lag in New) + var/tmp/scheduler_sleep_interval // Controls whether the scheduler is running or not var/tmp/isRunning = 0 @@ -38,6 +41,25 @@ var/global/datum/controller/processScheduler/processScheduler // Setup for these processes will be deferred until all the other processes are set up. var/tmp/list/deferredSetupList = new + var/tmp/currentTick = 0 + + var/tmp/currentTickStart = 0 + + var/tmp/timeAllowance = 0 + + var/tmp/cpuAverage = 0 + + var/tmp/timeAllowanceMax = 0 + +/datum/controller/processScheduler/New() + ..() + // When the process scheduler is first new'd, tick_lag may be wrong, so these + // get re-initialized when the process scheduler is started. + // (These are kept here for any processes that decide to process before round start) + scheduler_sleep_interval = world.tick_lag + timeAllowance = world.tick_lag * 0.5 + timeAllowanceMax = world.tick_lag + /** * deferSetupFor * @param path processPath @@ -66,12 +88,22 @@ var/global/datum/controller/processScheduler/processScheduler /datum/controller/processScheduler/proc/start() isRunning = 1 + // tick_lag will have been set by now, so re-initialize these + scheduler_sleep_interval = world.tick_lag + timeAllowance = world.tick_lag * 0.5 + timeAllowanceMax = world.tick_lag updateStartDelays() spawn(0) process() /datum/controller/processScheduler/proc/process() + updateCurrentTickData() + + for(var/i=world.tick_lag,i= last_start[p] + p.schedule_interval) + if (world.time >= last_queued[p] + p.schedule_interval) setQueuedProcessState(p) /datum/controller/processScheduler/proc/runQueuedProcesses() @@ -176,7 +204,7 @@ var/global/datum/controller/processScheduler/processScheduler /datum/controller/processScheduler/proc/updateStartDelays() for(var/datum/controller/process/p in processes) if(p.start_delay) - last_start[p] = world.time - p.start_delay + last_queued[p] = world.time - p.start_delay /datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process) spawn(0) @@ -198,8 +226,6 @@ var/global/datum/controller/processScheduler/processScheduler if (!(process in idle)) idle += process - process.idle() - /datum/controller/processScheduler/proc/setQueuedProcessState(var/datum/controller/process/process) if (process in running) running -= process @@ -219,17 +245,22 @@ var/global/datum/controller/processScheduler/processScheduler if (!(process in running)) running += process - process.running() - /datum/controller/processScheduler/proc/recordStart(var/datum/controller/process/process, var/time = null) if (isnull(time)) - time = world.time - - last_start[process] = time + time = TimeOfHour + last_queued[process] = world.time + last_start[process] = time + else + last_queued[process] = (time == 0 ? 0 : world.time) + last_start[process] = time /datum/controller/processScheduler/proc/recordEnd(var/datum/controller/process/process, var/time = null) if (isnull(time)) - time = world.time + time = TimeOfHour + + // If world.timeofday has rolled over, then we need to adjust. + if (time < last_start[process]) + last_start[process] -= 36000 var/lastRunTime = time - last_start[process] @@ -270,6 +301,12 @@ var/global/datum/controller/processScheduler/processScheduler return t / c return c +/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process) + return last_run_time[process] + +/datum/controller/processScheduler/proc/getProcessHighestRunTime(var/datum/controller/process/process) + return highest_run_time[process] + /datum/controller/processScheduler/proc/getStatusData() var/list/data = new @@ -307,22 +344,39 @@ var/global/datum/controller/processScheduler/processScheduler var/datum/controller/process/process = nameToProcessMap[processName] process.disable() -/datum/controller/processScheduler/proc/getProcess(var/name) - return nameToProcessMap[name] +/datum/controller/processScheduler/proc/getCurrentTickElapsedTime() + if (world.time > currentTick) + updateCurrentTickData() + return 0 + else + return TimeOfHour - currentTickStart -/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process) - return last_run_time[process] +/datum/controller/processScheduler/proc/updateCurrentTickData() + if (world.time > currentTick) + // New tick! + currentTick = world.time + currentTickStart = TimeOfHour + updateTimeAllowance() + cpuAverage = (world.cpu + cpuAverage + cpuAverage) / 3 -/datum/controller/processScheduler/proc/getProcessHighestRunTime(var/datum/controller/process/process) - return highest_run_time[process] +/datum/controller/processScheduler/proc/updateTimeAllowance() + // Time allowance goes down linearly with world.cpu. + var/tmp/error = cpuAverage - 100 + var/tmp/timeAllowanceDelta = sign(error) * -0.5 * world.tick_lag * max(0, 0.001 * abs(error)) -/datum/controller/processScheduler/proc/getIsRunning() - return isRunning + //timeAllowance = world.tick_lag * min(1, 0.5 * ((200/max(1,cpuAverage)) - 1)) + timeAllowance = min(timeAllowanceMax, max(0, timeAllowance + timeAllowanceDelta)) + +/datum/controller/processScheduler/proc/sign(var/x) + if (x == 0) + return 1 + return x / abs(x) /datum/controller/processScheduler/proc/statProcesses() if(!isRunning) stat("Processes", "Scheduler not running") return stat("Processes", "[processes.len] (R[running.len]/Q[queued.len]/I[idle.len])") + stat(null, "[round(cpuAverage, 0.1)] CPU, [round(timeAllowance, 0.1)/10] TA") for(var/datum/controller/process/p in processes) p.statProcess() diff --git a/code/controllers/ProcessScheduler/core/updateQueue.dm b/code/controllers/ProcessScheduler/core/updateQueue.dm deleted file mode 100644 index 118b6692b5a..00000000000 --- a/code/controllers/ProcessScheduler/core/updateQueue.dm +++ /dev/null @@ -1,127 +0,0 @@ -/** - * updateQueue.dm - */ - -#ifdef UPDATE_QUEUE_DEBUG -#define uq_dbg(text) world << text -#else -#define uq_dbg(text) -#endif -/datum/updateQueue - var/tmp/list/objects - var/tmp/previousStart - var/tmp/procName - var/tmp/list/arguments - var/tmp/datum/updateQueueWorker/currentWorker - var/tmp/workerTimeout - var/tmp/adjustedWorkerTimeout - var/tmp/currentKillCount - var/tmp/totalKillCount - -/datum/updateQueue/New(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0) - ..() - - uq_dbg("Update queue created.") - - // Init proc allows for recycling the worker. - init(objects = objects, procName = procName, arguments = arguments, workerTimeout = workerTimeout, inplace = inplace) - -/** - * init - * @param list objects objects to update - * @param text procName the proc to call on each item in the object list - * @param list arguments optional arguments to pass to the update proc - * @param number workerTimeout number of ticks to wait for an update to - finish before forking a new update worker - * @param bool inplace whether the updateQueue should make a copy of objects. - the internal list will be modified, so it is usually - a good idea to leave this alone. Default behavior is to - copy. - */ -/datum/updateQueue/proc/init(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0) - uq_dbg("Update queue initialization started.") - - if (!inplace) - // Make an internal copy of the list so we're not modifying the original. - initList(objects) - else - src.objects = objects - - // Init vars - src.procName = procName - src.arguments = arguments - src.workerTimeout = workerTimeout - - adjustedWorkerTimeout = workerTimeout - currentKillCount = 0 - totalKillCount = 0 - - uq_dbg("Update queue initialization finished. procName = '[procName]'") - -/datum/updateQueue/proc/initList(list/toCopy) - /** - * We will copy the list in reverse order, as our doWork proc - * will access them by popping an element off the end of the list. - * This ends up being quite a lot faster than taking elements off - * the head of the list. - */ - objects = new - - uq_dbg("Copying [toCopy.len] items for processing.") - - for(var/i=toCopy.len,i>0,) - objects.len++ - objects[objects.len] = toCopy[i--] - -/datum/updateQueue/proc/Run() - uq_dbg("Starting run...") - - startWorker() - while (istype(currentWorker) && !currentWorker.finished) - sleep(2) - checkWorker() - - uq_dbg("UpdateQueue completed run.") - -/datum/updateQueue/proc/checkWorker() - if(istype(currentWorker)) - // If world.timeofday has rolled over, then we need to adjust. - if(world.timeofday < currentWorker.lastStart) - currentWorker.lastStart -= 864000 - - if(world.timeofday - currentWorker.lastStart > adjustedWorkerTimeout) - // This worker is a bit slow, let's spawn a new one and kill the old one. - uq_dbg("Current worker is lagging... starting a new one.") - killWorker() - startWorker() - else // No worker! - uq_dbg("update queue ended up without a worker... starting a new one...") - startWorker() - -/datum/updateQueue/proc/startWorker() - // only run the worker if we have objects to work on - if(objects.len) - uq_dbg("Starting worker process.") - - // No need to create a fresh worker if we already have one... - if (istype(currentWorker)) - currentWorker.init(objects, procName, arguments) - else - currentWorker = new(objects, procName, arguments) - currentWorker.start() - else - uq_dbg("Queue is empty. No worker was started.") - currentWorker = null - -/datum/updateQueue/proc/killWorker() - // Kill the worker - currentWorker.kill() - currentWorker = null - // After we kill a worker, yield so that if the worker's been tying up the cpu, other stuff can immediately resume - sleep(-1) - currentKillCount++ - totalKillCount++ - if (currentKillCount >= 3) - uq_dbg("[currentKillCount] workers have been killed with a timeout of [adjustedWorkerTimeout]. Increasing worker timeout to compensate.") - adjustedWorkerTimeout++ - currentKillCount = 0 \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/core/updateQueueWorker.dm b/code/controllers/ProcessScheduler/core/updateQueueWorker.dm deleted file mode 100644 index 39737ef0213..00000000000 --- a/code/controllers/ProcessScheduler/core/updateQueueWorker.dm +++ /dev/null @@ -1,83 +0,0 @@ -datum/updateQueueWorker - var/tmp/list/objects - var/tmp/killed - var/tmp/finished - var/tmp/procName - var/tmp/list/arguments - var/tmp/lastStart - var/tmp/cpuThreshold - -datum/updateQueueWorker/New(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90) - ..() - uq_dbg("updateQueueWorker created.") - - init(objects, procName, arguments, cpuThreshold) - -datum/updateQueueWorker/proc/init(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90) - src.objects = objects - src.procName = procName - src.arguments = arguments - src.cpuThreshold = cpuThreshold - - killed = 0 - finished = 0 - -datum/updateQueueWorker/proc/doWork() - // If there's nothing left to execute or we were killed, mark finished and return. - if (!objects || !objects.len) return finished() - - lastStart = world.timeofday // Absolute number of ticks since the world started up - - var/datum/object = objects[objects.len] // Pull out the object - objects.len-- // Remove the object from the list - - if (istype(object) && !isturf(object) && isnull(object.gcDestroyed)) // We only work with real objects - call(object, procName)(arglist(arguments)) - - // If there's nothing left to execute - // or we were killed while running the above code, mark finished and return. - if (!objects || !objects.len) return finished() - - if (world.cpu > cpuThreshold) - // We don't want to force a tick into overtime! - // If the tick is about to go overtime, spawn the next update to go - // in the next tick. - uq_dbg("tick went into overtime with world.cpu = [world.cpu], deferred next update to next tick [1+(world.time / world.tick_lag)]") - - spawn(1) - doWork() - else - spawn(0) // Execute anonymous function immediately as if we were in a while loop... - doWork() - -datum/updateQueueWorker/proc/finished() - uq_dbg("updateQueueWorker finished.") - /** - * If the worker was killed while it was working on something, it - * should delete itself when it finally finishes working on it. - * Meanwhile, the updateQueue will have proceeded on with the rest of - * the queue. This will also terminate the spawned function that was - * created in the kill() proc. - */ - if(killed) - del(src) - - finished = 1 - -datum/updateQueueWorker/proc/kill() - uq_dbg("updateQueueWorker killed.") - killed = 1 - objects = null - - /** - * If the worker is not done in 30 seconds after it's killed, - * we'll forcibly delete it, causing the anonymous function it was - * running to be terminated. Hasta la vista, baby. - */ - spawn(300) - del(src) - -datum/updateQueueWorker/proc/start() - uq_dbg("updateQueueWorker started.") - spawn(0) - doWork() \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/processScheduler.js b/code/controllers/ProcessScheduler/test/processScheduler.js deleted file mode 100644 index 0a4f111355d..00000000000 --- a/code/controllers/ProcessScheduler/test/processScheduler.js +++ /dev/null @@ -1,56 +0,0 @@ -(function ($) { - function setRef(theRef) { - ref = theRef; - } - - function jax(action, data) { - if (typeof data === 'undefined') - data = {}; - var params = []; - for (var k in data) { - if (data.hasOwnProperty(k)) { - params.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k])); - } - } - var newLoc = '?src=' + ref + ';action=' + action + ';' + params.join(';'); - window.location = newLoc; - } - - function requestRefresh(e) { - jax("refresh", null); - } - - function handleRefresh(processTable) { - $('#processTable').html(processTable); - initProcessTableButtons(); - } - - function requestKill(e) { - var button = $(e.currentTarget); - jax("kill", {name: button.data("process-name")}); - } - - function requestEnable(e) { - var button = $(e.currentTarget); - jax("enable", {name: button.data("process-name")}); - } - - function requestDisable(e) { - var button = $(e.currentTarget); - jax("disable", {name: button.data("process-name")}); - } - - function initProcessTableButtons() { - $(".kill-btn").on("click", requestKill); - $(".enable-btn").on("click", requestEnable); - $(".disable-btn").on("click", requestDisable); - } - - window.setRef = setRef; - window.handleRefresh = handleRefresh; - - $(function() { - initProcessTableButtons(); - $('#btn-refresh').on("click", requestRefresh); - }); -}(jQuery)); \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/processSchedulerView.dm b/code/controllers/ProcessScheduler/test/processSchedulerView.dm deleted file mode 100644 index ae78b3f0154..00000000000 --- a/code/controllers/ProcessScheduler/test/processSchedulerView.dm +++ /dev/null @@ -1,94 +0,0 @@ -/datum/processSchedulerView - -/datum/processSchedulerView/Topic(href, href_list) - if (!href_list["action"]) - return - - switch (href_list["action"]) - if ("kill") - var/toKill = href_list["name"] - processScheduler.killProcess(toKill) - refreshProcessTable() - if ("enable") - var/toEnable = href_list["name"] - processScheduler.enableProcess(toEnable) - refreshProcessTable() - if ("disable") - var/toDisable = href_list["name"] - processScheduler.disableProcess(toDisable) - refreshProcessTable() - if ("refresh") - refreshProcessTable() - -/datum/processSchedulerView/proc/refreshProcessTable() - windowCall("handleRefresh", getProcessTable()) - -/datum/processSchedulerView/proc/windowCall(var/function, var/data = null) - usr << output(data, "processSchedulerContext.browser:[function]") - -/datum/processSchedulerView/proc/getProcessTable() - var/text = "" - // and the context of each - for (var/list/data in processScheduler.getStatusData()) - text += "" - text += "" - text += "" - text += "" - text += "" - text += "" - text += "" - text += "" - text += "" - text += "" - - text += "
NameAvg(s)Last(s)Highest(s)TickcountTickrateStateAction
[data["name"]][num2text(data["averageRunTime"]/10,3)][num2text(data["lastRunTime"]/10,3)][num2text(data["highestRunTime"]/10,3)][num2text(data["ticks"],4)][data["schedule"]][data["status"]]" - if (data["disabled"]) - text += "" - else - text += "" - text += "
" - return text - -/** - * getContext - * Outputs an interface showing stats for all processes. - */ -/datum/processSchedulerView/proc/getContext() - bootstrap_browse() - usr << browse('processScheduler.js', "file=processScheduler.js;display=0") - - var/text = {" - Process Scheduler Detail - - [bootstrap_includes()] - - - -

Process Scheduler

-
- -
- -

The process scheduler controls [processScheduler.getProcessCount()] loops.

"} - - text += "
" - text += getProcessTable() - text += "
" - - usr << browse(text, "window=processSchedulerContext;size=800x600") - -/datum/processSchedulerView/proc/bootstrap_browse() - usr << browse('bower_components/jquery/dist/jquery.min.js', "file=jquery.min.js;display=0") - usr << browse('bower_components/bootstrap2.3.2/bootstrap/js/bootstrap.min.js', "file=bootstrap.min.js;display=0") - usr << browse('bower_components/bootstrap2.3.2/bootstrap/css/bootstrap.min.css', "file=bootstrap.min.css;display=0") - usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings-white.png', "file=glyphicons-halflings-white.png;display=0") - usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings.png', "file=glyphicons-halflings.png;display=0") - usr << browse('bower_components/json2/json2.js', "file=json2.js;display=0") - -/datum/processSchedulerView/proc/bootstrap_includes() - return {" - - - - - "} diff --git a/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm b/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm deleted file mode 100644 index d08ec46c7da..00000000000 --- a/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm +++ /dev/null @@ -1,27 +0,0 @@ -/** - * testDyingUpdateQueueProcess - * This process is an example of a process using an updateQueue. - * The datums updated by this process behave badly and block the update loop - * by sleeping. If you #define UPDATE_QUEUE_DEBUG, you will see the updateQueue - * killing off its worker processes and spawning new ones to work around slow - * updates. This means that if you have a code path that sleeps for a long time - * in mob.Life once in a blue moon, the mob update loop will not hang. - */ -/datum/slowTestDatum/proc/wackyUpdateProcessName() - sleep(rand(0,20)) // Randomly REALLY slow :| - -/datum/controller/process/testDyingUpdateQueueProcess - var/tmp/datum/updateQueue/updateQueueInstance - var/tmp/list/testDatums = list() - -/datum/controller/process/testDyingUpdateQueueProcess/setup() - name = "Dying UpdateQueue Process" - schedule_interval = 30 // every 3 seconds - updateQueueInstance = new - for(var/i = 1, i < 30, i++) - testDatums.Add(new /datum/slowTestDatum) - -/datum/controller/process/testDyingUpdateQueueProcess/doWork() - updateQueueInstance.init(testDatums, "wackyUpdateProcessName") - updateQueueInstance.Run() - \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/testHarness.dm b/code/controllers/ProcessScheduler/test/testHarness.dm deleted file mode 100644 index 2b5f1dff813..00000000000 --- a/code/controllers/ProcessScheduler/test/testHarness.dm +++ /dev/null @@ -1,35 +0,0 @@ -/* - These are simple defaults for your project. - */ -#define DEBUG - -var/global/datum/processSchedulerView/processSchedulerView - -world - loop_checks = 0 - New() - ..() - processScheduler = new - processSchedulerView = new - -mob - step_size = 8 - - New() - ..() - - - verb - startProcessScheduler() - set name = "Start Process Scheduler" - processScheduler.setup() - processScheduler.start() - - getProcessSchedulerContext() - set name = "Get Process Scheduler Status Panel" - processSchedulerView.getContext() - - runUpdateQueueTests() - set name = "Run Update Queue Testsuite" - var/datum/updateQueueTests/t = new - t.runTests() \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/testHungProcess.dm b/code/controllers/ProcessScheduler/test/testHungProcess.dm deleted file mode 100644 index ced05dd4d70..00000000000 --- a/code/controllers/ProcessScheduler/test/testHungProcess.dm +++ /dev/null @@ -1,15 +0,0 @@ -/** - * testHungProcess - * This process is an example of a simple update loop process that hangs. - */ - -/datum/controller/process/testHungProcess/setup() - name = "Hung Process" - schedule_interval = 30 // every 3 seconds - -/datum/controller/process/testHungProcess/doWork() - sleep(1000) // FUCK - // scheck is also responsible for handling hung processes. If a process - // hangs, and later resumes, but has already been killed by the scheduler, - // scheck will force the process to bail out. - scheck() \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/testNiceProcess.dm b/code/controllers/ProcessScheduler/test/testNiceProcess.dm deleted file mode 100644 index aa921bc62fa..00000000000 --- a/code/controllers/ProcessScheduler/test/testNiceProcess.dm +++ /dev/null @@ -1,13 +0,0 @@ -/** - * testNiceProcess - * This process is an example of a simple update loop process that is - * relatively fast. - */ - -/datum/controller/process/testNiceProcess/setup() - name = "Nice Process" - schedule_interval = 10 // every second - -/datum/controller/process/testNiceProcess/doWork() - sleep(rand(1,5)) // Just to pretend we're doing something - \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/testSlowProcess.dm b/code/controllers/ProcessScheduler/test/testSlowProcess.dm deleted file mode 100644 index b7c9e6e21e8..00000000000 --- a/code/controllers/ProcessScheduler/test/testSlowProcess.dm +++ /dev/null @@ -1,28 +0,0 @@ -/** - * testSlowProcess - * This process is an example of a simple update loop process that is slow. - * The update loop here sleeps inside to provide an example, but if you had - * a computationally intensive loop process that is simply slow, you can use - * scheck() inside the loop to force it to yield periodically according to - * the sleep_interval var. By default, scheck will cause a loop to sleep every - * 2 ticks. - */ - -/datum/controller/process/testSlowProcess/setup() - name = "Slow Process" - schedule_interval = 30 // every 3 seconds - -/datum/controller/process/testSlowProcess/doWork() - // set background = 1 will cause loop constructs to sleep periodically, - // whenever the BYOND scheduler deems it productive to do so. - // This behavior is not always sufficient, nor is it always consistent. - // Rather than leaving it up to the BYOND scheduler, we can control it - // ourselves and leave nothing to the black box. - set background = 1 - - for(var/i=1,i<30,i++) - // Just to pretend we're doing something here - sleep(rand(3, 5)) - - // Forces this loop to yield(sleep) periodically. - scheck() \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/test/testUpdateQueue.dm b/code/controllers/ProcessScheduler/test/testUpdateQueue.dm deleted file mode 100644 index 07b64e927f3..00000000000 --- a/code/controllers/ProcessScheduler/test/testUpdateQueue.dm +++ /dev/null @@ -1,209 +0,0 @@ -var/global/list/updateQueueTestCount = list() - -/datum/updateQueueTests - var/start - proc - runTests() - world << "Running 9 tests..." - testUpdateQueuePerformance() - sleep(1) - testInplace() - sleep(1) - testInplaceUpdateQueuePerformance() - sleep(1) - testUpdateQueueReinit() - sleep(1) - testCrashingQueue() - sleep(1) - testEmptyQueue() - sleep(1) - testManySlowItemsInQueue() - sleep(1) - testVariableWorkerTimeout() - sleep(1) - testReallySlowItemInQueue() - sleep(1) - world << "Finished!" - - beginTiming() - start = world.time - - endTiming(text) - var/time = (world.time - start) / world.tick_lag - world << {"Performance - [text] - [time] ticks"} - - getCount() - return updateQueueTestCount[updateQueueTestCount.len] - - incrementTestCount() - updateQueueTestCount.len++ - updateQueueTestCount[updateQueueTestCount.len] = 0 - - assertCountEquals(count, text) - assertThat(getCount() == count, text) - - assertCountLessThan(count, text) - assertThat(getCount() < count, text) - - assertCountGreaterThan(count, text) - assertThat(getCount() > count, text) - - assertThat(condition, text) - if (condition) - world << {"PASS: [text]"} - else - world << {"FAIL: [text]"} - - testUpdateQueuePerformance() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=100000,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - - var/datum/updateQueue/uq = new(objs) - - beginTiming() - uq.Run() - endTiming("updating 100000 simple objects") - - assertCountEquals(100000, "test that update queue updates all objects expected") - del(objs) - del(uq) - - testUpdateQueueReinit() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=100,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - - var/datum/updateQueue/uq = new(objs) - uq.Run() - objs = new - - for(var/i=1,i<=100,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - uq.init(objs) - uq.Run() - assertCountEquals(200, "test that update queue reinitializes properly and updates all objects as expected.") - del(objs) - del(uq) - - testInplace() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=100,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - var/datum/updateQueue/uq = new(objects = objs, inplace = 1) - uq.Run() - assertThat(objs.len == 0, "test that update queue inplace option really works inplace") - assertCountEquals(100, "test that inplace update queue updates the right number of objects") - del(objs) - del(uq) - - testInplaceUpdateQueuePerformance() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=100000,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - - var/datum/updateQueue/uq = new(objs) - - beginTiming() - uq.Run() - endTiming("updating 100000 simple objects in place") - del(objs) - del(uq) - - testCrashingQueue() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=10,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - objs.Add(new /datum/uqTestDatum/crasher(updateQueueTestCount.len)) - for(var/i=1,i<=10,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - - var/datum/updateQueue/uq = new(objs) - uq.Run() - assertCountEquals(20, "test that update queue handles crashed update procs OK") - del(objs) - del(uq) - - testEmptyQueue() - incrementTestCount() - var/list/objs = new - var/datum/updateQueue/uq = new(objs) - uq.Run() - assertCountEquals(0, "test that update queue doesn't barf on empty lists") - del(objs) - del(uq) - - testManySlowItemsInQueue() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=30,i++) - objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len)) - var/datum/updateQueue/uq = new(objs) - uq.Run() - assertCountEquals(30, "test that update queue slows down execution if too many objects are slow to update") - del(objs) - del(uq) - - testVariableWorkerTimeout() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=20,i++) - objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len)) - var/datum/updateQueue/uq = new(objs, workerTimeout=6) - uq.Run() - assertCountEquals(20, "test that variable worker timeout works properly") - del(objs) - del(uq) - - testReallySlowItemInQueue() - incrementTestCount() - var/list/objs = new - for(var/i=1,i<=10,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - objs.Add(new /datum/uqTestDatum/reallySlow(updateQueueTestCount.len)) - for(var/i=1,i<=10,i++) - objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len)) - var/datum/updateQueue/uq = new(objs) - uq.Run() - assertCountEquals(20, "test that update queue skips objects that are too slow to update") - del(objs) - del(uq) - - - -datum/uqTestDatum - var/testNum - New(testNum) - ..() - src.testNum = testNum - proc/update() - updateQueueTestCount[testNum]++ - proc/lag(cycles) - set background = 1 - for(var/i=0,i 20) EG.dismantle() - scheck() + SCHECK /datum/controller/process/air_system/proc/setup_overlays() plmaster = new /obj/effect/overlay() diff --git a/code/controllers/Processes/alarm.dm b/code/controllers/Processes/alarm.dm index 6530df1e135..bf2d501ae7a 100644 --- a/code/controllers/Processes/alarm.dm +++ b/code/controllers/Processes/alarm.dm @@ -1,6 +1,35 @@ +// We manually initialize the alarm handlers instead of looping over all existing types +// to make it possible to write: camera.triggerAlarm() rather than alarm_manager.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. +/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() +/var/global/datum/alarm_handler/camera/camera_alarm = new() +/var/global/datum/alarm_handler/fire/fire_alarm = new() +/var/global/datum/alarm_handler/motion/motion_alarm = new() +/var/global/datum/alarm_handler/power/power_alarm = new() + +// Alarm Manager, the manager for alarms. +var/datum/controller/process/alarm/alarm_manager + +/datum/controller/process/alarm + var/list/datum/alarm/all_handlers + /datum/controller/process/alarm/setup() name = "alarm" schedule_interval = 20 // every 2 seconds + all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) + alarm_manager = src /datum/controller/process/alarm/doWork() - alarm_manager.fire() + for(var/datum/alarm_handler/AH in all_handlers) + AH.process() + +/datum/controller/process/alarm/proc/active_alarms() + var/list/all_alarms = new + for(var/datum/alarm_handler/AH in all_handlers) + var/list/alarms = AH.alarms + all_alarms += alarms + + return all_alarms + +/datum/controller/process/alarm/proc/number_of_active_alarms() + var/list/alarms = active_alarms() + return alarms.len diff --git a/code/controllers/Processes/bot.dm b/code/controllers/Processes/bot.dm index 618c5ae90b8..dceb5504b67 100644 --- a/code/controllers/Processes/bot.dm +++ b/code/controllers/Processes/bot.dm @@ -1,5 +1,3 @@ -/datum/controller/process/bot - /datum/controller/process/bot/setup() name = "bot" schedule_interval = 20 // every 2 seconds @@ -14,7 +12,8 @@ stat(null, "[aibots && aibots.len] bots") /datum/controller/process/bot/doWork() - for(var/obj/machinery/bot/B in aibots) + for(last_object in aibots) + var/obj/machinery/bot/B = last_object if(istype(B) && isnull(B.gcDestroyed)) // Some bots sleep when they process, but there's not many bots, so just spawn them off spawn(-1) @@ -22,7 +21,7 @@ B.bot_process() catch(var/exception/e) catchException(e, B) - // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. - src.scheck() + SCHECK else - aibots -= B \ No newline at end of file + catchBadType(B) + aibots -= B diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/Processes/inactivity.dm index e4cc04e4871..57e82348572 100644 --- a/code/controllers/Processes/inactivity.dm +++ b/code/controllers/Processes/inactivity.dm @@ -11,6 +11,6 @@ C << "You have been inactive for more than 10 minutes and have been disconnected." del(C) - scheck() + SCHECK #undef INACTIVITY_KICK*/ diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm index dd0a1e928f6..7d463abbd7f 100644 --- a/code/controllers/Processes/lighting.dm +++ b/code/controllers/Processes/lighting.dm @@ -7,6 +7,9 @@ var/global/datum/controller/process/lighting/lighting_controller lighting_controller = src create_lighting_overlays() + // Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load. + spawn(300) + doWork() /datum/controller/process/lighting/statProcess() ..() diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm index 3e450c84b9f..8074578b323 100644 --- a/code/controllers/Processes/machinery.dm +++ b/code/controllers/Processes/machinery.dm @@ -19,22 +19,22 @@ /datum/controller/process/machinery/proc/process_sort() if(machinery_sort_required) machinery_sort_required = 0 - machines = dd_sortedObjectList(machines) + machines = dd_sortedObjectList(machines) /datum/controller/process/machinery/proc/process_machines() - for(var/obj/machinery/M in machines) - if(M && isnull(M.gcDestroyed)) + for(last_object in machines) + var/obj/machinery/M = last_object + if(istype(M) && isnull(M.gcDestroyed)) #ifdef PROFILE_MACHINES var/time_start = world.timeofday #endif try if(M.process() == PROCESS_KILL) - //M.inMachineList = 0 We don't use this debugging function machines.Remove(M) continue - if(M && M.use_power) + if(M.use_power) M.auto_use_power() catch(var/exception/e) catchException(e, M) @@ -48,19 +48,20 @@ machine_profiling[M.type] += (time_end - time_start) #endif else + catchBadType(M) machines -= M - scheck() + SCHECK_EVERY(100) /datum/controller/process/machinery/proc/process_power() - for(var/datum/powernet/powerNetwork in powernets) + for(last_object in powernets) + var/datum/powernet/powerNetwork = last_object if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed)) try powerNetwork.reset() catch(var/exception/e) catchException(e, powerNetwork) - // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. - src.scheck() + SCHECK continue powernets.Remove(powerNetwork) @@ -70,4 +71,4 @@ for(var/obj/item/I in processing_power_items) if(!I.pwr_drain()) // 0 = Process Kill, remove from processing list. processing_power_items.Remove(I) - scheck() + SCHECK diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm index ea2beb24dd4..be630a68fd8 100644 --- a/code/controllers/Processes/mob.dm +++ b/code/controllers/Processes/mob.dm @@ -18,15 +18,16 @@ stat(null, "[mob_list.len] mobs") /datum/controller/process/mob/doWork() - for(var/mob/M in mob_list) + for(last_object in mob_list) + var/mob/M = last_object if(istype(M) && isnull(M.gcDestroyed)) try M.Life() catch(var/exception/e) catchException(e, M) - // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. - src.scheck() + SCHECK else + catchBadType(M) mob_list -= M mob_master.process() diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm index 83b4b21aa89..1667af943da 100644 --- a/code/controllers/Processes/nanoui.dm +++ b/code/controllers/Processes/nanoui.dm @@ -1,5 +1,3 @@ -/datum/controller/process/nanoui - /datum/controller/process/nanoui/setup() name = "nanoui" schedule_interval = 20 // every 2 seconds @@ -9,11 +7,13 @@ stat(null, "[nanomanager.processing_uis.len] UIs") /datum/controller/process/nanoui/doWork() - for(var/datum/nanoui/NUI in nanomanager.processing_uis) + for(last_object in nanomanager.processing_uis) + var/datum/nanoui/NUI = last_object if(istype(NUI) && isnull(NUI.gcDestroyed)) try NUI.process() catch(var/exception/e) catchException(e, NUI) else + catchBadType(NUI) nanomanager.processing_uis -= NUI diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm index 1559fc52b32..0e573bcc05c 100644 --- a/code/controllers/Processes/obj.dm +++ b/code/controllers/Processes/obj.dm @@ -1,6 +1,3 @@ -var/global/list/object_profiling = list() -/datum/controller/process/obj - /datum/controller/process/obj/setup() name = "obj" schedule_interval = 20 // every 2 seconds @@ -16,13 +13,16 @@ var/global/list/object_profiling = list() stat(null, "[processing_objects.len] objects") /datum/controller/process/obj/doWork() - for(var/obj/O in processing_objects) + for(last_object in processing_objects) + var/datum/O = last_object if(istype(O) && isnull(O.gcDestroyed)) try - O.process() + // Reagent datums get shoved in here, but the process proc isn't on the + // base datum type, so we just call it blindly. + O:process() catch(var/exception/e) catchException(e, O) - // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. - src.scheck() + SCHECK else - processing_objects -= O \ No newline at end of file + catchBadType(O) + processing_objects -= O diff --git a/code/controllers/Processes/pipenet.dm b/code/controllers/Processes/pipenet.dm index f76a8eeec4a..f7b01391543 100644 --- a/code/controllers/Processes/pipenet.dm +++ b/code/controllers/Processes/pipenet.dm @@ -8,14 +8,15 @@ stat(null, "[pipe_networks.len] pipe nets") /datum/controller/process/pipenet/doWork() - for(var/datum/pipe_network/pipeNetwork in pipe_networks) + for(last_object in pipe_networks) + var/datum/pipe_network/pipeNetwork = last_object if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed)) try pipeNetwork.process() catch(var/exception/e) catchException(e, pipeNetwork) - // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. - src.scheck() + SCHECK continue else + catchBadType(pipeNetwork) pipe_networks -= pipeNetwork diff --git a/code/controllers/Processes/sun.dm b/code/controllers/Processes/sun.dm index 93e626af3bb..abd2dc5433d 100644 --- a/code/controllers/Processes/sun.dm +++ b/code/controllers/Processes/sun.dm @@ -44,8 +44,17 @@ var/global/datum/controller/process/sun/sun //now tell the solar control computers to update their status and linked devices /datum/controller/process/sun/proc/update_solar_machinery() - for(var/obj/machinery/power/solar_control/SC in solars) - if(!SC.powernet) - solars.Remove(SC) - continue - SC.update() + for(last_object in solars) + var/obj/machinery/power/solar_control/SC = last_object + if(istype(SC) && isnull(SC.gcDestroyed)) + if(!SC.powernet) + solars -= SC + continue + try + SC.update() + catch(var/exception/e) + catchException(e, SC) + SCHECK + else + catchBadType(SC) + solars -= SC diff --git a/code/controllers/subsystem/alarms.dm b/code/controllers/subsystem/alarms.dm deleted file mode 100644 index 1e33fc7246b..00000000000 --- a/code/controllers/subsystem/alarms.dm +++ /dev/null @@ -1,30 +0,0 @@ -// We manually initialize the alarm handlers instead of looping over all existing types -// to make it possible to write: camera.triggerAlarm() rather than alarm_manager.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. -/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() -/var/global/datum/alarm_handler/camera/camera_alarm = new() -/var/global/datum/alarm_handler/fire/fire_alarm = new() -/var/global/datum/alarm_handler/motion/motion_alarm = new() -/var/global/datum/alarm_handler/power/power_alarm = new() - -/datum/subsystem/alarm - name = "Alarm" - var/list/datum/alarm/all_handlers - -/datum/subsystem/alarm/New() - all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) - -/datum/subsystem/alarm/fire() - for(var/datum/alarm_handler/AH in all_handlers) - AH.process() - -/datum/subsystem/alarm/proc/active_alarms() - var/list/all_alarms = new - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/subsystem/alarm/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len diff --git a/code/controllers/subsystems.dm b/code/controllers/subsystems.dm deleted file mode 100644 index 11025d8d535..00000000000 --- a/code/controllers/subsystems.dm +++ /dev/null @@ -1,46 +0,0 @@ -#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;} - -/datum/subsystem - //things you will want to define - var/name //name of the subsystem - var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values. - var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer. - - //things you will probably want to leave alone - var/can_fire = 0 //prevent fire() calls - var/last_fire = 0 //last world.time we called fire() - var/next_fire = 0 //scheduled world.time for next fire() - var/cpu = 0 //cpu-usage stats (somewhat vague) - var/cost = 0 //average time to execute - var/times_fired = 0 //number of times we have called fire() - -//used to initialize the subsystem BEFORE the map has loaded -/datum/subsystem/New() - -//previously, this would have been named 'process()' but that name is used everywhere for different things! -//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds. -//fire(), and the procs it calls, SHOULD NOT HAVE ANY SLEEP OPERATIONS in them! -//YE BE WARNED! -/datum/subsystem/proc/fire() - can_fire = 0 - -//used to initialize the subsystem AFTER the map has loaded -/datum/subsystem/proc/Initialize(start_timeofday) - var/time = (world.timeofday - start_timeofday) / 10 - var/msg = "Initialized [name] SubSystem within [time] seconds" - world << "[msg]" - world.log << msg - -//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc. -/datum/subsystem/proc/stat_entry() - stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)") - -//could be used to postpone a costly subsystem for one cycle -//for instance, during cpu intensive operations like explosions -/datum/subsystem/proc/postpone() - if(next_fire - world.time < wait) - next_fire += wait - -//usually called via datum/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash) -//should attempt to salvage what it can from the old instance of subsystem -/datum/subsystem/proc/Recover() diff --git a/code/modules/garbage collection/garbage_collector.dm b/code/modules/garbage collection/garbage_collector.dm index 3e6c6c0b967..8f2886176ce 100644 --- a/code/modules/garbage collection/garbage_collector.dm +++ b/code/modules/garbage collection/garbage_collector.dm @@ -64,7 +64,7 @@ var/global/datum/controller/process/garbage_collector/garbageCollector queue.Cut(1, 2) soft_dels++ dels_count++ - scheck() + SCHECK #ifdef GC_DEBUG #undef GC_DEBUG diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm index 926b353bbd1..916920393ba 100644 --- a/code/modules/lighting/lighting_process.dm +++ b/code/modules/lighting/lighting_process.dm @@ -3,7 +3,7 @@ var/last_overlay_count = 0 /datum/controller/process/lighting/doWork() - var/list/lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated. + var/list/lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from SCHECK don't cause things to be cut from the list without being updated. last_light_count = lighting_update_lights.len lighting_update_lights = null //Nulling it first because of http://www.byond.com/forum/?post=1854520 lighting_update_lights = list() @@ -21,7 +21,7 @@ L.force_update = 0 L.needs_update = 0 - scheck() + SCHECK var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above. last_overlay_count = lighting_update_overlays.len @@ -32,4 +32,4 @@ O.update_overlay() O.needs_update = 0 - scheck() + SCHECK diff --git a/paradise.dme b/paradise.dme index 9006739533a..34cd1cf32c5 100644 --- a/paradise.dme +++ b/paradise.dme @@ -18,6 +18,7 @@ #include "code\__DEFINES\_readme.dm" #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\atmospherics.dm" +#include "code\__DEFINES\btime.dm" #include "code\__DEFINES\clothing.dm" #include "code\__DEFINES\combat.dm" #include "code\__DEFINES\flags.dm" @@ -31,6 +32,7 @@ #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mob.dm" #include "code\__DEFINES\preferences.dm" +#include "code\__DEFINES\process_scheduler.dm" #include "code\__DEFINES\qdel.dm" #include "code\__DEFINES\sight.dm" #include "code\__DEFINES\stat.dm" @@ -145,7 +147,6 @@ #include "code\controllers\hooks.dm" #include "code\controllers\master_controller.dm" #include "code\controllers\shuttle_controller.dm" -#include "code\controllers\subsystems.dm" #include "code\controllers\verbs.dm" #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.dm" @@ -166,11 +167,9 @@ #include "code\controllers\Processes\supply.dm" #include "code\controllers\Processes\ticker.dm" #include "code\controllers\Processes\vote.dm" -#include "code\controllers\ProcessScheduler\core\_define.dm" #include "code\controllers\ProcessScheduler\core\_stubs.dm" #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" -#include "code\controllers\subsystem\alarms.dm" #include "code\datums\ai_laws.dm" #include "code\datums\browser.dm" #include "code\datums\cargoprofile.dm"