From c520ebd8a50c40246fa7dabd71c419b55233aba6 Mon Sep 17 00:00:00 2001 From: Krausus Date: Sat, 18 Jul 2015 13:14:32 -0400 Subject: [PATCH 1/4] Process and Process Scheduler Tweaks --- code/_globalvars/lists/mobs.dm | 2 - .../ProcessScheduler/core/process.dm | 102 ++++++++++++------ .../ProcessScheduler/core/processScheduler.dm | 30 ++++-- code/controllers/Processes/air.dm | 27 ++++- code/controllers/Processes/bot.dm | 28 +++-- code/controllers/Processes/disease.dm | 11 -- .../controllers/Processes/emergencyShuttle.dm | 2 +- code/controllers/Processes/garbage.dm | 16 +-- code/controllers/Processes/lighting.dm | 15 +++ code/controllers/Processes/machinery.dm | 29 +++-- code/controllers/Processes/mob.dm | 29 +++-- code/controllers/Processes/nanoui.dm | 16 ++- code/controllers/Processes/obj.dm | 27 +++-- code/controllers/Processes/pipenet.dm | 13 ++- code/controllers/Processes/shuttle.dm | 2 +- code/controllers/Processes/supply.dm | 2 +- code/controllers/master_controller.dm | 4 +- code/game/machinery/doors/airlock_control.dm | 3 +- .../garbage collection/garbage_collector.dm | 11 +- code/modules/lighting/lighting_process.dm | 13 +-- code/modules/mob/mob.dm | 75 +------------ paradise.dme | 4 +- 22 files changed, 257 insertions(+), 204 deletions(-) delete mode 100644 code/controllers/Processes/disease.dm create mode 100644 code/controllers/Processes/lighting.dm diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 5de92892b30..41816d7d7d8 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -52,5 +52,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/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm index 89048113d30..0cfec361b3f 100644 --- a/code/controllers/ProcessScheduler/core/process.dm +++ b/code/controllers/ProcessScheduler/core/process.dm @@ -85,7 +85,13 @@ 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" @@ -99,7 +105,7 @@ datum/controller/process/New(var/datum/controller/processScheduler/scheduler) last_task = 0 last_object = null -datum/controller/process/proc/started() +/datum/controller/process/proc/started() // Initialize last_slept so we can know when to sleep last_slept = world.timeofday @@ -114,48 +120,48 @@ 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/datum/lastObj = last_object var/lastObjType = "null" if(istype(lastObj)) @@ -172,7 +178,7 @@ datum/controller/process/proc/handleHung() 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) @@ -187,7 +193,7 @@ datum/controller/process/proc/kill() // This should del del(src) -datum/controller/process/proc/scheck(var/tickId = 0) +/datum/controller/process/proc/scheck(var/tickId = 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 @@ -196,8 +202,8 @@ datum/controller/process/proc/scheck(var/tickId = 0) // 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 (world.cpu >= (cpu_threshold + cpu_defer_count * world.tick_lag * 10)) + sleep(world.tick_lag) cpu_defer_count++ last_slept = world.timeofday else @@ -210,7 +216,7 @@ datum/controller/process/proc/scheck(var/tickId = 0) sleep(0) last_slept = world.timeofday -datum/controller/process/proc/update() +/datum/controller/process/proc/update() // Clear delta if(previousStatus != status) setStatus(status) @@ -224,18 +230,18 @@ datum/controller/process/proc/update() else if (elapsedTime > hang_warning_time) setStatus(PROCESS_STATUS_MAYBE_HUNG) -datum/controller/process/proc/getElapsedTime() +/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/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 +253,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 +275,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 +302,52 @@ 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/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() + stat("[name]", "T#[getTicks()]|LR [getLastRunTime()]|HR [getHighestRunTime()]|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 diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm index 1be24045938..49455b45f9e 100644 --- a/code/controllers/ProcessScheduler/core/processScheduler.dm +++ b/code/controllers/ProcessScheduler/core/processScheduler.dm @@ -66,6 +66,7 @@ var/global/datum/controller/processScheduler/processScheduler /datum/controller/processScheduler/proc/start() isRunning = 1 + updateStartDelays() spawn(0) process() @@ -108,12 +109,8 @@ var/global/datum/controller/processScheduler/processScheduler if (p.disabled || p.running || p.queued || !p.idle) continue - // If world.timeofday has rolled over, then we need to adjust. - if (world.timeofday < last_start[p]) - last_start[p] -= 864000 - // If the process should be running by now, go ahead and queue it - if (world.timeofday > last_start[p] + p.schedule_interval) + if (world.time >= last_start[p] + p.schedule_interval) setQueuedProcessState(p) /datum/controller/processScheduler/proc/runQueuedProcesses() @@ -176,6 +173,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_start[p] = world.time - p.start_delay /datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process) spawn(0) @@ -222,17 +223,13 @@ var/global/datum/controller/processScheduler/processScheduler /datum/controller/processScheduler/proc/recordStart(var/datum/controller/process/process, var/time = null) if (isnull(time)) - time = world.timeofday + time = 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 - - // If world.timeofday has rolled over, then we need to adjust. - if (time < last_start[process]) - last_start[process] -= 864000 + time = world.time var/lastRunTime = time - last_start[process] @@ -316,5 +313,16 @@ var/global/datum/controller/processScheduler/processScheduler /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/getIsRunning() return isRunning + +/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])") + for(var/datum/controller/process/p in processes) + p.statProcess() diff --git a/code/controllers/Processes/air.dm b/code/controllers/Processes/air.dm index c251b913ce6..00206b1fc39 100644 --- a/code/controllers/Processes/air.dm +++ b/code/controllers/Processes/air.dm @@ -15,9 +15,17 @@ var/global/datum/controller/process/air_system/air_master var/failed_ticks = 0 var/tick_progress = 0 + // Stats + var/last_active = 0 + var/last_excited = 0 + var/last_hpd = 0 + var/last_hotspots = 0 + var/last_asc = 0 + /datum/controller/process/air_system/setup() name = "air" schedule_interval = 20 // every 2 seconds + start_delay = 4 air_master = src world << "Processing Geometry..." @@ -32,30 +40,41 @@ var/global/datum/controller/process/air_system/air_master current_cycle++ process_active_turfs() process_excited_groups() - scheck() process_high_pressure_delta() process_hotspots() process_super_conductivity() - scheck() 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() /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() /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 - 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() /datum/controller/process/air_system/proc/remove_from_active(var/turf/simulated/T) if(istype(T)) @@ -100,6 +119,7 @@ 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) @@ -107,6 +127,7 @@ var/global/datum/controller/process/air_system/air_master return if(EG.breakdown_cooldown > 20) EG.dismantle() + scheck() /datum/controller/process/air_system/proc/setup_overlays() plmaster = new /obj/effect/overlay() diff --git a/code/controllers/Processes/bot.dm b/code/controllers/Processes/bot.dm index b93324d0901..618c5ae90b8 100644 --- a/code/controllers/Processes/bot.dm +++ b/code/controllers/Processes/bot.dm @@ -1,20 +1,28 @@ /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(var/obj/machinery/bot/B in aibots) + 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) + // Use src explicitly after a try/catch, or BYOND messes src up. I have no idea why. + src.scheck() + else + aibots -= B \ No newline at end of file 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 f589c641f5b..68a01747513 100644 --- a/code/controllers/Processes/garbage.dm +++ b/code/controllers/Processes/garbage.dm @@ -1,10 +1,14 @@ -/datum/controller/process/garbage/setup() +/datum/controller/process/garbage_collector/setup() name = "garbage" schedule_interval = 20 // every 2 seconds - if(!garbageCollector) - garbageCollector = new + garbageCollector = src -/datum/controller/process/garbage/doWork() - garbageCollector.process() - scheck() \ 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/lighting.dm b/code/controllers/Processes/lighting.dm new file mode 100644 index 00000000000..dd0a1e928f6 --- /dev/null +++ b/code/controllers/Processes/lighting.dm @@ -0,0 +1,15 @@ +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() + +/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 2d8986ff2ae..39e5bb80195 100644 --- a/code/controllers/Processes/machinery.dm +++ b/code/controllers/Processes/machinery.dm @@ -1,6 +1,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_power() @@ -13,13 +19,16 @@ 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) + //M.inMachineList = 0 We don't use this debugging function + machines.Remove(M) + continue - if(M && M.use_power) - M.auto_use_power() + if(M && M.use_power) + M.auto_use_power() + catch(var/exception/e) + catchException(e, M) #ifdef PROFILE_MACHINES var/time_end = world.timeofday @@ -37,8 +46,12 @@ /datum/controller/process/machinery/proc/process_power() for(var/datum/powernet/powerNetwork in powernets) if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed)) - powerNetwork.reset() - scheck() + 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() continue else powernets -= powerNetwork diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm index 4be567c4ef2..ea2beb24dd4 100644 --- a/code/controllers/Processes/mob.dm +++ b/code/controllers/Processes/mob.dm @@ -1,27 +1,34 @@ /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(var/mob/M in mob_list) + 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() + else + 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..83b4b21aa89 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(var/datum/nanoui/NUI in nanomanager.processing_uis) + if(istype(NUI) && isnull(NUI.gcDestroyed)) + try + NUI.process() + catch(var/exception/e) + catchException(e, NUI) + else + nanomanager.processing_uis -= NUI diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm index 15ad98dd3d6..1559fc52b32 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(var/obj/O in processing_objects) + if(istype(O) && isnull(O.gcDestroyed)) + try + 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() + else + processing_objects -= O \ No newline at end of file diff --git a/code/controllers/Processes/pipenet.dm b/code/controllers/Processes/pipenet.dm index b59373a88a1..f76a8eeec4a 100644 --- a/code/controllers/Processes/pipenet.dm +++ b/code/controllers/Processes/pipenet.dm @@ -1,12 +1,21 @@ /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) if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed)) - pipeNetwork.process() - scheck() + 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() continue else 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/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 99d8931ef3a..533725d51cb 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -42,8 +42,6 @@ datum/controller/game_controller var/list/shuttle_list // For debugging and VV var/datum/ore_distribution/asteroid_ore_map // 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) @@ -255,7 +253,7 @@ datum/controller/game_controller/proc/process() // GC timer = world.timeofday last_thing_processed = garbageCollector.type - garbageCollector.process() + garbageCollector.processGarbage() garbageCollectorCost = (world.timeofday - timer) / 10 //TIMING diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index fef0ef7d66b..dda0a74abe0 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -11,7 +11,8 @@ obj/machinery/door/airlock obj/machinery/door/airlock/process() ..() if (arePowerSystemsOn()) - execute_current_command() + spawn() + execute_current_command() obj/machinery/door/airlock/receive_signal(datum/signal/signal) if (!arePowerSystemsOn()) return //no power diff --git a/code/modules/garbage collection/garbage_collector.dm b/code/modules/garbage collection/garbage_collector.dm index 97d67c30ac8..3e6c6c0b967 100644 --- a/code/modules/garbage collection/garbage_collector.dm +++ b/code/modules/garbage collection/garbage_collector.dm @@ -9,14 +9,14 @@ // 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 @@ -64,6 +64,7 @@ var/datum/garbage_collector/garbageCollector queue.Cut(1, 2) soft_dels++ dels_count++ + scheck() #ifdef GC_DEBUG #undef GC_DEBUG @@ -73,7 +74,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 a0242f78b69..2ea0d0133c0 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" - schedule_interval = LIGHTING_INTERVAL - lighting_controller = src - - 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. + last_light_count = lighting_update_lights.len lighting_update_lights = list() for(var/datum/light_source/L in lighting_update_lights_old) @@ -22,6 +18,7 @@ var/global/datum/controller/process/lighting/lighting_controller scheck() var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above. + last_overlay_count = lighting_update_overlays.len lighting_update_overlays = list() for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5042ae90996..f89f19066c8 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/paradise.dme b/paradise.dme index 9b0a59df6e8..c51d10cf80b 100644 --- a/paradise.dme +++ b/paradise.dme @@ -147,11 +147,11 @@ #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.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,8 +166,6 @@ #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\datums\ai_laws.dm" #include "code\datums\browser.dm" #include "code\datums\cargoprofile.dm" From 26d35be7645ab1b9d3d58c400471c6682824ba6c Mon Sep 17 00:00:00 2001 From: Krausus Date: Tue, 4 Aug 2015 00:08:20 -0400 Subject: [PATCH 2/4] Restores trivial.txt --- config/news/trivial.txt | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 config/news/trivial.txt diff --git a/config/news/trivial.txt b/config/news/trivial.txt new file mode 100644 index 00000000000..82b5a53c92b --- /dev/null +++ b/config/news/trivial.txt @@ -0,0 +1,82 @@ +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'. +Pens proclaim pencils obsolete, 'lead is dead'. +Rock and paper sues scissors for discrimination. +Steak tell-all book reveals he never liked sitting by potato. +Woodchuck stops counting how many times he’s chucked 'Never again'. +{{AFFECTED}} clerk first person able to pronounce '@*$%!'. +{{AFFECTED}} delis serving boiled paperback dictionaries, 'Adjectives chewy' customers declare. +{{AFFECTED}} weather deemed 'boring'; meteors and rad storms to be imported. +Most {{AFFECTED}} security officers prefer cream over sugar. +Palindrome speakers conference in {{AFFECTED}}; 'Wow!' says Otto. +Question mark worshipped as deity by ancient {{AFFECTED}} dwellers. +Spilled milk causes whole {{AFFECTED}} populace to cry. +World largest carp patty at display on {{AFFECTED}}. +'Here kitty kitty' no longer preferred tajaran retrieval technique. +Man travels 7000 light years to retrieve lost hankie, 'It was my favourite'. +New bowling lane that shoots mini-meteors at bowlers very popular. +Guy gets tattoo of Tau Ceti on chest '[pick("CentComm","star","starship","asteroid")] tickles most'. +Skrell marries computer; wedding attended by 100 modems. +Chef reports successfully using harmonica as cheese grater. +Nanotrasen invents handkerchief that says 'Bless you' after sneeze. +Clone accused of posing for other clones’s school photo. +Clone accused of stealing other clones’s employee of the month award. +Woman robs station with hair dryer; crewmen love new style. +This space for rent. +{{AFFECTED}} Baker Wins Pickled Crumpet Toss Three Years Running +Skrell Scientist Discovers Abacus Can Be Used To Dry Towels +Survey: 'Cheese Louise' Voted Best Pizza Restaurant In Tau Ceti +I Was Framed, jokes {{AFFECTED}} artist +Mysterious Loud Rumbling Noises In {{AFFECTED}} Found To Be Mysterious Loud Rumblings +Alien ambassador becomes lost on {{AFFECTED}}, refuses to ask for directions +Swamp Gas Verified To Be Exhalations Of Stars--Movie Stars--Long Passed +Tainted Broccoli Weapon Of Choice For Syndicate Assassins +Chefs Find Broccoli Effective Tool For Cutting Cheese +Broccoli Found To Cause Grumpiness In Monkeys +Survey: 80% Of People on {{AFFECTED}} Love Clog-Dancing +Giant Hairball Has Perfect Grammar But Rolls rr's Too Much, Linguists Say +{{AFFECTED}} Phonebooks Print All Wrong Numbers; Results In 15 New Marriages +Tajaran Burglar Spotted on {{AFFECTED}}, Mistaken For Dalmatian +Gibson Gazette Updates Frequently Absurd, Poll Indicates +Esoteric Verbosity Culminates In Communicative Ennui, {{AFFECTED}} Academics Note +Taj Demand Longer Breaks, Cleaner Litter, Slower Mice +Survey: 3 Out Of 5 Skrell Loathe Modern Art +Skrell Scientist Discovers Gravity While Falling Down Stairs +Boy Saves Tajaran From Tree on {{AFFECTED}}, Thousands Cheer +Shipment Of Apples Overturns, {{AFFECTED}} Diner Offers Applesauce Special +Spotted Owl Spotted on {{AFFECTED}} +Humans Everywhere Agree: Purring Tajarans Are Happy Tajarans +From The Desk Of Wise Guy Sammy: One Word In This Gazette Is Sdrawkcab +From The Desk Of Wise Guy Sammy: It's Hard To Have Too Much Shelf Space +From The Desk Of Wise Guy Sammy: Wine And Friendships Get Better With Age +From The Desk Of Wise Guy Sammy: The Insides Of Golf Balls Are Mostly Rubber Bands +From The Desk Of Wise Guy Sammy: You Don't Have To Fool All The People, Just The Right Ones +From The Desk Of Wise Guy Sammy: If You Made The Mess, You Clean It Up +From The Desk Of Wise Guy Sammy: It Is Easier To Get Forgiveness Than Permission +From The Desk Of Wise Guy Sammy: Check Your Facts Before Making A Fool Of Yourself +From The Desk Of Wise Guy Sammy: You Can't Outwait A Bureaucracy +From The Desk Of Wise Guy Sammy: It's Better To Yield Right Of Way Than To Demand It +From The Desk Of Wise Guy Sammy: A Person Who Likes Cats Can't Be All Bad +From The Desk Of Wise Guy Sammy: Help Is The Sunny Side Of Control +From The Desk Of Wise Guy Sammy: Two Points Determine A Straight Line +From The Desk Of Wise Guy Sammy: Reading Improves The Mind And Lifts The Spirit +From The Desk Of Wise Guy Sammy: Better To Aim High And Miss Then To Aim Low And Hit +From The Desk Of Wise Guy Sammy: Meteors Often Strike The Same Place More Than Once +Tommy B. Saif Sez: Look Both Ways Before Boarding The Shuttle +Tommy B. Saif Sez: Hold On; Sudden Stops Sometimes Necessary +Tommy B. Saif Sez: Keep Fingers Away From Moving Panels +Tommy B. Saif Sez: No Left Turn, Except Shuttles +Tommy B. Saif Sez: Return Seats And Trays To Their Proper Upright Position +Tommy B. Saif Sez: Eating And Drinking In Docking Bays Is Prohibited +Tommy B. Saif Sez: Accept No Substitutes, And Don't Be Fooled By Imitations +Tommy B. Saif Sez: Do Not Remove This Tag Under Penalty Of Law +Tommy B. Saif Sez: Always Mix Thoroughly When So Instructed +Tommy B. Saif Sez: Try To Keep Six Month's Expenses In Reserve +Tommy B. Saif Sez: Change Not Given Without Purchase +Tommy B. Saif Sez: If You Break It, You Buy It +Tommy B. Saif Sez: Reservations Must Be Cancelled 48 Hours Prior To Event To Obtain Refund +Doughnuts: Is There Anything They Can't Do +If Tin Whistles Are Made Of Tin, What Do They Make Foghorns Out Of? +Broccoli discovered to be colonies of tiny aliens with murder on their minds From 61edb3eb95581d1633279d69f059fa665efd2db9 Mon Sep 17 00:00:00 2001 From: Krausus Date: Sat, 8 Aug 2015 09:07:26 -0400 Subject: [PATCH 3/4] Further process tweaks - Integrates Volundr's btime library and associated process scheduler changes. - btime is implemented separately from the process scheduler, as precise time measurement is also useful elsewhere. - `TimeOfHour` is no longer internally throttled; throttling is instead done by `SCHECK`. - If btime's `gettime` cannot be called at world startup, an error will be output and the world will stop. - Retains the change to schedule processes according to game time, rather than real time. - Removes the (now unused) update queue files. - Removes the process scheduler testing files. - These are standalone tests for the process scheduler, completely unrelated to its use in the full codebase. We never used them. - Moves the process scheduler defines into __DEFINES. - Makes the lighting process run once before the round starts. - Renames `scheck` to `sleepCheck`, to ensure any code that tries to use `scheck` will fail to compile. - Adds `SCHECK` and `SCHECK_EVERY` macros that skip calling `sleepCheck` entirely until a specified number of `SCHECK`s (50 by default) have been called. - Makes most processes iterate using their `last_object` variable, allowing hang recovery to show the type that caused the hang. - Makes processes output an error when they filter out a type they refuse to process. - Rolls the recently-added alarm subsystem into the alarm process. - Removes the now unused subsystems code. --- btime.dll | Bin 0 -> 6656 bytes code/__DEFINES/btime.dm | 21 ++ .../process_scheduler.dm} | 8 +- code/_globalvars/misc.dm | 4 +- .../ProcessScheduler/ProcessScheduler.dme | 32 --- .../ProcessScheduler/core/process.dm | 85 ++++--- .../ProcessScheduler/core/processScheduler.dm | 108 ++++++--- .../ProcessScheduler/core/updateQueue.dm | 127 ----------- .../core/updateQueueWorker.dm | 83 ------- .../ProcessScheduler/test/processScheduler.js | 56 ----- .../test/processSchedulerView.dm | 94 -------- .../test/testDyingUpdateQueueProcess.dm | 27 --- .../ProcessScheduler/test/testHarness.dm | 35 --- .../ProcessScheduler/test/testHungProcess.dm | 15 -- .../ProcessScheduler/test/testNiceProcess.dm | 13 -- .../ProcessScheduler/test/testSlowProcess.dm | 28 --- .../ProcessScheduler/test/testUpdateQueue.dm | 209 ------------------ .../test/testUpdateQueueProcess.dm | 24 -- .../test/testZombieProcess.dm | 13 -- code/controllers/Processes/air.dm | 12 +- code/controllers/Processes/alarm.dm | 31 ++- code/controllers/Processes/bot.dm | 11 +- code/controllers/Processes/inactivity.dm | 2 +- code/controllers/Processes/lighting.dm | 3 + code/controllers/Processes/machinery.dm | 21 +- code/controllers/Processes/mob.dm | 7 +- code/controllers/Processes/nanoui.dm | 6 +- code/controllers/Processes/obj.dm | 16 +- code/controllers/Processes/pipenet.dm | 7 +- code/controllers/Processes/sun.dm | 19 +- code/controllers/subsystem/alarms.dm | 30 --- code/controllers/subsystems.dm | 46 ---- .../garbage collection/garbage_collector.dm | 2 +- code/modules/lighting/lighting_process.dm | 6 +- paradise.dme | 5 +- 35 files changed, 255 insertions(+), 951 deletions(-) create mode 100644 btime.dll create mode 100644 code/__DEFINES/btime.dm rename code/{controllers/ProcessScheduler/core/_define.dm => __DEFINES/process_scheduler.dm} (63%) delete mode 100644 code/controllers/ProcessScheduler/ProcessScheduler.dme delete mode 100644 code/controllers/ProcessScheduler/core/updateQueue.dm delete mode 100644 code/controllers/ProcessScheduler/core/updateQueueWorker.dm delete mode 100644 code/controllers/ProcessScheduler/test/processScheduler.js delete mode 100644 code/controllers/ProcessScheduler/test/processSchedulerView.dm delete mode 100644 code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm delete mode 100644 code/controllers/ProcessScheduler/test/testHarness.dm delete mode 100644 code/controllers/ProcessScheduler/test/testHungProcess.dm delete mode 100644 code/controllers/ProcessScheduler/test/testNiceProcess.dm delete mode 100644 code/controllers/ProcessScheduler/test/testSlowProcess.dm delete mode 100644 code/controllers/ProcessScheduler/test/testUpdateQueue.dm delete mode 100644 code/controllers/ProcessScheduler/test/testUpdateQueueProcess.dm delete mode 100644 code/controllers/ProcessScheduler/test/testZombieProcess.dm delete mode 100644 code/controllers/subsystem/alarms.dm delete mode 100644 code/controllers/subsystems.dm diff --git a/btime.dll b/btime.dll new file mode 100644 index 0000000000000000000000000000000000000000..36dcbe3af7561942191dde97718a2844dc2a23ef GIT binary patch literal 6656 zcmeHLdvH|M89$qBU;`VnY8Jv6WC=-!K?(aLS(4!9u_cTI7dB5IESudMcEdj6-Fq>y z;=ruwxL#9fb*y$e4j3J5r)8j0iPDjUfB`2=BXu%y1e>d;= zt$(zRXY!rL_dehG&UfyndgC6FN(hkyM3ImljI>##{+qiP$a5CHK8GAmKf9<$w)X6z z8jm-mX=8)iSf^j(as~oHPSeb2SU#Zf1~irT+co~6n<>c1n3b1Mz2NQ2+ZqopEl)!E zvRBJDVO_rLaAMq5UMsD4OKaQd*UC3a{CAgkNchh325CH$7>`Kf2CvIQbtZRUvl6ma zmPTgHj@Bk|!z5+VY}qVA@JEz!Y;G1t6+k1cRnk0#kTfEb5Xq5j#S%eVx{;6+VgV6C zRPOp1U)u5sX#rN5OGpVNTL?KES4CGhnvD=6@Ow3ch^6@DHI$0b;~DVmK42o{E?hNCaGw z;)}e>EM*0+h*Xb^9-+~i;L^g^Quu;>e}LWMHoS9$WIp;7cz}sL`GqI~ zNkg(Lfm#J?l<%61&qF$B>TUMeQsC;5Au$K|C6zd;85AQ9bY7vRQuzw1?_SU$l)5Nx zh>P0?bAHnP*qwrPTu=zCTfzdG`{GT~2eb7OUsd{xLve{L^_8cvQI8e&tod8vvFYE9w1s} zu}K=8R@~4WAU#oZqFRGKTuK=TuK>?Gl*dDLi|RbkP%f8XIMeGXL2AX0QgUx=mS=#b zQWu`bTQ9^&c#>u)y>nF%Y#ej9Dq_!2F?t-yOh-z@lbv3L;V$d2t;;%Mqro@PS|<+L zVw)ioK8dV9FY+Vepp@YvKa3f@BsZVy`Pgc>(hEx($wBGJpe?omF}ON(XY}e&V^tFl zyRPS1IQ|Ch>V?VTH$$1iOypCFI1uiW7hhv9I%vtP>)A`WX!o^TQvH_f2-S+7YJ4cQ zm}Q*Y5QUktlGNk7i|9ug|B2z;YqL>LTFh11nNOb(P9#hJGxnMca9>~)z( z9++V`SKn2s$i`i`Q`o5#s+7f_vgtyDBK6(k&)He!;pmd$K{jTH;%48Zu3z$}^_ph_ zo_p406P0@(@}Ai6irMSxr5w>&Ck>|%?Fn}`B}JGRLP=*HfGV~W0Slj$BPKP1Rk4iX zY;lTBq9{OFDOmE3#}-~42oK8==O9#1xFZ=~paNjLQNa4D@ngin{!D+_Z}FnUGfuW5gOe9xkZIwDl&sUp5S zRTtpy;TzkZ!uGLik)IT-Qwe`ia<50Ece-ow&8&}?36amjdlJuX&r2$Vm|nWrF&GP< z%%bk6xxM3E$O@7M2 z<57^)T}_B;MPgJ+BfJBjit<@68>|zCiQ=pL%s1|b<}p?0C0;4KnQ_}{LXHosW=}*+ z)UT_95fint#j)v3Z@-wo8NyKKsg`}kOP^~p0vcLd6udq?mro!cMs}> z46+;Sm3HSp4?O;HU!;wx5et_?A4Xmh>S=kwxMuifQw`g{z#xsnNO z=YnmUZEVoRghIeGZk`gFIj^7Dyn*pC&JYt{6|}jV;|`)EePyFhG$jlq#^iclRyQ_P>%x52mbU zID1EkWBfG;Gr7;YVV!lYL0{nZ`3OkV)%q}pCru|cQR#wo^Bhacw_bU9os zM})K%mSG5)pWg zCw%UbJNN(`+fFut8`}s5BqNJc9TN6Tsw06}1aI42OdIF$I0J5^n$h73`u)KG`G{Dh zZPHWaze%>+zH|)E?ExxUKC-;lMVA{7!E7F-#j7oldO?`rDi=bDfDq7qtK{y}>{tIOHX{o#DQooVq4dnFySXWkOI& z-Y1pbkh9sx)Oc9N>0aw?W}R$D1&%O8_NT7r8Med5uq{E>?+mz@iXcz-N1meDQR8)O zmk zX)ezOR)+8f`kkR=ey@uShJr2JGSsJ)&XB)ghb~uxo9b<0LR=l5Q`CmiTwQ@ScU8tL zji!WS`4G1T)$!ldW{Ar{GQ_xe*2{HBdNgPyEb{;lD~#L5dUxOoZ(~B&iPOo}c8Jm< zwU*hz_%uG+m*zS{YXUoh+Zi@j!+XnI^kpy2ZE^ZSOzx_Z<=1uJEPP%6G>$J>FIj$* zuO-W`ZyMqy%aie3WuvvAa?QHc4GoFOf3v1_C=2H{Q#)5%uXSh{?RITQ`;c~*woAKT z`-b+k_N?}tHm1F-RqGb(v^tY+lg_QQ_= 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" From a00f9bbc64d0a49fb75f1760cf3eada9f909f7e0 Mon Sep 17 00:00:00 2001 From: Krausus Date: Sat, 8 Aug 2015 19:31:13 -0400 Subject: [PATCH 4/4] A few more process tweaks - Removes unused btime globals. - Removes unnecessary multiplication. - Makes the garbage collector run twice as often, but do half as much work. - Makes the garbage collector SCHECK more often when performing hard deletions. - Adds a delay to the garbage collector's start. --- code/__DEFINES/btime.dm | 3 --- code/controllers/ProcessScheduler/core/process.dm | 2 +- code/controllers/Processes/garbage.dm | 3 ++- code/modules/garbage collection/garbage_collector.dm | 6 ++++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/btime.dm b/code/__DEFINES/btime.dm index 1e9cda3258f..a80446d441c 100644 --- a/code/__DEFINES/btime.dm +++ b/code/__DEFINES/btime.dm @@ -2,9 +2,6 @@ #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")()) diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm index 0a2143a3e14..c09b5730b69 100644 --- a/code/controllers/ProcessScheduler/core/process.dm +++ b/code/controllers/ProcessScheduler/core/process.dm @@ -206,7 +206,7 @@ CRASH("Process [name] hung and was restarted.") if (main.getCurrentTickElapsedTime() > main.timeAllowance) - sleep(world.tick_lag*1) + sleep(world.tick_lag) cpu_defer_count++ last_slept = TimeOfHour else diff --git a/code/controllers/Processes/garbage.dm b/code/controllers/Processes/garbage.dm index 68a01747513..2e53be6414d 100644 --- a/code/controllers/Processes/garbage.dm +++ b/code/controllers/Processes/garbage.dm @@ -1,6 +1,7 @@ /datum/controller/process/garbage_collector/setup() name = "garbage" - schedule_interval = 20 // every 2 seconds + schedule_interval = 10 + start_delay = 3 garbageCollector = src diff --git a/code/modules/garbage collection/garbage_collector.dm b/code/modules/garbage collection/garbage_collector.dm index 8f2886176ce..41c01dc5672 100644 --- a/code/modules/garbage collection/garbage_collector.dm +++ b/code/modules/garbage collection/garbage_collector.dm @@ -2,9 +2,9 @@ // 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 @@ -60,6 +60,8 @@ var/global/datum/controller/process/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++