Fix shuttle air even more. (#27889)

* Even better synchronization.

* Lint.
This commit is contained in:
Charlie Nolan
2025-01-08 13:10:16 -08:00
committed by GitHub
parent db5be68723
commit 7d92e944ea
2 changed files with 32 additions and 34 deletions
+32 -27
View File
@@ -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)
@@ -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