mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 23:58:07 +01:00
Some more improvements and overhead shaving.
This commit is contained in:
@@ -226,8 +226,10 @@ var/datum/subsystem/garbage_collector/SSgarbage
|
||||
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
|
||||
/datum/proc/Destroy(force=FALSE)
|
||||
tag = null
|
||||
for(var/thing in SStimer.timer_src_dict[src])
|
||||
qdel(thing)
|
||||
var/list/timers = active_timers
|
||||
active_timers = null
|
||||
for(var/timer in timers)
|
||||
qdel(timer)
|
||||
return QDEL_HINT_QUEUE
|
||||
|
||||
/datum/var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define BUCKET_LEN (world.fps*10*60) //how many ticks should we keep in the bucket. (10 minutes worth)
|
||||
#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
|
||||
#define BUCKET_POS(timer) (round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) + 1)
|
||||
var/datum/subsystem/timer/SStimer
|
||||
|
||||
@@ -16,13 +16,12 @@ var/datum/subsystem/timer/SStimer
|
||||
var/head_offset = 0 //world.time of the first entry in the the bucket.
|
||||
var/practical_offset = 0 //index of the first non-empty item in the bucket.
|
||||
var/bucket_resolution = 0 //world.tick_lag the bucket was designed for
|
||||
var/bucket_count = 0 //how many timers are in the buckets
|
||||
|
||||
var/list/buckets //list of buckets, each bucket holds every timer that has to run that byond tick.
|
||||
var/list/bucket_list //list of buckets, each bucket holds every timer that has to run that byond tick.
|
||||
|
||||
var/list/timer_id_dict //list of all active timers assoicated to their timer id (for easy lookup)
|
||||
|
||||
var/list/timer_src_dict //list of everything with an active timer attached to it as key with a list of those timers as the value (for destroy)
|
||||
|
||||
var/list/clienttime_timers //special snowflake timers that run on fancy pansy "client time"
|
||||
|
||||
|
||||
@@ -31,7 +30,6 @@ var/datum/subsystem/timer/SStimer
|
||||
hashes = list()
|
||||
|
||||
timer_id_dict = list()
|
||||
timer_src_dict = list()
|
||||
|
||||
clienttime_timers = list()
|
||||
|
||||
@@ -39,83 +37,155 @@ var/datum/subsystem/timer/SStimer
|
||||
|
||||
|
||||
/datum/subsystem/timer/stat_entry(msg)
|
||||
..("P:[length(processing)] H:[length(hashes)] C:[length(clienttime_timers)]")
|
||||
..("B:[bucket_count] P:[length(processing)] H:[length(hashes)] C:[length(clienttime_timers)]")
|
||||
|
||||
/datum/subsystem/timer/fire()
|
||||
if (length(clienttime_timers))
|
||||
for (var/thing in clienttime_timers)
|
||||
var/datum/timedevent/event = thing
|
||||
if (event.timeToRun <= REALTIMEOFDAY)
|
||||
var/datum/callback/callback = event.callback
|
||||
qdel(event) //this is right, we delete the event before running it so that stack overflows don't cause us to repeatively run the same event
|
||||
callback.InvokeAsync()
|
||||
var/datum/timedevent/timer = thing
|
||||
if (timer.spent)
|
||||
qdel(timer)
|
||||
continue
|
||||
if (timer.timeToRun <= REALTIMEOFDAY)
|
||||
var/datum/callback/callBack = timer.callBack
|
||||
timer.spent = TRUE
|
||||
callBack.InvokeAsync()
|
||||
qdel(timer)
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
if (head_offset + (world.tick_lag * BUCKET_LEN) < world.time || length(src.buckets) != BUCKET_LEN || world.tick_lag != bucket_resolution)
|
||||
refill_buckets()
|
||||
if (head_offset + (world.tick_lag * BUCKET_LEN) < world.time || length(src.bucket_list) != BUCKET_LEN || world.tick_lag != bucket_resolution)
|
||||
shift_buckets()
|
||||
|
||||
var/list/buckets = src.buckets
|
||||
while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time && !MC_TICK_CHECK)
|
||||
var/datum/timedevent/event = buckets[practical_offset]
|
||||
while (event)
|
||||
var/datum/callback/callback = event.callback
|
||||
if (!callback)
|
||||
CRASH("Invalid event: event.timeToRun=[event.timeToRun]||qdeleted(event)=[qdeleted(event)]||world.time=[world.time]||head_offset=[head_offset]||practical_offset=[practical_offset]")
|
||||
qdel(event) //same as above
|
||||
callback.InvokeAsync()
|
||||
var/list/bucket_list = src.bucket_list
|
||||
var/static/list/spent = list()
|
||||
LOOP_OUTER
|
||||
while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time && !MC_TICK_CHECK)
|
||||
var/datum/timedevent/head = bucket_list[practical_offset]
|
||||
if (!head)
|
||||
practical_offset++
|
||||
if (MC_TICK_CHECK)
|
||||
break
|
||||
continue
|
||||
var/datum/timedevent/timer = head
|
||||
do
|
||||
var/datum/callback/callBack = timer.callBack
|
||||
if (!callBack || timer.spent)
|
||||
qdel(timer)
|
||||
bucket_resolution = null //force bucket recreation
|
||||
CRASH("Invalid timer: timer.timeToRun=[timer.timeToRun]||qdeleted(timer)=[qdeleted(timer)]||world.time=[world.time]||head_offset=[head_offset]||practical_offset=[practical_offset]||timer.spent=[timer.spent]")
|
||||
|
||||
spent += timer
|
||||
timer.spent = TRUE
|
||||
|
||||
callBack.InvokeAsync()
|
||||
|
||||
timer = timer.next
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
if (!timer || timer == head)
|
||||
break
|
||||
if (head.prev)
|
||||
head.prev.next = timer
|
||||
if (timer)
|
||||
if (timer.prev)
|
||||
timer.prev.next = head
|
||||
timer.prev = head.prev
|
||||
bucket_list[practical_offset] = timer
|
||||
break LOOP_OUTER
|
||||
while (timer && timer != head)
|
||||
|
||||
bucket_list[practical_offset++] = null
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
event = buckets[practical_offset]
|
||||
break
|
||||
|
||||
practical_offset++
|
||||
timer_id_dict -= spent
|
||||
bucket_count -= length(spent)
|
||||
for (var/timer in spent)
|
||||
qdel(timer)
|
||||
spent.len = 0
|
||||
|
||||
/datum/subsystem/timer/proc/refill_buckets()
|
||||
sortTim(processing, /proc/cmp_timer)
|
||||
src.buckets = new(BUCKET_LEN)
|
||||
|
||||
/datum/subsystem/timer/proc/shift_buckets()
|
||||
var/list/bucket_list = src.bucket_list
|
||||
var/list/alltimers = list()
|
||||
//collect the timers currently in the bucket
|
||||
for (var/bucket_head in bucket_list)
|
||||
if (!bucket_head)
|
||||
continue
|
||||
var/datum/timedevent/bucket_node = bucket_head
|
||||
do
|
||||
alltimers += bucket_node
|
||||
bucket_node = bucket_node.next
|
||||
while(bucket_node && bucket_node != bucket_head)
|
||||
|
||||
|
||||
bucket_list = new(BUCKET_LEN)
|
||||
src.bucket_list = bucket_list //cache update
|
||||
practical_offset = 1
|
||||
bucket_count = 0
|
||||
head_offset = world.time
|
||||
bucket_resolution = world.tick_lag
|
||||
var/list/buckets = src.buckets
|
||||
var/new_offset
|
||||
for (var/thing in processing)
|
||||
var/datum/timedevent/event = thing
|
||||
if (!event)
|
||||
processing -= event
|
||||
|
||||
alltimers += processing
|
||||
if (!length(alltimers))
|
||||
return
|
||||
|
||||
sortTim(alltimers, .proc/cmp_timer)
|
||||
|
||||
var/datum/timedevent/head = alltimers[1]
|
||||
|
||||
if (head.timeToRun < head_offset)
|
||||
head_offset = head.timeToRun
|
||||
|
||||
var/list/timers_to_remove = list()
|
||||
|
||||
for (var/thing in alltimers)
|
||||
var/datum/timedevent/timer = thing
|
||||
if (!timer)
|
||||
timers_to_remove += timer
|
||||
continue
|
||||
|
||||
if (isnull(new_offset))
|
||||
new_offset = event.timeToRun
|
||||
|
||||
var/bucket_pos = round((event.timeToRun - new_offset) / world.tick_lag) + 1
|
||||
var/bucket_pos = BUCKET_POS(timer)
|
||||
if (bucket_pos > BUCKET_LEN)
|
||||
break
|
||||
|
||||
var/datum/timedevent/bucket_head = buckets[bucket_pos]
|
||||
timers_to_remove += timer //remove it from the big list once we are done
|
||||
if (!timer.callBack || timer.spent)
|
||||
continue
|
||||
bucket_count++
|
||||
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
||||
if (!bucket_head)
|
||||
buckets[bucket_pos] = event
|
||||
bucket_list[bucket_pos] = timer
|
||||
timer.next = null
|
||||
timer.prev = null
|
||||
continue
|
||||
|
||||
if (!bucket_head.prev)
|
||||
bucket_head.prev = bucket_head
|
||||
event.next = bucket_head
|
||||
event.prev = bucket_head.prev
|
||||
event.next.prev = event
|
||||
event.prev.next = event
|
||||
head_offset = new_offset
|
||||
timer.next = bucket_head
|
||||
timer.prev = bucket_head.prev
|
||||
timer.next.prev = timer
|
||||
timer.prev.next = timer
|
||||
|
||||
processing = (alltimers - timers_to_remove)
|
||||
|
||||
|
||||
/datum/subsystem/timer/Recover()
|
||||
processing |= SStimer.processing
|
||||
hashes |= SStimer.hashes
|
||||
timer_id_dict |= SStimer.timer_id_dict
|
||||
bucket_list |= SStimer.bucket_list
|
||||
|
||||
/datum/var/list/active_timers
|
||||
/datum/timedevent
|
||||
var/id
|
||||
var/datum/callback/callback
|
||||
var/datum/callback/callBack
|
||||
var/timeToRun
|
||||
var/datum/timerid
|
||||
var/hash
|
||||
var/list/flags
|
||||
var/spent = FALSE //set to true right before running.
|
||||
|
||||
//cicular doublely linked list
|
||||
var/datum/timedevent/next
|
||||
@@ -123,9 +193,9 @@ var/datum/subsystem/timer/SStimer
|
||||
|
||||
var/static/nextid = 1
|
||||
|
||||
/datum/timedevent/New(datum/callback/callback, timeToRun, flags, hash)
|
||||
/datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash)
|
||||
id = nextid++
|
||||
src.callback = callback
|
||||
src.callBack = callBack
|
||||
src.timeToRun = timeToRun
|
||||
src.flags = flags
|
||||
|
||||
@@ -135,36 +205,36 @@ var/datum/subsystem/timer/SStimer
|
||||
|
||||
SStimer.timer_id_dict["timerid[id]"] = src
|
||||
|
||||
if (callback.object != GLOBAL_PROC)
|
||||
SStimer.timer_src_dict[callback.object] = src
|
||||
if (callBack.object != GLOBAL_PROC)
|
||||
LAZYINITLIST(callBack.object.active_timers)
|
||||
callBack.object.active_timers += src
|
||||
|
||||
if (flags & TIMER_CLIENT_TIME)
|
||||
SStimer.clienttime_timers += src
|
||||
return
|
||||
|
||||
SStimer.processing += src
|
||||
|
||||
//get the list of buckets
|
||||
var/list/buckets = SStimer.buckets
|
||||
var/list/bucket_list = SStimer.bucket_list
|
||||
//calculate our place in the bucket list
|
||||
var/bucket_pos = BUCKET_POS(src)
|
||||
//we are too far aways from needing to run to be in the bucket list, refill_buckets() will handle us.
|
||||
if (bucket_pos > length(buckets))
|
||||
//we are too far aways from needing to run to be in the bucket list, shift_buckets() will handle us.
|
||||
if (bucket_pos > length(bucket_list))
|
||||
SStimer.processing += src
|
||||
return
|
||||
//get the bucket for our tick
|
||||
var/datum/timedevent/event = buckets[bucket_pos]
|
||||
//empty bucket, we will just add ourself
|
||||
if (!event)
|
||||
buckets[bucket_pos] = src
|
||||
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
||||
SStimer.bucket_count++
|
||||
//empty bucket, we will just add ourselves
|
||||
if (!bucket_head)
|
||||
bucket_list[bucket_pos] = src
|
||||
if (bucket_pos < SStimer.practical_offset)
|
||||
SStimer.practical_offset = bucket_pos
|
||||
return
|
||||
//other wise, lets do a simplified linked list add.
|
||||
if (!event.prev)
|
||||
event.prev = event
|
||||
|
||||
next = event
|
||||
prev = event.prev
|
||||
if (!bucket_head.prev)
|
||||
bucket_head.prev = bucket_head
|
||||
next = bucket_head
|
||||
prev = bucket_head.prev
|
||||
next.prev = src
|
||||
prev.next = src
|
||||
|
||||
@@ -173,37 +243,45 @@ var/datum/subsystem/timer/SStimer
|
||||
if (hash)
|
||||
SStimer.hashes -= hash
|
||||
|
||||
SStimer.timer_id_dict -= "timerid[id]"
|
||||
|
||||
if (callback && callback.object != GLOBAL_PROC)
|
||||
SStimer.timer_src_dict -= callback.object
|
||||
if (callBack && callBack.object != GLOBAL_PROC && callBack.object.active_timers)
|
||||
callBack.object.active_timers -= src
|
||||
UNSETEMPTY(callBack.object.active_timers)
|
||||
|
||||
callback = null
|
||||
callBack = null
|
||||
|
||||
if (flags & TIMER_CLIENT_TIME)
|
||||
SStimer.clienttime_timers -= src
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
SStimer.processing -= src
|
||||
if (!spent)
|
||||
if (prev == next && next)
|
||||
next.prev = null
|
||||
prev.next = null
|
||||
else
|
||||
if (prev)
|
||||
prev.next = next
|
||||
if (next)
|
||||
next.prev = prev
|
||||
|
||||
if (prev == next && next)
|
||||
next.prev = null
|
||||
prev.next = null
|
||||
SStimer.timer_id_dict -= "timerid[id]"
|
||||
|
||||
var/bucketpos = BUCKET_POS(src)
|
||||
var/datum/timedevent/buckethead
|
||||
var/list/bucket_list = SStimer.bucket_list
|
||||
|
||||
if (bucketpos > 0 && bucketpos <= length(bucket_list))
|
||||
buckethead = bucket_list[bucketpos]
|
||||
else
|
||||
SStimer.processing -= src
|
||||
SStimer.bucket_count--
|
||||
if (buckethead == src)
|
||||
bucket_list[bucketpos] = next
|
||||
else
|
||||
if (prev)
|
||||
prev.next = next
|
||||
|
||||
if (next)
|
||||
next.prev = prev
|
||||
|
||||
var/bucketpos = BUCKET_POS(src)
|
||||
var/datum/timedevent/buckethead
|
||||
var/list/buckets = SStimer.buckets
|
||||
|
||||
if (bucketpos > 0 && bucketpos <= length(buckets))
|
||||
buckethead = buckets[bucketpos]
|
||||
if (buckethead == src)
|
||||
buckets[bucketpos] = next
|
||||
if (prev && prev.next == src)
|
||||
prev.next = null
|
||||
if (next && next.prev == src)
|
||||
next.prev = null
|
||||
|
||||
prev = null
|
||||
next = null
|
||||
@@ -226,28 +304,34 @@ var/datum/subsystem/timer/SStimer
|
||||
hashlist += callback.arguments
|
||||
hash = hashlist.Join("|||||||")
|
||||
|
||||
var/datum/timedevent/hash_event = SStimer.hashes[hash]
|
||||
if(hash_event)
|
||||
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
|
||||
if(hash_timer)
|
||||
if (flags & TIMER_OVERRIDE)
|
||||
qdel(hash_event)
|
||||
qdel(hash_timer)
|
||||
else
|
||||
return hash_event.id
|
||||
return hash_timer.id
|
||||
|
||||
var/timeToRun = world.time + wait
|
||||
if (flags & TIMER_CLIENT_TIME)
|
||||
timeToRun = REALTIMEOFDAY + wait
|
||||
|
||||
var/datum/timedevent/event = new(callback, timeToRun, flags, hash)
|
||||
|
||||
|
||||
return event.id
|
||||
var/datum/timedevent/timer = new(callback, timeToRun, flags, hash)
|
||||
return timer.id
|
||||
|
||||
|
||||
/proc/deltimer(id)
|
||||
if (!id)
|
||||
return FALSE
|
||||
var/datum/timedevent/event = SStimer.timer_id_dict["timerid[id]"]
|
||||
if (event)
|
||||
qdel(event)
|
||||
if (!istext(id))
|
||||
if (istype(id, /datum/timedevent))
|
||||
qdel(id)
|
||||
return TRUE
|
||||
var/datum/timedevent/timer = SStimer.timer_id_dict["timerid[id]"]
|
||||
if (timer && !timer.spent)
|
||||
qdel(timer)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
#undef BUCKET_LEN
|
||||
#undef BUCKET_POS
|
||||
Reference in New Issue
Block a user