diff --git a/code/controllers/subsystem/SSair.dm b/code/controllers/subsystem/SSair.dm index a02013adb5e..5553f6d78d0 100644 --- a/code/controllers/subsystem/SSair.dm +++ b/code/controllers/subsystem/SSair.dm @@ -79,14 +79,14 @@ SUBSYSTEM_DEF(air) /// Which step we're currently on, used to let us resume if our time budget elapses. var/currentpart = SSAIR_DEFERREDPIPENETS - /// Is MILLA currently in synchronous mode? TRUE if data is fresh and changes can be made, FALSE if data is from last tick and changes cannot be made (because this tick is still processing). - var/is_synchronous = TRUE + /// Is MILLA currently idle? TRUE if the MILLA tick has finished, meaning data is fresh and changes can be made. FALSE if MILLA is currently running a tick, meaning data is from last tick and changes cannot be made. + var/milla_idle = TRUE - /// Are we currently running in a MILLA-safe context, i.e. is is_synchronous *guaranteed* to be TRUE. Nothing outside of this file should change this. + /// Are we currently running in a MILLA-safe context, i.e. is milla_idle *guaranteed* to be TRUE. Nothing outside of this file should change this. VAR_PRIVATE/in_milla_safe_code = FALSE - /// Are we in MILLA-safe code that can safely sleep? - VAR_PRIVATE/safe_to_sleep = FALSE + /// What sleeping callbacks are currently running? + VAR_PRIVATE/list/sleepers = list() /// A list of callbacks waiting for MILLA to finish its tick and enter synchronous mode. var/list/waiting_for_sync = list() @@ -176,23 +176,26 @@ SUBSYSTEM_DEF(air) machinery_to_construct = SSair.machinery_to_construct currentrun = SSair.currentrun currentpart = SSair.currentpart - is_synchronous = SSair.is_synchronous + milla_idle = SSair.milla_idle #define SLEEPABLE_TIMER (world.time + world.tick_usage * world.tick_lag / 100) /datum/controller/subsystem/air/fire(resumed = 0) // All atmos stuff assumes MILLA is synchronous. Ensure it actually is. - if(!is_synchronous) + if(!milla_idle || length(sleepers) > 0) var/timer = SLEEPABLE_TIMER - while(!is_synchronous) + while(!milla_idle || length(sleepers) > 0) // Sleep for 1ms. sleep(0.01) - if(MC_TICK_CHECK) - time_slept.record_progress((SLEEPABLE_TIMER - timer) * 100, FALSE) - return + var/new_timer = SLEEPABLE_TIMER + time_slept.record_progress((new_timer - timer) * 100, FALSE) + timer = new_timer time_slept.record_progress((SLEEPABLE_TIMER - timer) * 100, TRUE) + // Run the sleepless callbacks again in case more showed up since on_milla_tick_finished() + run_sleepless_callbacks() + fire_sleepless(resumed) #undef SLEEPABLE_TIMER @@ -311,7 +314,7 @@ SUBSYSTEM_DEF(air) timer = TICK_USAGE_REAL spawn_milla_tick_thread() - is_synchronous = FALSE + milla_idle = FALSE cost_milla_tick = MC_AVERAGE(cost_milla_tick, get_milla_tick_time()) cost_full.record_progress(TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer), state != SS_PAUSED && state != SS_PAUSING) @@ -716,10 +719,10 @@ SUBSYSTEM_DEF(air) // Just in case someone is naughty and decides to sleep, make sure that this method runs fully anyway. set waitfor = FALSE - if(is_synchronous) + if(milla_idle) var/was_safe = SSair.in_milla_safe_code SSair.in_milla_safe_code = TRUE - // This is one of two intended places to call this otherwise-unsafe proc. + // This is one of four intended places to call this otherwise-unsafe proc. CB.private_unsafe_invoke() SSair.in_milla_safe_code = was_safe return @@ -728,23 +731,22 @@ SUBSYSTEM_DEF(air) /// Similar to addtimer, but triggers once MILLA enters synchronous mode. This version allows for sleeping if it's absolutely necessary. /datum/controller/subsystem/air/proc/sleepable_synchronize(datum/milla_safe_must_sleep/CB) - if(safe_to_sleep) - var/was_safe = SSair.in_milla_safe_code - SSair.in_milla_safe_code = TRUE - // This is one of two intended places to call this otherwise-unsafe proc. + if(length(sleepers)) + sleepers += CB + // This is one of four intended places to call this otherwise-unsafe proc. CB.private_unsafe_invoke() - SSair.in_milla_safe_code = was_safe + sleepers -= CB return sleepable_waiting_for_sync += CB /datum/controller/subsystem/air/proc/is_in_milla_safe_code() - return in_milla_safe_code + return in_milla_safe_code || length(sleepers) > 0 /datum/controller/subsystem/air/proc/on_milla_tick_finished() - run_sleeping_callbacks() - is_synchronous = TRUE + milla_idle = TRUE run_sleepless_callbacks() + run_sleeping_callbacks() /datum/controller/subsystem/air/proc/run_sleepless_callbacks() // Just in case someone is naughty and decides to sleep, make sure that this method runs fully anyway. @@ -752,20 +754,23 @@ SUBSYSTEM_DEF(air) in_milla_safe_code = TRUE for(var/datum/milla_safe/CB as anything in waiting_for_sync) - // This is one of two intended places to call this otherwise-unsafe proc. + // This is one of four intended places to call this otherwise-unsafe proc. CB.private_unsafe_invoke() waiting_for_sync.Cut() in_milla_safe_code = FALSE /datum/controller/subsystem/air/proc/run_sleeping_callbacks() in_milla_safe_code = TRUE - safe_to_sleep = TRUE for(var/datum/milla_safe_must_sleep/CB as anything in sleepable_waiting_for_sync) - // This is one of two intended places to call this otherwise-unsafe proc. + sleepers += CB + // This is one of four intended places to call this otherwise-unsafe proc. CB.private_unsafe_invoke() + sleepers -= CB sleepable_waiting_for_sync.Cut() in_milla_safe_code = FALSE - safe_to_sleep = FALSE + +/datum/controller/subsystem/air/proc/has_sleeper(datum/milla_safe_must_sleep/sleeper) + return sleeper in sleepers /proc/milla_tick_finished() // Any proc that wants MILLA to be synchronous should not sleep. @@ -861,7 +866,7 @@ SUBSYSTEM_DEF(air) /// Used internally to check that we're running safely, but without breaking things worse if we aren't. /datum/milla_safe_must_sleep/proc/soft_assert_safe() - ASSERT(SSair.is_in_milla_safe_code()) + ASSERT(SSair.has_sleeper(src)) /// Fetch the air for a turf. Only use from `on_run`. /datum/milla_safe_must_sleep/proc/get_turf_air(turf/T) diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index e39ccf5ef5f..7bfd1fa004e 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -50,13 +50,6 @@ What are the archived variables for? /// Is this mixture currently synchronized with MILLA? Always true for non-bound mixtures. var/synchronized = TRUE - /// Tracks the callbacks from synchronize() that haven't run yet. - var/list/waiting_for_sync = list() - -/datum/gas_mixture/Destroy() - waiting_for_sync.Cut() - return ..() - /// Marks this gas mixture as changed from MILLA. Does nothing on non-bound mixtures. /datum/gas_mixture/proc/set_dirty() return