mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-03 22:12:38 +00:00
Improved scheduler (#1864)
changes: Rewrote scheduler to use a sorted linked-list to store scheduled tasks so that only one element needs to be checked if none of the tasks are ready to fire. Scheduler now checks tasks every 2 ds. Adding tasks to an empty scheduler or tasks that are triggered after any existing tasks is fast, but adding tasks that trigger between two existing tasks requires iterating through some of the task list. It's no addtimer(), but it's something.
This commit is contained in:
@@ -30,3 +30,5 @@
|
||||
last_slept = TimeOfHour; \
|
||||
tick_start = world.tick_usage; \
|
||||
}
|
||||
|
||||
#define PSCHED_CHECK_TICK (world.tick_usage > 100 || (world.tick_usage - tick_start) > tick_allowance)
|
||||
|
||||
@@ -4,54 +4,141 @@
|
||||
* Scheduler *
|
||||
************/
|
||||
/datum/controller/process/scheduler
|
||||
var/list/scheduled_tasks
|
||||
var/tick_completed = TRUE
|
||||
var/list/queued_tasks
|
||||
var/list/datum/scheduled_task/tasks = list()
|
||||
var/datum/scheduled_task/head
|
||||
|
||||
/datum/controller/process/scheduler/setup()
|
||||
name = "scheduler"
|
||||
schedule_interval = 2 SECONDS
|
||||
scheduled_tasks = list()
|
||||
schedule_interval = 2
|
||||
scheduler = src
|
||||
|
||||
/datum/controller/process/scheduler/doWork()
|
||||
if (tick_completed)
|
||||
queued_tasks = scheduled_tasks.Copy()
|
||||
tick_completed = FALSE
|
||||
while (head && !PSCHED_CHECK_TICK)
|
||||
if (head.destroyed)
|
||||
head = head.next
|
||||
tasks -= head
|
||||
head.kill()
|
||||
continue
|
||||
if (head.trigger_time >= world.time)
|
||||
return // Nothing after this will be ready to fire.
|
||||
|
||||
while (queued_tasks.len)
|
||||
var/datum/scheduled_task/task = queued_tasks[queued_tasks.len]
|
||||
queued_tasks.len--
|
||||
// This one's ready to fire, process it.
|
||||
var/datum/scheduled_task/task = head
|
||||
head = task.next
|
||||
task.pre_process()
|
||||
task.process()
|
||||
task.post_process()
|
||||
if (task.destroyed) // post_process probably destroyed it.
|
||||
tasks -= task
|
||||
task.kill()
|
||||
|
||||
/datum/controller/process/scheduler/proc/queue(datum/scheduled_task/task)
|
||||
if (!task || !task.trigger_time)
|
||||
warning("scheduler: Invalid task queued! Ignoring.")
|
||||
return
|
||||
// Reset this in-case we're doing a rebuild.
|
||||
task.next = null
|
||||
if (!head && !tasks.len)
|
||||
head = task
|
||||
tasks += task
|
||||
return
|
||||
|
||||
if (!head) // Head's missing but we still have tasks, rebuild.
|
||||
tasks += task
|
||||
rebuild_queue()
|
||||
return
|
||||
|
||||
var/datum/scheduled_task/curr = head
|
||||
while (curr.next && curr.trigger_time < task.trigger_time)
|
||||
curr = curr.next
|
||||
|
||||
if (!curr.next)
|
||||
// We're at the end of the queue, just append.
|
||||
curr.next = task
|
||||
tasks += task
|
||||
return
|
||||
|
||||
// Inserting midway into the list.
|
||||
var/old_next = curr.next
|
||||
curr.next = task
|
||||
task.next = old_next
|
||||
tasks += task
|
||||
|
||||
// Rebuilds the queue linked-list, removing invalid or destroyed entries.
|
||||
/datum/controller/process/scheduler/proc/rebuild_queue()
|
||||
log_debug("scheduler: Rebuilding queue.")
|
||||
var/list/old_tasks = tasks
|
||||
tasks = list()
|
||||
if (!old_tasks.len)
|
||||
log_debug("scheduler: rebuild was called on empty queue! Aborting.")
|
||||
return
|
||||
|
||||
// Find the head.
|
||||
for (var/thing in old_tasks)
|
||||
var/datum/scheduled_task/task = thing
|
||||
if (QDELETED(task))
|
||||
scheduled_tasks -= task
|
||||
old_tasks -= task
|
||||
continue
|
||||
|
||||
if (world.time > task.trigger_time)
|
||||
unschedule(task)
|
||||
task.pre_process()
|
||||
task.process()
|
||||
task.post_process()
|
||||
F_SCHECK
|
||||
if (task.destroyed)
|
||||
old_tasks -= task
|
||||
task.kill()
|
||||
continue
|
||||
|
||||
tick_completed = TRUE
|
||||
if (!head || task.trigger_time < head.trigger_time)
|
||||
head = task
|
||||
|
||||
if (!head)
|
||||
log_debug("scheduler: unable to find head! Purging task queue.")
|
||||
for (var/thing in old_tasks)
|
||||
var/datum/scheduled_task/task = thing
|
||||
if (QDELETED(task))
|
||||
continue
|
||||
|
||||
task.kill()
|
||||
|
||||
head = null
|
||||
return
|
||||
|
||||
// Don't queue the head.
|
||||
tasks += head
|
||||
old_tasks -= head
|
||||
|
||||
// Now rebuild the queue.
|
||||
for (var/thing in old_tasks)
|
||||
var/datum/scheduled_task/task = thing
|
||||
|
||||
queue(task)
|
||||
|
||||
log_debug("scheduler: Queue diff is [old_tasks.len - tasks.len].")
|
||||
|
||||
/datum/controller/process/scheduler/statProcess()
|
||||
..()
|
||||
stat(null, "[scheduled_tasks.len] tasks, [queued_tasks.len] queued")
|
||||
stat(null, "[tasks.len] task\s")
|
||||
|
||||
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
|
||||
scheduled_tasks += st
|
||||
destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
|
||||
queue(st)
|
||||
|
||||
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
|
||||
if(st in scheduled_tasks)
|
||||
scheduled_tasks -= st
|
||||
destroyed_event.unregister(st, src)
|
||||
st.destroyed = TRUE
|
||||
|
||||
/**********
|
||||
* Helpers *
|
||||
**********/
|
||||
/proc/schedule(source, the_proc, time, ...)
|
||||
if (time < 0)
|
||||
return
|
||||
time += world.time
|
||||
var/list/the_args
|
||||
if (length(args) > 3)
|
||||
the_args = args.Copy(4)
|
||||
else
|
||||
the_args = list()
|
||||
if (source)
|
||||
return schedule_task_with_source(time, the_proc, the_args)
|
||||
else
|
||||
return schedule_task(time, the_proc, the_args)
|
||||
|
||||
/proc/schedule_task_in(var/in_time, var/procedure, var/list/arguments = list())
|
||||
return schedule_task(world.time + in_time, procedure, arguments)
|
||||
|
||||
@@ -77,68 +164,3 @@
|
||||
var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
|
||||
scheduler.schedule(st)
|
||||
return st
|
||||
|
||||
/*************
|
||||
* Task Datum *
|
||||
*************/
|
||||
/datum/scheduled_task
|
||||
var/trigger_time
|
||||
var/procedure
|
||||
var/list/arguments
|
||||
var/task_after_process
|
||||
var/list/task_after_process_args
|
||||
|
||||
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
..()
|
||||
src.trigger_time = trigger_time
|
||||
src.procedure = procedure
|
||||
src.arguments = arguments ? arguments : list()
|
||||
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
|
||||
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
|
||||
task_after_process_args += src
|
||||
|
||||
/datum/scheduled_task/Destroy()
|
||||
procedure = null
|
||||
arguments.Cut()
|
||||
task_after_process = null
|
||||
task_after_process_args.Cut()
|
||||
return ..()
|
||||
|
||||
/datum/scheduled_task/proc/pre_process()
|
||||
task_triggered_event.raise_event(list(src))
|
||||
|
||||
/datum/scheduled_task/proc/process()
|
||||
if(procedure)
|
||||
call(procedure)(arglist(arguments))
|
||||
|
||||
/datum/scheduled_task/proc/post_process()
|
||||
call(task_after_process)(arglist(task_after_process_args))
|
||||
|
||||
// Resets the trigger time, has no effect if the task has already triggered
|
||||
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
|
||||
src.trigger_time = world.time + trigger_in
|
||||
|
||||
/datum/scheduled_task/source
|
||||
var/datum/source
|
||||
|
||||
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
src.source = source
|
||||
destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
|
||||
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
|
||||
|
||||
/datum/scheduled_task/source/Destroy()
|
||||
source = null
|
||||
return ..()
|
||||
|
||||
/datum/scheduled_task/source/process()
|
||||
call(source, procedure)(arglist(arguments))
|
||||
|
||||
/datum/scheduled_task/source/proc/source_destroyed()
|
||||
qdel(src)
|
||||
|
||||
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
|
||||
qdel(st)
|
||||
|
||||
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
|
||||
st.trigger_time = world.time + trigger_delay
|
||||
scheduler.schedule(st)
|
||||
|
||||
79
code/datums/scheduled_task.dm
Normal file
79
code/datums/scheduled_task.dm
Normal file
@@ -0,0 +1,79 @@
|
||||
/*************
|
||||
* Task Datum *
|
||||
*************/
|
||||
/datum/scheduled_task
|
||||
var/trigger_time
|
||||
var/procedure
|
||||
var/list/arguments
|
||||
var/task_after_process
|
||||
var/list/task_after_process_args
|
||||
var/datum/scheduled_task/next
|
||||
var/destroyed = FALSE
|
||||
|
||||
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
..()
|
||||
src.trigger_time = trigger_time
|
||||
src.procedure = procedure
|
||||
src.arguments = arguments ? arguments : list()
|
||||
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
|
||||
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
|
||||
task_after_process_args += src
|
||||
|
||||
/datum/scheduled_task/Destroy()
|
||||
if (!destroyed)
|
||||
kill(no_del = TRUE)
|
||||
procedure = null
|
||||
arguments.Cut()
|
||||
task_after_process = null
|
||||
task_after_process_args.Cut()
|
||||
return ..()
|
||||
|
||||
/datum/scheduled_task/proc/pre_process()
|
||||
return
|
||||
|
||||
/datum/scheduled_task/proc/process()
|
||||
if(procedure)
|
||||
call(procedure)(arglist(arguments))
|
||||
|
||||
/datum/scheduled_task/proc/post_process()
|
||||
call(task_after_process)(arglist(task_after_process_args))
|
||||
|
||||
// Resets the trigger time, has no effect if the task has already triggered
|
||||
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
|
||||
src.trigger_time = world.time + trigger_in
|
||||
|
||||
/datum/scheduled_task/proc/kill(no_del = FALSE)
|
||||
if (!destroyed)
|
||||
warning("scheduler: Non-destroyed task was killed!")
|
||||
destroyed = TRUE
|
||||
|
||||
if (src in scheduler.tasks)
|
||||
warning("scheduler: Task was not cleaned up correctly, rebuilding scheduler queue!")
|
||||
scheduler.rebuild_queue()
|
||||
|
||||
if (!no_del)
|
||||
qdel(src)
|
||||
|
||||
/datum/scheduled_task/source
|
||||
var/datum/source
|
||||
|
||||
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
src.source = source
|
||||
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
|
||||
|
||||
/datum/scheduled_task/source/Destroy()
|
||||
source = null
|
||||
return ..()
|
||||
|
||||
/datum/scheduled_task/source/process()
|
||||
call(source, procedure)(arglist(arguments))
|
||||
|
||||
/datum/scheduled_task/source/proc/source_destroyed()
|
||||
destroyed = TRUE
|
||||
|
||||
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
|
||||
st.destroyed = TRUE
|
||||
|
||||
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
|
||||
st.trigger_time = world.time + trigger_delay
|
||||
scheduler.schedule(st)
|
||||
@@ -30,8 +30,7 @@
|
||||
var/obj/item/stack/material/steel/repairing
|
||||
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
|
||||
var/open_duration = 150//How long it stays open
|
||||
var/datum/scheduled_task/close_task
|
||||
var/datum/scheduled_task/hatch_task
|
||||
|
||||
|
||||
var/hashatch = 0//If 1, this door has hatches, and certain small creatures can move through them without opening the door
|
||||
var/hatchstate = 0//0: closed, 1: open
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to
|
||||
var/dirt = 0
|
||||
|
||||
var/datum/scheduled_task/unwet_task
|
||||
|
||||
// This is not great.
|
||||
/turf/simulated/proc/wet_floor(var/wet_val = 1)
|
||||
if(wet_val < wet)
|
||||
@@ -26,22 +24,17 @@
|
||||
wet_overlay = image('icons/effects/water.dmi',src,"wet_floor")
|
||||
overlays += wet_overlay
|
||||
|
||||
if(unwet_task)
|
||||
unwet_task.trigger_task_in(180 SECONDS)
|
||||
else
|
||||
unwet_task = schedule_task_in(180 SECONDS)
|
||||
task_triggered_event.register(unwet_task, src, /turf/simulated/proc/task_unwet_floor)
|
||||
|
||||
/turf/simulated/proc/task_unwet_floor(var/triggered_task)
|
||||
if(triggered_task == unwet_task)
|
||||
unwet_task = null
|
||||
unwet_floor()
|
||||
schedule_task_with_source_in(180 SECONDS, src, .proc/unwet_floor)
|
||||
|
||||
/turf/simulated/proc/unwet_floor()
|
||||
wet = 0
|
||||
if(wet_overlay)
|
||||
overlays -= wet_overlay
|
||||
wet_overlay = null
|
||||
--wet
|
||||
if (wet < 1)
|
||||
wet = 0
|
||||
if(wet_overlay)
|
||||
overlays -= wet_overlay
|
||||
wet_overlay = null
|
||||
else
|
||||
schedule_task_with_source_in(180 SECONDS, src, .proc/unwet_floor)
|
||||
|
||||
/turf/simulated/clean_blood()
|
||||
for(var/obj/effect/decal/cleanable/blood/B in contents)
|
||||
@@ -54,11 +47,6 @@
|
||||
holy = 1
|
||||
levelupdate()
|
||||
|
||||
/turf/simulated/Destroy()
|
||||
qdel(unwet_task)
|
||||
unwet_task = null
|
||||
return ..()
|
||||
|
||||
/turf/simulated/proc/initialize()
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user