Makes timer subsystems available as a new subsystem type (#59073)

This commit is contained in:
Emmett Gaines
2021-05-15 01:59:21 -07:00
committed by GitHub
parent 0c164bd657
commit 3cf19e34dd
6 changed files with 53 additions and 33 deletions
+38 -30
View File
@@ -1,9 +1,9 @@
/// Controls how many buckets should be kept, each representing a tick. (1 minutes worth)
#define BUCKET_LEN (world.fps*1*60)
/// Helper for getting the correct bucket for a given timer
#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
#define BUCKET_POS(timer) (((round((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
/// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
#define TIMER_MAX(timer_ss) (world.time + TICKS2DS(min(BUCKET_LEN-(timer_ss.practical_offset-DS2TICKS(world.time - timer_ss.head_offset))-1, BUCKET_LEN-1)))
/// Max float with integer precision
#define TIMER_ID_MAX (2**24)
@@ -43,6 +43,8 @@ SUBSYSTEM_DEF(timer)
var/list/clienttime_timers = list()
/// Contains the last time that a timer's callback was invoked, or the last tick the SS fired if no timers are being processed
var/last_invoke_tick = 0
/// Keeps track of the next index to work on for client timers
var/next_clienttime_timer_index = 0
/// Contains the last time that a warning was issued for not invoking callbacks
var/static/last_invoke_warning = 0
/// Boolean operator controlling if the timer SS will automatically reset buckets if it fails to invoke callbacks for an extended period of time
@@ -100,7 +102,6 @@ SUBSYSTEM_DEF(timer)
log_world(to_log.Join("\n"))
// Process client-time timers
var/static/next_clienttime_timer_index = 0
if (next_clienttime_timer_index)
clienttime_timers.Cut(1, next_clienttime_timer_index+1)
next_clienttime_timer_index = 0
@@ -181,7 +182,7 @@ SUBSYSTEM_DEF(timer)
var/i = 0
for (i in 1 to length(second_queue))
timer = second_queue[i]
if (timer.timeToRun >= TIMER_MAX)
if (timer.timeToRun >= TIMER_MAX(src))
i--
break
@@ -275,7 +276,7 @@ SUBSYSTEM_DEF(timer)
continue
// Check that the TTR is within the range covered by buckets, when exceeded we've finished
if (timer.timeToRun >= TIMER_MAX)
if (timer.timeToRun >= TIMER_MAX(src))
i--
break
@@ -348,8 +349,10 @@ SUBSYSTEM_DEF(timer)
var/datum/timedevent/next
/// Previous timed event in the bucket
var/datum/timedevent/prev
/// The timer subsystem this event is associated with
var/datum/controller/subsystem/timer/timer_subsystem
/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source)
/datum/timedevent/New(datum/callback/callBack, wait, flags, datum/controller/subsystem/timer/timer_subsystem, hash, source)
var/static/nextid = 1
id = TIMER_ID_NULL
src.callBack = callBack
@@ -357,13 +360,14 @@ SUBSYSTEM_DEF(timer)
src.flags = flags
src.hash = hash
src.source = source
src.timer_subsystem = timer_subsystem || SStimer
// Determine time at which the timer's callback should be invoked
timeToRun = (flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) + wait
// Include the timer in the hash table if the timer is unique
if (flags & TIMER_UNIQUE)
SStimer.hashes[hash] = src
timer_subsystem.hashes[hash] = src
// Generate ID for the timer if the timer is stoppable, include in the timer id dictionary
if (flags & TIMER_STOPPABLE)
@@ -372,9 +376,9 @@ SUBSYSTEM_DEF(timer)
nextid += min(1, 2 ** round(nextid / SHORT_REAL_LIMIT))
else
nextid++
SStimer.timer_id_dict[id] = src
timer_subsystem.timer_id_dict[id] = src
if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME))
if ((timeToRun < world.time || timeToRun < timer_subsystem.head_offset) && !(flags & TIMER_CLIENT_TIME))
CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]")
if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object))
@@ -385,7 +389,7 @@ SUBSYSTEM_DEF(timer)
/datum/timedevent/Destroy()
..()
if (flags & TIMER_UNIQUE && hash)
SStimer.hashes -= hash
timer_subsystem.hashes -= hash
if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers)
callBack.object.active_timers -= src
@@ -394,12 +398,12 @@ SUBSYSTEM_DEF(timer)
callBack = null
if (flags & TIMER_STOPPABLE)
SStimer.timer_id_dict -= id
timer_subsystem.timer_id_dict -= id
if (flags & TIMER_CLIENT_TIME)
if (!spent)
spent = world.time
SStimer.clienttime_timers -= src
timer_subsystem.clienttime_timers -= src
return QDEL_HINT_IWILLGC
if (!spent)
@@ -423,8 +427,8 @@ SUBSYSTEM_DEF(timer)
// Store local references for the bucket list and secondary queue
// This is faster than referencing them from the datum itself
var/list/bucket_list = SStimer.bucket_list
var/list/second_queue = SStimer.second_queue
var/list/bucket_list = timer_subsystem.bucket_list
var/list/second_queue = timer_subsystem.second_queue
// Attempt to get the head of the bucket
var/datum/timedevent/buckethead
@@ -436,14 +440,14 @@ SUBSYSTEM_DEF(timer)
// into an existing bucket, or is otherwise not present in the secondary queue
if(buckethead == src)
bucket_list[bucketpos] = next
SStimer.bucket_count--
else if(timeToRun < TIMER_MAX)
SStimer.bucket_count--
timer_subsystem.bucket_count--
else if(timeToRun < TIMER_MAX(timer_subsystem))
timer_subsystem.bucket_count--
else
var/l = length(second_queue)
second_queue -= src
if(l == length(second_queue))
SStimer.bucket_count--
timer_subsystem.bucket_count--
// Remove the timed event from the bucket, ensuring to maintain
// the integrity of the bucket's list if relevant
@@ -473,20 +477,20 @@ SUBSYSTEM_DEF(timer)
// Check if this timed event should be diverted to the client time bucket, or the secondary queue
var/list/L
if (flags & TIMER_CLIENT_TIME)
L = SStimer.clienttime_timers
else if (timeToRun >= TIMER_MAX)
L = SStimer.second_queue
L = timer_subsystem.clienttime_timers
else if (timeToRun >= TIMER_MAX(timer_subsystem))
L = timer_subsystem.second_queue
if(L)
BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY)
return
// Get a local reference to the bucket list, this is faster than referencing the datum
var/list/bucket_list = SStimer.bucket_list
var/list/bucket_list = timer_subsystem.bucket_list
// Find the correct bucket for this timed event
var/bucket_pos = BUCKET_POS(src)
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
SStimer.bucket_count++
timer_subsystem.bucket_count++
// If there is no timed event at this position, then the bucket is 'empty'
// and we can just set this event to that position
@@ -522,7 +526,7 @@ SUBSYSTEM_DEF(timer)
* * wait deciseconds to run the timer for
* * flags flags for this timer, see: code\__DEFINES\subsystems.dm
*/
/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, file, line)
/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, datum/controller/subsystem/timer/timer_subsystem, file, line)
if (!callback)
CRASH("addtimer called without a callback")
@@ -538,6 +542,8 @@ SUBSYSTEM_DEF(timer)
if(wait >= INFINITY)
CRASH("Attempted to create timer with INFINITY delay")
timer_subsystem = timer_subsystem || SStimer
// Generate hash if relevant for timed events with the TIMER_UNIQUE flag
var/hash
if (flags & TIMER_UNIQUE)
@@ -547,7 +553,7 @@ SUBSYSTEM_DEF(timer)
hashlist += callback.arguments
hash = hashlist.Join("|||||||")
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
var/datum/timedevent/hash_timer = timer_subsystem.hashes[hash]
if(hash_timer)
if (hash_timer.spent) // it's pending deletion, pretend it doesn't exist.
hash_timer.hash = null // but keep it from accidentally deleting us
@@ -562,7 +568,7 @@ SUBSYSTEM_DEF(timer)
else if(flags & TIMER_OVERRIDE)
stack_trace("TIMER_OVERRIDE used without TIMER_UNIQUE")
var/datum/timedevent/timer = new(callback, wait, flags, hash, file && "[file]:[line]")
var/datum/timedevent/timer = new(callback, wait, flags, timer_subsystem, hash, file && "[file]:[line]")
return timer.id
/**
@@ -571,7 +577,7 @@ SUBSYSTEM_DEF(timer)
* Arguments:
* * id a timerid or a /datum/timedevent
*/
/proc/deltimer(id)
/proc/deltimer(id, datum/controller/subsystem/timer/timer_subsystem)
if (!id)
return FALSE
if (id == TIMER_ID_NULL)
@@ -579,8 +585,9 @@ SUBSYSTEM_DEF(timer)
if (istype(id, /datum/timedevent))
qdel(id)
return TRUE
timer_subsystem = timer_subsystem || SStimer
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id]
if (timer && (!timer.spent || timer.flags & TIMER_DELETE_ME))
qdel(timer)
return TRUE
@@ -592,7 +599,7 @@ SUBSYSTEM_DEF(timer)
* Arguments:
* * id a timerid or a /datum/timedevent
*/
/proc/timeleft(id)
/proc/timeleft(id, datum/controller/subsystem/timer/timer_subsystem)
if (!id)
return null
if (id == TIMER_ID_NULL)
@@ -600,8 +607,9 @@ SUBSYSTEM_DEF(timer)
if (istype(id, /datum/timedevent))
var/datum/timedevent/timer = id
return timer.timeToRun - world.time
timer_subsystem = timer_subsystem || SStimer
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id]
return (timer && !timer.spent) ? timer.timeToRun - world.time : null
#undef BUCKET_LEN