Skip to content

Commit

Permalink
Merge pull request #4335 from out-of-phaze/feature/port-dpc
Browse files Browse the repository at this point in the history
Port DPC from CitRP, add support for unique zero-wait timers
  • Loading branch information
MistakeNot4892 authored Aug 22, 2024
2 parents de2242e + 953b5b0 commit 6a7e7f8
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 1 deletion.
9 changes: 9 additions & 0 deletions code/__defines/MC.dm
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ if(Datum.is_processing) {\
#define TIMER_NO_HASH_WAIT BITFLAG(4) // For unique timers: don't distinguish timers by wait.
#define TIMER_LOOP BITFLAG(5) // Repeat the timer until it's deleted or the parent is destroyed.

// TIMER_OVERRIDE is impossible to support because we don't track that for DPC queued calls, and adding a third list for that would be a lot of overhead for no real benefit
// TIMER_STOPPABLE can't work because it uses timer IDs instead of hashes, and DPC queued calls don't have IDs.
// TIMER_LOOP doesn't work because it needs to be a timer that can re-insert in the list, and a zero-wait looping timer should really be a ticker subsystem instead.
// Update these defines if any of those change.
/// These are the flags forbidden when putting zero-wait timers on SSdpc instead of SStimer.
#define DPC_FORBID_FLAGS TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE | TIMER_LOOP
/// These are the flags forbidden when putting zero-wait TIMER_UNIQUE timers on SSdpc instead of SStimer.
#define UDPC_FORBID_FLAGS TIMER_OVERRIDE | TIMER_STOPPABLE | TIMER_LOOP

#define TIMER_ID_NULL -1

/**
Expand Down
1 change: 1 addition & 0 deletions code/__defines/subsystem-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// SS_TICKER
#define SS_PRIORITY_OVERLAY 100 // Applies overlays. May cause overlay pop-in if it gets behind.
#define SS_PRIORITY_TIMER 20
#define SS_PRIORITY_DPC 19

// Normal
#define SS_PRIORITY_TICKER 100 // Gameticker.
Expand Down
3 changes: 3 additions & 0 deletions code/__defines/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,7 @@
#define RUNLEVEL_SETUP 2
#define RUNLEVEL_GAME 4
#define RUNLEVEL_POSTGAME 8
/// default runlevels for most subsystems
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
/// all valid runlevels - subsystems with this will run all the time after their MC init stage.
#define RUNLEVELS_ALL (RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
55 changes: 55 additions & 0 deletions code/controllers/subsystems/DPC.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
This is pretty much just an optimization for wait=0 timers. They're relatively common, but generally don't actually need the more
complex features of SStimer. SSdpc can handle these timers instead (and it's a lot simpler than SStimer is), but it can't handle
timers with certain flags (check MC.dm). This doesn't need to be explicitly used, eligible timers are automatically converted.
*/

SUBSYSTEM_DEF(dpc)
name = "Delayed Procedure Call"
wait = 1
runlevels = RUNLEVELS_ALL
priority = SS_PRIORITY_DPC
flags = SS_TICKER | SS_NO_INIT

var/list/queued_calls = list()
var/avg = 0
var/list/unique_queued_calls = list()
var/unique_avg = 0

/datum/controller/subsystem/dpc/stat_entry()
return ..() + " Q: [queued_calls.len], AQ: ~[round(avg)], UQ: [unique_queued_calls.len], UAQ: ~[round(unique_avg)]"

/datum/controller/subsystem/dpc/fire(resumed = FALSE)
var/list/qc = queued_calls
var/list/uqc = unique_queued_calls
if (!resumed)
avg = MC_AVERAGE_FAST(avg, qc.len)
unique_avg = MC_AVERAGE_FAST(unique_avg, uqc.len)

var/q_idex = 1

while (q_idex <= qc.len)
var/datum/callback/CB = qc[q_idex]
q_idex += 1

CB.InvokeAsync()

if (MC_TICK_CHECK)
break

if (q_idex > 1)
queued_calls.Cut(1, q_idex)

q_idex = 1 // Reuse this variable so we don't waste time allocating two
while (q_idex <= uqc.len)
var/hash = uqc[q_idex]
var/datum/callback/CB = uqc[hash]
q_idex += 1

CB.InvokeAsync()

if (MC_TICK_CHECK)
break

if (q_idex > 1)
unique_queued_calls.Cut(1, q_idex)
22 changes: 21 additions & 1 deletion code/controllers/subsystems/timer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ SUBSYSTEM_DEF(timer)
wait = 1 //SS_TICKER subsystem, so wait is in ticks
priority = SS_PRIORITY_TIMER
flags = SS_NO_INIT | SS_TICKER
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
runlevels = RUNLEVELS_ALL

/// Queue used for storing timers that do not fit into the current buckets
var/list/datum/timedevent/second_queue = list()
Expand Down Expand Up @@ -569,6 +569,10 @@ SUBSYSTEM_DEF(timer)
PRINT_STACK_TRACE("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not \
be supported and may refuse to run or run with a 0 wait")

if (wait == 0 && !(flags & DPC_FORBID_FLAGS))
SSdpc.queued_calls += callback
return

wait = max(NONUNIT_CEILING(wait, world.tick_lag), world.tick_lag)

if(wait >= INFINITY)
Expand Down Expand Up @@ -597,6 +601,22 @@ SUBSYSTEM_DEF(timer)
if (hash_timer.flags & TIMER_STOPPABLE)
. = hash_timer.id
return
else
if(wait == 0 || flags & TIMER_NO_HASH_WAIT)
// Check if a unique DPC queued call exists with the same hash
var/datum/callback/dpc_callback = SSdpc.unique_queued_calls[hash]
if(dpc_callback)
if(flags & TIMER_OVERRIDE)
// if this turns out to have too much overhead, could try setting
// the hash's callback to null and then just having SSdpc skip nulls?
SSdpc.unique_queued_calls -= hash
else
return // don't create a timer
// No timer with this hash exists, so we can use the fast unique-DPC path
// Have to make sure it doesn't have illegal flags for unique DPC though
if(wait == 0 && !(flags & UDPC_FORBID_FLAGS))
SSdpc.unique_queued_calls[hash] = callback
return
else if(flags & TIMER_OVERRIDE)
PRINT_STACK_TRACE("TIMER_OVERRIDE used without TIMER_UNIQUE")

Expand Down
1 change: 1 addition & 0 deletions nebula.dme
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@
#include "code\controllers\subsystems\configuration.dm"
#include "code\controllers\subsystems\daycycle.dm"
#include "code\controllers\subsystems\disposals.dm"
#include "code\controllers\subsystems\DPC.dm"
#include "code\controllers\subsystems\evac.dm"
#include "code\controllers\subsystems\event.dm"
#include "code\controllers\subsystems\fluids.dm"
Expand Down

0 comments on commit 6a7e7f8

Please sign in to comment.