From 4e05dd63f94a25b6c2fc23df376c0e1501b55941 Mon Sep 17 00:00:00 2001 From: pigeons Date: Tue, 25 Jul 2017 19:48:21 -0400 Subject: [PATCH] Fixes timer id collision (#29437) The list of active timers timer_id_dict identifies timers by their numerical id as a string. However this was done using embedded expressions inside the string, which calls num2text with a default of 6 significant figures. This means anything at or above 1 million is expressed in scientific notation. "timer[1000000]" -> "timer1e+06" "timer[1000001]" -> "timer1e+06" Calling num2text manually with 8 significant figures kicks the collision problem down the road to 2^24 (16 million). nextid is now selectively incremented and is looped back to 1 when reaching the 2^24 threshold. Also now includes collision checking. --- code/controllers/subsystem/timer.dm | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index daf7dc2ca9d..5696fa09e72 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -1,5 +1,6 @@ #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) +#define TIMER_ID_MAX (2**24) //max float with integer precision SUBSYSTEM_DEF(timer) name = "Timer" @@ -225,18 +226,23 @@ SUBSYSTEM_DEF(timer) var/static/nextid = 1 /datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash) - id = nextid++ + id = TIMER_ID_NULL src.callBack = callBack src.timeToRun = timeToRun src.flags = flags src.hash = hash - name = "Timer: [id]: TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" - if (flags & TIMER_UNIQUE) SStimer.hashes[hash] = src if (flags & TIMER_STOPPABLE) - SStimer.timer_id_dict["timerid[id]"] = src + do + if (nextid >= TIMER_ID_MAX) + nextid = 1 + id = nextid++ + while(SStimer.timer_id_dict["timerid" + num2text(id, 8)]) + SStimer.timer_id_dict["timerid" + num2text(id, 8)] = src + + name = "Timer: " + num2text(id, 8) + ", TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" if (callBack.object != GLOBAL_PROC) LAZYADD(callBack.object.active_timers, src) @@ -297,7 +303,7 @@ SUBSYSTEM_DEF(timer) callBack = null if (flags & TIMER_STOPPABLE) - SStimer.timer_id_dict -= "timerid[id]" + SStimer.timer_id_dict -= "timerid" + num2text(id, 8) if (flags & TIMER_CLIENT_TIME) SStimer.clienttime_timers -= src @@ -378,9 +384,7 @@ SUBSYSTEM_DEF(timer) timeToRun = REALTIMEOFDAY + wait var/datum/timedevent/timer = new(callback, timeToRun, flags, hash) - if (flags & TIMER_STOPPABLE) - return timer.id - return TIMER_ID_NULL + return timer.id /proc/deltimer(id) if (!id) @@ -391,6 +395,7 @@ SUBSYSTEM_DEF(timer) if (istype(id, /datum/timedevent)) qdel(id) return TRUE + //id is string var/datum/timedevent/timer = SStimer.timer_id_dict["timerid[id]"] if (timer && !timer.spent) qdel(timer)