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..a80446d441c
--- /dev/null
+++ b/code/__DEFINES/btime.dm
@@ -0,0 +1,18 @@
+// Comment this out if the external btime library is unavailable
+#define PRECISE_TIMER_AVAILABLE
+
+#ifdef PRECISE_TIMER_AVAILABLE
+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/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 5275cd7ca91..b2ddadd7404 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -53,5 +53,3 @@ var/global/list/med_hud_users = list()
var/global/list/sec_hud_users = list()
var/global/list/antag_hud_users = list()
//items that ask to be called every cycle
-
-var/global/list/active_diseases = list() //Diseases are mob-based, so they get to go here
\ No newline at end of file
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 89048113d30..c09b5730b69 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
@@ -85,26 +85,33 @@
var/tmp/last_object
-datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
+ // Counts the number of times an exception has occurred; gets reset after 10
+ var/tmp/list/exceptions = list()
+
+ // Number of deciseconds to delay before starting the process
+ var/start_delay = 0
+
+/datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
..()
main = scheduler
previousStatus = "idle"
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
last_task = 0
last_object = null
-datum/controller/process/proc/started()
+/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
@@ -114,65 +121,65 @@ datum/controller/process/proc/started()
onStart()
-datum/controller/process/proc/finished()
+/datum/controller/process/proc/finished()
ticks++
idle()
main.processFinished(src)
onFinish()
-datum/controller/process/proc/doWork()
+/datum/controller/process/proc/doWork()
-datum/controller/process/proc/setup()
+/datum/controller/process/proc/setup()
-datum/controller/process/proc/process()
+/datum/controller/process/proc/process()
started()
doWork()
finished()
-datum/controller/process/proc/running()
+/datum/controller/process/proc/running()
idle = 0
queued = 0
running = 1
hung = 0
setStatus(PROCESS_STATUS_RUNNING)
-datum/controller/process/proc/idle()
+/datum/controller/process/proc/idle()
queued = 0
running = 0
idle = 1
hung = 0
setStatus(PROCESS_STATUS_IDLE)
-datum/controller/process/proc/queued()
+/datum/controller/process/proc/queued()
idle = 0
running = 0
queued = 1
hung = 0
setStatus(PROCESS_STATUS_QUEUED)
-datum/controller/process/proc/hung()
+/datum/controller/process/proc/hung()
hung = 1
setStatus(PROCESS_STATUS_HUNG)
-datum/controller/process/proc/handleHung()
+/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)
main.restartProcess(src.name)
-datum/controller/process/proc/kill()
+/datum/controller/process/proc/kill()
if (!killed)
var/msg = "[name] process was killed at tick #[ticks]."
logTheThing("debug", null, null, msg)
@@ -182,60 +189,68 @@ datum/controller/process/proc/kill()
// 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 * 10)
- sleep(1)
+ if (main.getCurrentTickElapsedTime() > main.timeAllowance)
+ sleep(world.tick_lag)
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()
+/datum/controller/process/proc/update()
// Clear delta
if(previousStatus != status)
setStatus(status)
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
-datum/controller/process/proc/tickDetail()
+/datum/controller/process/proc/getElapsedTime()
+ var/timeofhour = TimeOfHour
+ if (timeofhour < run_start)
+ return timeofhour - (run_start - 36000)
+ return timeofhour - run_start
+
+/datum/controller/process/proc/tickDetail()
return
-datum/controller/process/proc/getContext()
+/datum/controller/process/proc/getContext()
return "
[name]
[main.averageRunTime(src)]
[main.last_run_time[src]]
[main.highest_run_time[src]]
[ticks]
\n"
-datum/controller/process/proc/getContextData()
+/datum/controller/process/proc/getContextData()
return list(
"name" = name,
"averageRunTime" = main.averageRunTime(src),
@@ -247,10 +262,10 @@ datum/controller/process/proc/getContextData()
"disabled" = disabled
)
-datum/controller/process/proc/getStatus()
+/datum/controller/process/proc/getStatus()
return status
-datum/controller/process/proc/getStatusText(var/s = 0)
+/datum/controller/process/proc/getStatusText(var/s = 0)
if(!s)
s = status
switch(s)
@@ -269,21 +284,21 @@ datum/controller/process/proc/getStatusText(var/s = 0)
else
return "UNKNOWN"
-datum/controller/process/proc/getPreviousStatus()
+/datum/controller/process/proc/getPreviousStatus()
return previousStatus
-datum/controller/process/proc/getPreviousStatusText()
+/datum/controller/process/proc/getPreviousStatusText()
return getStatusText(previousStatus)
-datum/controller/process/proc/setStatus(var/newStatus)
+/datum/controller/process/proc/setStatus(var/newStatus)
previousStatus = status
status = newStatus
-datum/controller/process/proc/setLastTask(var/task, var/object)
+/datum/controller/process/proc/setLastTask(var/task, var/object)
last_task = task
last_object = object
-datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target)
+/datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target)
main = target.main
name = target.name
schedule_interval = target.schedule_interval
@@ -296,22 +311,62 @@ datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target
last_object = target.last_object
copyStateFrom(target)
-datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
+/datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
-datum/controller/process/proc/onKill()
+/datum/controller/process/proc/onKill()
-datum/controller/process/proc/onStart()
+/datum/controller/process/proc/onStart()
-datum/controller/process/proc/onFinish()
+/datum/controller/process/proc/onFinish()
-datum/controller/process/proc/disable()
+/datum/controller/process/proc/disable()
disabled = 1
-datum/controller/process/proc/enable()
+/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)
+/datum/controller/process/proc/getHighestRunTime()
+ return main.getProcessHighestRunTime(src)
+
/datum/controller/process/proc/getTicks()
return ticks
+
+/datum/controller/process/proc/statProcess()
+ 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]"
+ var/eid = "[e]" // Exception ID, for tracking repeated exceptions
+ var/ptext = "" // "processing..." text, for what was being processed (if known)
+ if(istype(e))
+ etext += " in [e.file], line [e.line]"
+ eid = "[e.file]:[e.line]"
+ if(eid in exceptions)
+ if(exceptions[eid]++ >= 10)
+ return
+ else
+ exceptions[eid] = 1
+ if(istype(thrower, /datum))
+ var/datum/D = thrower
+ ptext = " processing [D.type]"
+ if(istype(thrower, /atom))
+ var/atom/A = thrower
+ ptext += " ([A]) ([A.x],[A.y],[A.z])"
+ world.log << "\[[time_stamp()]\] Process [name] caught exception[ptext]: [etext]"
+ if(exceptions[eid] >= 10)
+ 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 8bb365b2a7f..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,11 +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,6 +201,10 @@ var/global/datum/controller/processScheduler/processScheduler
nameToProcessMap[newProcess.name] = newProcess
+/datum/controller/processScheduler/proc/updateStartDelays()
+ for(var/datum/controller/process/p in processes)
+ if(p.start_delay)
+ last_queued[p] = world.time - p.start_delay
/datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process)
spawn(0)
@@ -197,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
@@ -218,21 +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.timeofday
-
- 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.timeofday
+ time = TimeOfHour
// If world.timeofday has rolled over, then we need to adjust.
if (time < last_start[process])
- last_start[process] -= 864000
+ last_start[process] -= 36000
var/lastRunTime = time - last_start[process]
@@ -273,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
@@ -310,11 +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/getIsRunning()
- return isRunning
+/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))
+
+ //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 = "
Name
Avg(s)
Last(s)
Highest(s)
Tickcount
Tickrate
State
Action
"
- // and the context of each
- for (var/list/data in processScheduler.getStatusData())
- text += "
"
- text += "
[data["name"]]
"
- text += "
[num2text(data["averageRunTime"]/10,3)]
"
- text += "
[num2text(data["lastRunTime"]/10,3)]
"
- text += "
[num2text(data["highestRunTime"]/10,3)]
"
- text += "
[num2text(data["ticks"],4)]
"
- text += "
[data["schedule"]]
"
- text += "
[data["status"]]
"
- text += "
"
- if (data["disabled"])
- text += ""
- else
- text += ""
- text += "
"
- 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,iProcessing Geometry..."
@@ -37,27 +45,36 @@ var/global/datum/controller/process/air_system/air_master
process_super_conductivity()
return 1
+/datum/controller/process/air_system/statProcess()
+ ..()
+ stat(null, "[last_active] active")
+ stat(null, "[last_excited] EG|[last_hpd] HPD|[last_asc] ASC|[last_hotspots] Hot")
+
/datum/controller/process/air_system/proc/process_hotspots()
+ last_hotspots = hotspots.len
for(var/obj/effect/hotspot/H in hotspots)
H.process()
- scheck()
+ SCHECK
/datum/controller/process/air_system/proc/process_super_conductivity()
+ last_asc = active_super_conductivity.len
for(var/turf/simulated/T in active_super_conductivity)
T.super_conduct()
- scheck()
+ SCHECK
/datum/controller/process/air_system/proc/process_high_pressure_delta()
+ last_hpd = high_pressure_delta.len
for(var/turf/T in high_pressure_delta)
T.high_pressure_movements()
T.pressure_difference = 0
- scheck()
- high_pressure_delta.len = 0
+ SCHECK
+ high_pressure_delta.Cut()
/datum/controller/process/air_system/proc/process_active_turfs()
+ last_active = active_turfs.len
for(var/turf/simulated/T in active_turfs)
T.process_cell()
- scheck()
+ SCHECK
/datum/controller/process/air_system/proc/remove_from_active(var/turf/simulated/T)
if(istype(T))
@@ -101,15 +118,16 @@ var/global/datum/controller/process/air_system/air_master
active_turfs |= T
/datum/controller/process/air_system/proc/process_excited_groups()
+ last_excited = excited_groups.len
for(var/datum/excited_group/EG in excited_groups)
EG.breakdown_cooldown ++
if(EG.breakdown_cooldown == 10)
EG.self_breakdown()
- scheck()
+ SCHECK
return
if(EG.breakdown_cooldown > 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 b93324d0901..dceb5504b67 100644
--- a/code/controllers/Processes/bot.dm
+++ b/code/controllers/Processes/bot.dm
@@ -1,20 +1,27 @@
-/datum/controller/process/bot
- var/tmp/datum/updateQueue/updateQueueInstance
-
/datum/controller/process/bot/setup()
name = "bot"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
/datum/controller/process/bot/started()
..()
- if(!updateQueueInstance)
- if(!aibots)
- aibots = list()
- else if(aibots.len)
- updateQueueInstance = new
+ if(!aibots)
+ aibots = list()
+
+/datum/controller/process/bot/statProcess()
+ ..()
+ stat(null, "[aibots && aibots.len] bots")
/datum/controller/process/bot/doWork()
- if(updateQueueInstance)
- updateQueueInstance.init(aibots, "bot_process")
- updateQueueInstance.Run()
+ 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)
+ try
+ B.bot_process()
+ catch(var/exception/e)
+ catchException(e, B)
+ SCHECK
+ else
+ catchBadType(B)
+ aibots -= B
diff --git a/code/controllers/Processes/disease.dm b/code/controllers/Processes/disease.dm
deleted file mode 100644
index a8d840097ec..00000000000
--- a/code/controllers/Processes/disease.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/datum/controller/process/disease
- var/tmp/datum/updateQueue/updateQueueInstance
-
-/datum/controller/process/disease/setup()
- name = "disease"
- schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
-
-/datum/controller/process/disease/doWork()
- updateQueueInstance.init(active_diseases, "process")
- updateQueueInstance.Run()
diff --git a/code/controllers/Processes/emergencyShuttle.dm b/code/controllers/Processes/emergencyShuttle.dm
index e7289311b95..efd537bb062 100644
--- a/code/controllers/Processes/emergencyShuttle.dm
+++ b/code/controllers/Processes/emergencyShuttle.dm
@@ -1,5 +1,5 @@
/datum/controller/process/emergencyShuttle/setup()
- name = "emergency shuttle"
+ name = "e-shuttle"
schedule_interval = 20 // every 2 seconds
if(!emergency_shuttle)
diff --git a/code/controllers/Processes/garbage.dm b/code/controllers/Processes/garbage.dm
index 9686bcd42b3..2e53be6414d 100644
--- a/code/controllers/Processes/garbage.dm
+++ b/code/controllers/Processes/garbage.dm
@@ -1,9 +1,15 @@
-/datum/controller/process/garbage/setup()
+/datum/controller/process/garbage_collector/setup()
name = "garbage"
- schedule_interval = 20 // every 2 seconds
+ schedule_interval = 10
+ start_delay = 3
- if(!garbageCollector)
- garbageCollector = new
+ garbageCollector = src
-/datum/controller/process/garbage/doWork()
- garbageCollector.process()
\ No newline at end of file
+/datum/controller/process/garbage_collector/doWork()
+ // Garbage collection code can be found in code\modules\garbage collection\garbage_collector.dm
+ processGarbage()
+
+/datum/controller/process/garbage_collector/statProcess()
+ ..()
+ stat(null, "[del_everything ? "Off" : "On"], [queue.len] queued")
+ stat(null, "Dels: [dels_count], [soft_dels] soft, [hard_dels] hard")
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
new file mode 100644
index 00000000000..7d463abbd7f
--- /dev/null
+++ b/code/controllers/Processes/lighting.dm
@@ -0,0 +1,18 @@
+var/global/datum/controller/process/lighting/lighting_controller
+
+/datum/controller/process/lighting/setup()
+ name = "lighting"
+ schedule_interval = LIGHTING_INTERVAL
+ start_delay = 1
+ 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()
+ ..()
+ stat(null, "[last_light_count] lights, [last_overlay_count] overlays")
+
+// Lighting process code located in modules\lighting\lighting_process.dm
\ No newline at end of file
diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm
index 9d069101f54..8074578b323 100644
--- a/code/controllers/Processes/machinery.dm
+++ b/code/controllers/Processes/machinery.dm
@@ -3,6 +3,12 @@
/datum/controller/process/machinery/setup()
name = "machinery"
schedule_interval = 20 // every 2 seconds
+ start_delay = 12
+
+/datum/controller/process/machinery/statProcess()
+ ..()
+ stat(null, "[machines.len] machines")
+ stat(null, "[powernets.len] powernets")
/datum/controller/process/machinery/doWork()
process_sort()
@@ -13,22 +19,25 @@
/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
- if(M.process() == PROCESS_KILL)
- //M.inMachineList = 0 We don't use this debugging function
- machines.Remove(M)
- continue
+ try
+ if(M.process() == PROCESS_KILL)
+ machines.Remove(M)
+ continue
- if(M && M.use_power)
- M.auto_use_power()
+ if(M.use_power)
+ M.auto_use_power()
+ catch(var/exception/e)
+ catchException(e, M)
#ifdef PROFILE_MACHINES
var/time_end = world.timeofday
@@ -39,15 +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))
- powerNetwork.reset()
- scheck()
+ try
+ powerNetwork.reset()
+ catch(var/exception/e)
+ catchException(e, powerNetwork)
+ SCHECK
continue
powernets.Remove(powerNetwork)
@@ -57,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 4be567c4ef2..be630a68fd8 100644
--- a/code/controllers/Processes/mob.dm
+++ b/code/controllers/Processes/mob.dm
@@ -1,27 +1,35 @@
/datum/controller/process/mob
- var/tmp/datum/updateQueue/updateQueueInstance
/datum/controller/process/mob/setup()
name = "mob"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
+ start_delay = 16
if(!mob_master)
mob_master = new
mob_master.Setup()
/datum/controller/process/mob/started()
..()
- if(!updateQueueInstance)
- if(!mob_list)
- mob_list = list()
- else if(mob_list.len)
- updateQueueInstance = new
+ if(!mob_list)
+ mob_list = list()
+
+/datum/controller/process/mob/statProcess()
+ ..()
+ stat(null, "[mob_list.len] mobs")
/datum/controller/process/mob/doWork()
- if(updateQueueInstance)
- updateQueueInstance.init(mob_list, "Life")
- updateQueueInstance.Run()
- mob_master.process()
+ 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)
+ SCHECK
+ else
+ catchBadType(M)
+ mob_list -= M
+ mob_master.process()
var/global/datum/controller/mob_system/mob_master
diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm
index c8396bcab87..1667af943da 100644
--- a/code/controllers/Processes/nanoui.dm
+++ b/code/controllers/Processes/nanoui.dm
@@ -1,11 +1,19 @@
-/datum/controller/process/nanoui
- var/tmp/datum/updateQueue/updateQueueInstance
-
/datum/controller/process/nanoui/setup()
name = "nanoui"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
+
+/datum/controller/process/nanoui/statProcess()
+ ..()
+ stat(null, "[nanomanager.processing_uis.len] UIs")
/datum/controller/process/nanoui/doWork()
- updateQueueInstance.init(nanomanager.processing_uis, "process")
- updateQueueInstance.Run()
+ 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 15ad98dd3d6..0e573bcc05c 100644
--- a/code/controllers/Processes/obj.dm
+++ b/code/controllers/Processes/obj.dm
@@ -1,21 +1,28 @@
-var/global/list/object_profiling = list()
-/datum/controller/process/obj
- var/tmp/datum/updateQueue/updateQueueInstance
-
/datum/controller/process/obj/setup()
name = "obj"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
+ start_delay = 8
/datum/controller/process/obj/started()
..()
- if(!updateQueueInstance)
- if(!processing_objects)
- processing_objects = list()
- else if(processing_objects.len)
- updateQueueInstance = new
+ if(!processing_objects)
+ processing_objects = list()
+
+/datum/controller/process/obj/statProcess()
+ ..()
+ stat(null, "[processing_objects.len] objects")
/datum/controller/process/obj/doWork()
- if(updateQueueInstance)
- updateQueueInstance.init(processing_objects, "process")
- updateQueueInstance.Run()
+ for(last_object in processing_objects)
+ var/datum/O = last_object
+ if(istype(O) && isnull(O.gcDestroyed))
+ try
+ // 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)
+ SCHECK
+ else
+ catchBadType(O)
+ processing_objects -= O
diff --git a/code/controllers/Processes/pipenet.dm b/code/controllers/Processes/pipenet.dm
index b59373a88a1..f7b01391543 100644
--- a/code/controllers/Processes/pipenet.dm
+++ b/code/controllers/Processes/pipenet.dm
@@ -1,12 +1,22 @@
/datum/controller/process/pipenet/setup()
name = "pipenet"
schedule_interval = 20 // every 2 seconds
+ start_delay = 18
+
+/datum/controller/process/pipenet/statProcess()
+ ..()
+ 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))
- pipeNetwork.process()
- scheck()
+ try
+ pipeNetwork.process()
+ catch(var/exception/e)
+ catchException(e, pipeNetwork)
+ SCHECK
continue
else
+ catchBadType(pipeNetwork)
pipe_networks -= pipeNetwork
diff --git a/code/controllers/Processes/shuttle.dm b/code/controllers/Processes/shuttle.dm
index a10586d76d9..df0e5fa1ae2 100644
--- a/code/controllers/Processes/shuttle.dm
+++ b/code/controllers/Processes/shuttle.dm
@@ -1,5 +1,5 @@
/datum/controller/process/Shuttle/setup()
- name = "shuttle controller"
+ name = "shuttle"
schedule_interval = 20 // every 2 seconds
if(!shuttle_controller)
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/Processes/supply.dm b/code/controllers/Processes/supply.dm
index 891a511ec6d..aa4ba1e52ab 100644
--- a/code/controllers/Processes/supply.dm
+++ b/code/controllers/Processes/supply.dm
@@ -1,5 +1,5 @@
/datum/controller/process/supply/setup()
- name = "supply controller"
+ name = "supply"
schedule_interval = 300 // every 30 seconds
/datum/controller/process/supply/doWork()
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 137eeb3aff1..3ecef1fa27a 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -20,8 +20,6 @@ var/global/pipe_processing_killed = 0
datum/controller/game_controller
var/list/shuttle_list // For debugging and VV
- var/global/datum/garbage_collector/garbageCollector
-
datum/controller/game_controller/New()
//There can be only one master_controller. Out with the old and in with the new.
if(master_controller != src)
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 97d67c30ac8..41c01dc5672 100644
--- a/code/modules/garbage collection/garbage_collector.dm
+++ b/code/modules/garbage collection/garbage_collector.dm
@@ -2,21 +2,21 @@
// For general information about how the GC works and how to use it, see __gc_info.dm
-#define GC_COLLECTIONS_PER_TICK 300 // Was 100.
+#define GC_COLLECTIONS_PER_TICK 150 // Was 100.
#define GC_COLLECTION_TIMEOUT (30 SECONDS)
-#define GC_FORCE_DEL_PER_TICK 60
+#define GC_FORCE_DEL_PER_TICK 30
//#define GC_DEBUG
// A list of types that were queued in the GC, and had to be soft deleted; used in testing
var/list/gc_hard_del_types = list()
-var/datum/garbage_collector/garbageCollector
+var/global/datum/controller/process/garbage_collector/garbageCollector
// The time a datum was destroyed by the GC, or null if it hasn't been
/datum/var/gcDestroyed
// Whether a datum was hard-deleted by the GC; 0 if not, 1 if it was queued, -1 if directly deleted
/datum/var/hard_deleted = 0
-/datum/garbage_collector
+/datum/controller/process/garbage_collector
var/list/queue = new
var/del_everything = 0
@@ -25,7 +25,7 @@ var/datum/garbage_collector/garbageCollector
var/hard_dels = 0
var/soft_dels = 0
-/datum/garbage_collector/proc/addTrash(var/datum/D)
+/datum/controller/process/garbage_collector/proc/addTrash(var/datum/D)
if(!istype(D) || del_everything)
del(D)
hard_dels++
@@ -35,7 +35,7 @@ var/datum/garbage_collector/garbageCollector
queue -= "\ref[D]" // If this is a re-used ref, remove the old ref from the queue
queue["\ref[D]"] = world.time
-/datum/garbage_collector/proc/process()
+/datum/controller/process/garbage_collector/proc/processGarbage()
var/remainingCollectionPerTick = GC_COLLECTIONS_PER_TICK
var/remainingForceDelPerTick = GC_FORCE_DEL_PER_TICK
var/collectionTimeScope = world.time - GC_COLLECTION_TIMEOUT
@@ -60,10 +60,13 @@ var/datum/garbage_collector/garbageCollector
queue.Cut(1, 2)
remainingForceDelPerTick--
+ // Sleep check more aggressively when force deleting.
+ calls_since_last_scheck += 9
else // Otherwise, it was GC'd - remove it from the queue
queue.Cut(1, 2)
soft_dels++
dels_count++
+ SCHECK
#ifdef GC_DEBUG
#undef GC_DEBUG
@@ -73,7 +76,7 @@ var/datum/garbage_collector/garbageCollector
#undef GC_COLLECTION_TIMEOUT
#undef GC_COLLECTIONS_PER_TICK
-/datum/garbage_collector/proc/hardDel(var/datum/D)
+/datum/controller/process/garbage_collector/proc/hardDel(var/datum/D)
gc_hard_del_types |= D.type
D.hard_deleted = 1
if(!D.gcDestroyed)
diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm
index ef13726cdef..916920393ba 100644
--- a/code/modules/lighting/lighting_process.dm
+++ b/code/modules/lighting/lighting_process.dm
@@ -1,14 +1,10 @@
-var/global/datum/controller/process/lighting/lighting_controller
-
-/datum/controller/process/lighting/setup()
- name = "lighting"
- lighting_controller = src
- schedule_interval = LIGHTING_INTERVAL
-
- create_lighting_overlays()
+/datum/controller/process/lighting
+ var/last_light_count = 0
+ 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()
@@ -25,9 +21,10 @@ var/global/datum/controller/process/lighting/lighting_controller
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
lighting_update_overlays = null //Same as above
lighting_update_overlays = list()
@@ -35,4 +32,4 @@ var/global/datum/controller/process/lighting/lighting_controller
O.update_overlay()
O.needs_update = 0
- scheck()
+ SCHECK
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 43cb845414d..2b94fdae71c 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -926,77 +926,12 @@ var/list/slot_equipment_priority = list( \
if(client && client.holder)
if(statpanel("DI")) //not looking at that panel
- stat(null, "Location:\t([x], [y], [z])")
- stat(null, "CPU:\t[world.cpu]")
- stat(null, "Instances:\t[world.contents.len]")
+ stat("Loc", "([x], [y], [z]) [loc]")
+ stat("CPU", "[world.cpu]")
+ stat("Instances", "[world.contents.len]")
- if (garbageCollector)
- stat(null, "\tqdel - [garbageCollector.del_everything ? "off" : "on"]")
- stat(null, "\ton queue - [garbageCollector.queue.len]")
- stat(null, "\ttotal delete - [garbageCollector.dels_count]")
- stat(null, "\tsoft delete - [garbageCollector.soft_dels]")
- stat(null, "\thard delete - [garbageCollector.hard_dels]")
- else
- stat(null, "Garbage Controller is not running.")
-
- if(processScheduler.getIsRunning())
- var/datum/controller/process/process
-
- process = processScheduler.getProcess("ticker")
- stat(null, "TIC\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("air")
- stat(null, "AIR\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("lighting")
- stat(null, "LIG\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("mob")
- stat(null, "MOB([mob_list.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("machinery")
- stat(null, "MAC([machines.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("obj")
- stat(null, "OBJ([processing_objects.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("bot")
- stat(null, "BOT([aibots.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("pipenet")
- stat(null, "PIP([pipe_networks.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("nanoui")
- stat(null, "NAN([nanomanager.processing_uis.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("disease")
- stat(null, "DIS([active_diseases.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("garbage")
- stat(null, "GAR\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- process = processScheduler.getProcess("sun")
- stat(null, "SUN([sun.solars.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("garbage")
- //stat(null, "GAR\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("vote")
- //stat(null, "VOT\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("shuttle controller")
- //stat(null, "SHT\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("emergency shuttle")
- //stat(null, "EME\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("inactivity")
- //stat(null, "IAC\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
-
- //process = processScheduler.getProcess("event")
- //stat(null, "EVE([events.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
- else
- stat(null, "processScheduler is not running.")
+ if(processScheduler)
+ processScheduler.statProcesses()
statpanel("Status") // Switch to the Status panel again, for the sake of the lazy Stat procs
diff --git a/config/news/trivial.txt b/config/news/trivial.txt
index f27ecfb791f..82b5a53c92b 100644
--- a/config/news/trivial.txt
+++ b/config/news/trivial.txt
@@ -1,4 +1,4 @@
-Tree stuck in tajaran; firefighters baffled.
+Tree stuck in tajaran; firefighters baffled.
Armadillos want aardvarks removed from dictionary claims 'here first'.
Angel found dancing on pinhead ordered to stop; cited for public nuisance.
Letters claim they are better than numbers; 'Always have been'.
diff --git a/paradise.dme b/paradise.dme
index 7d3cd4d7a8b..f90a049128f 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,17 +147,16 @@
#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"
#include "code\controllers\Processes\alarm.dm"
#include "code\controllers\Processes\bot.dm"
-#include "code\controllers\Processes\disease.dm"
#include "code\controllers\Processes\emergencyShuttle.dm"
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\garbage.dm"
#include "code\controllers\Processes\inactivity.dm"
+#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\mob.dm"
#include "code\controllers\Processes\nanoui.dm"
@@ -166,13 +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\ProcessScheduler\core\updateQueue.dm"
-#include "code\controllers\ProcessScheduler\core\updateQueueWorker.dm"
-#include "code\controllers\subsystem\alarms.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\browser.dm"
#include "code\datums\cargoprofile.dm"