Finishes the allocation refactor (#55965)

#53841 continuation
I recall that the reason for infinite stuns and such was that the priority wasn't being cleared properly when something hitched and I fixed it and I don't remember where exactly but it works now (TM)
A variation of this is TMed on TGMC and it works fine
This commit is contained in:
TiviPlus
2021-01-11 19:42:00 +01:00
committed by GitHub
parent 166fa386c0
commit 87234f3fd8
12 changed files with 153 additions and 151 deletions

View File

@@ -1,14 +1,19 @@
/// Percentage of tick to leave for master controller to run
#define MAPTICK_MC_MIN_RESERVE 70
#define MAPTICK_MC_MIN_RESERVE 60
/// internal_tick_usage is updated every tick by extools
#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100)
/// Tick limit while running normally
#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((Master.normalized_internal_tick_usage / world.tick_lag) * 100)
/// Amount of a tick to reserve for byond regardless of its reported internal_tick_usage
#define TICK_BYOND_RESERVE 2
#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE))
/// Tick limit while running normally
#define TICK_LIMIT_RUNNING (max(TICK_LIMIT_RUNNING_BACKGROUND, MAPTICK_MC_MIN_RESERVE))
/// Tick limit for background things, strictly obeys the internal_tick_usage metric
#define TICK_LIMIT_RUNNING_BACKGROUND (100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE)
/// Precent of a tick to require to even bother running anything. (10 percent of the tick_limit_running by default)
#define TICK_MIN_RUNTIME (TICK_LIMIT_RUNNING * 0.1)
/// Tick limit used to resume things in stoplag
#define TICK_LIMIT_TO_RUN 70
#define TICK_LIMIT_TO_RUN (Master.current_ticklimit - TICK_MIN_RUNTIME)
/// Tick limit for MC while running
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC (TICK_LIMIT_RUNNING - TICK_MIN_RUNTIME)
/// Tick limit while initializing
#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE)

View File

@@ -61,9 +61,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/static/random_seed
//current tick limit, assigned before running a subsystem.
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
///current tick limit, assigned before running a subsystem. used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
/// normalized version of the byond internal tick usage metric to smooth out peaks.
var/normalized_internal_tick_usage = 0
/datum/controller/master/New()
if(!config)
@@ -173,7 +174,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// Please don't stuff random bullshit here,
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
/datum/controller/master/Initialize(delay, init_sss, tgs_prime)
set waitfor = 0
set waitfor = FALSE
if(delay)
sleep(delay)
@@ -235,7 +236,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// Starts the mc, and sticks around to restart it if the loop ever ends.
/datum/controller/master/proc/StartProcessing(delay)
set waitfor = 0
set waitfor = FALSE
if(delay)
sleep(delay)
testing("Master starting processing")
@@ -309,12 +310,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
while (1)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
normalized_internal_tick_usage = max(0, MC_AVG_FAST_UP_SLOW_DOWN(normalized_internal_tick_usage, GLOB.internal_tick_usage))
var/starting_tick_usage = TICK_USAGE
if (processing <= 0)
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
//Anti-tick-contention heuristics:
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
@@ -328,15 +332,13 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
if (last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
sleep_delta += 1
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) //we ran 3/4 of the way into the tick
sleep_delta += 1
//debug
if (make_runtime)
var/datum/controller/subsystem/SS
SS.can_fire = 0
SS.can_fire = FALSE
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
@@ -359,7 +361,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
else
subsystems_to_check = tickersubsystems
if (CheckQueue(subsystems_to_check) <= 0)
if (!CheckQueue(subsystems_to_check))
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
return
@@ -371,7 +373,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
continue
if (queue_head)
if (RunQueue() <= 0)
if (!RunQueue())
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
return
@@ -392,8 +394,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
skip_ticks--
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
current_ticklimit = TICK_LIMIT_RUNNING
if (processing * sleep_delta <= world.tick_lag)
current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
sleep(world.tick_lag * (processing * sleep_delta))
@@ -401,8 +401,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// This is what decides if something should run.
/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
. = 0 //so the mc knows if we runtimed
//we create our variables outside of the loops to save on overhead
var/datum/controller/subsystem/SS
var/SS_flags
@@ -424,139 +422,139 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
if ((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time)
continue
SS.enqueue()
. = 1
return TRUE // so MC knows if we runtime
// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
/datum/controller/master/proc/RunQueue()
. = 0
var/datum/controller/subsystem/queue_node
var/queue_node_flags
var/queue_node_priority
var/datum/controller/subsystem/queue_node = queue_head //The subsystem we're running right now
var/queue_node_flags //Cache of queue node flags
var/queue_node_priority //Cache of queue node priority
var/queue_node_paused
var/current_tick_budget
var/tick_precentage
var/tick_remaining
var/ran = TRUE //this is right
var/ran_non_ticker = FALSE
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
var/tick_usage
var/current_tick_budget = queue_priority_count
var/tick_precentage //tick % used, used for calculating tick remaining
var/tick_remaining //How much of the tick we've got left over to do stuff
var/ran_non_ticker = FALSE //Whether we've started running non-ticker subsystems yet
var/bg_calc = FALSE //have we swtiched current_tick_budget to background mode yet?
var/tick_usage //How much of a tick we're using in this queue node
//keep running while we have stuff to run and we haven't gone over a tick
// this is so subsystems paused eariler can use tick time that later subsystems never used
while (ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
ran = FALSE
bg_calc = FALSE
current_tick_budget = queue_priority_count
queue_node = queue_head
while (queue_node)
if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
break
queue_node_flags = queue_node.flags
queue_node_priority = queue_node.queued_priority
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
queue_node = queue_node.queue_next
continue
//super special case, subsystems where we can't make them pause mid way through
//if we can't run them this tick (without going over a tick)
//we bump up their priority and attempt to run them next tick
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
// in those cases, so we just let them run)
if (queue_node_flags & SS_NO_TICK_CHECK)
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
if (!(queue_node_flags & SS_BACKGROUND))
queue_node.queued_priority += queue_priority_count * 0.1
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
current_tick_budget -= queue_node_priority
queue_node = queue_node.queue_next
continue
if (!bg_calc && (queue_node_flags & SS_BACKGROUND))
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
tick_remaining = TICK_LIMIT_RUNNING - TICK_USAGE
if (current_tick_budget > 0 && queue_node_priority > 0)
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
else
tick_precentage = tick_remaining
tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
current_ticklimit = round(TICK_USAGE + tick_precentage)
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
tick_usage = TICK_USAGE
var/state = queue_node.ignite(queue_node_paused)
tick_usage = TICK_USAGE - tick_usage
if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
//this is so subsystems paused eariler can use tick time that later subsystems never used
while (queue_head && queue_node && TICK_USAGE < TICK_LIMIT_MC)
queue_node_flags = queue_node.flags
queue_node_priority = queue_node.queued_priority
if (tick_usage < 0)
tick_usage = 0
queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage-tick_precentage))
queue_node.state = state
if (state == SS_PAUSED)
queue_node.paused_ticks++
queue_node.paused_tick_usage += tick_usage
queue_node = queue_node.queue_next
continue
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
tick_usage += queue_node.paused_tick_usage
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
if (bg_calc) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
queue_node.last_fire = world.time
queue_node.times_fired++
if (queue_node_flags & SS_TICKER)
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
else if (queue_node_flags & SS_POST_FIRE_TIMING)
queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
else if (queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
queue_node.queued_time = 0
//remove from queue
queue_node.dequeue()
if(!(queue_node_flags & SS_TICKER) && skip_ticks)
queue_node = queue_node.queue_next
continue
. = 1
//super special case, subsystems where we can't make them pause mid way through
//if we can't run them this tick (without going over a tick)
//we bump up their priority and attempt to run them next tick
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
// in those cases, so we just let them run)
if (queue_node_flags & SS_NO_TICK_CHECK)
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
if (!(queue_node_flags & SS_BACKGROUND))
queue_node.queued_priority += queue_priority_count * 0.1
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
current_tick_budget -= queue_node_priority
queue_node = queue_node.queue_next
continue
//Checks if we're in background calculation mode yet and sets us to it if we are
if (!bg_calc && (queue_node_flags & SS_BACKGROUND))
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
tick_remaining = ((bg_calc) ? (TICK_LIMIT_RUNNING_BACKGROUND) : (TICK_LIMIT_RUNNING)) - TICK_USAGE
if (tick_remaining < TICK_MIN_RUNTIME)
current_tick_budget -= queue_node_priority
queue_node = queue_node.queue_next
continue
if (current_tick_budget > 0 && queue_node_priority > 0)
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
else
tick_precentage = tick_remaining
tick_precentage = max(TICK_MIN_RUNTIME, tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
current_ticklimit = round(TICK_USAGE + tick_precentage)
//Check for if we ran a non-ticker ss, used for check if we can start processing SS_NO_TICK_CHECK stuff yet
if(!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
tick_usage = TICK_USAGE
var/state = queue_node.ignite(queue_node_paused)
tick_usage = TICK_USAGE - tick_usage
if(state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
if(tick_usage < 0)
tick_usage = 0
queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage-tick_precentage))
queue_node.state = state
if(state == SS_PAUSED)
queue_node.paused_ticks++
queue_node.paused_tick_usage += tick_usage
queue_node = queue_node.queue_next
continue
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
tick_usage += queue_node.paused_tick_usage
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
if (bg_calc) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
queue_node.last_fire = world.time
queue_node.times_fired++
//Calculate next fire
if (queue_node_flags & SS_TICKER)
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
else if (queue_node_flags & SS_POST_FIRE_TIMING)
queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
else if (queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
queue_node.queued_time = 0
//remove from queue
queue_node.dequeue()
queue_node = queue_node.queue_next
return TRUE // so MC knows if we runtime
//resets the queue, and all subsystems, while filtering out the subsystem lists
// called if any mc's queue procs runtime or exit improperly.
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
. = 0
log_world("MC: SoftReset called, resetting MC queue state.")
if (!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
@@ -593,7 +591,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
queue_priority_count = 0
queue_priority_count_bg = 0
log_world("MC: SoftReset: Finished.")
. = 1
return TRUE // so MC knows if we runtime
/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick.
/datum/controller/master/proc/laggy_byond_map_update_incoming()

View File

@@ -1,6 +1,5 @@
SUBSYSTEM_DEF(adjacent_air)
name = "Atmos Adjacency"
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 10
priority = FIRE_PRIORITY_ATMOS_ADJACENCY

View File

@@ -3,7 +3,6 @@ SUBSYSTEM_DEF(air)
init_order = INIT_ORDER_AIR
priority = FIRE_PRIORITY_AIR
wait = 0.5 SECONDS
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cached_cost = 0

View File

@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(augury)
name = "Augury"
flags = SS_NO_INIT
flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/watchers = list()

View File

@@ -23,12 +23,12 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
return
. = elements_by_type[element_id] = new eletype
/****
* Generates an id for bespoke elements when given the argument list
* Generating the id here is a bit complex because we need to support named arguments
* Named arguments can appear in any order and we need them to appear after ordered arguments
* We assume that no one will pass in a named argument with a value of null
**/
/**
* Generates an id for bespoke elements when given the argument list
* Generating the id here is a bit complex because we need to support named arguments
* Named arguments can appear in any order and we need them to appear after ordered arguments
* We assume that no one will pass in a named argument with a value of null
**/
/datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments)
var/datum/element/eletype = arguments[1]
var/list/fullid = list("[eletype]")

View File

@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(disease)
name = "Disease"
flags = SS_NO_FIRE
flags = SS_NO_FIRE | SS_BACKGROUND
var/list/active_diseases = list() //List of Active disease in all mobs; purely for quick referencing.
var/list/diseases

View File

@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(fire_burning)
name = "Fire Burning"
priority = FIRE_PRIOTITY_BURNING
flags = SS_NO_INIT|SS_BACKGROUND
flags = SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()

View File

@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(pai)
name = "pAI"
flags = SS_NO_INIT|SS_NO_FIRE
flags = SS_NO_INIT|SS_NO_FIRE|SS_BACKGROUND
var/list/candidates = list()
var/ghost_spam = FALSE

View File

@@ -2,5 +2,5 @@ PROCESSING_SUBSYSTEM_DEF(fields)
name = "Fields"
wait = 2
priority = FIRE_PRIORITY_FIELDS
flags = SS_KEEP_TIMING | SS_NO_INIT
flags = SS_KEEP_TIMING | SS_NO_INIT | SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME

View File

@@ -3,7 +3,7 @@
SUBSYSTEM_DEF(processing)
name = "Processing"
priority = FIRE_PRIORITY_PROCESS
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
flags = SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 1 SECONDS
var/stat_tag = "P" //Used for logging

View File

@@ -3,5 +3,6 @@ PROCESSING_SUBSYSTEM_DEF(wet_floors)
priority = FIRE_PRIORITY_WET_FLOORS
wait = 10
stat_tag = "WFP" //Used for logging
flags = SS_BACKGROUND
var/temperature_coeff = 2
var/time_ratio = 1.5 SECONDS