Files
Aurora.3/code/controllers/Processes/scheduler.dm
Lohikar 97856e8735 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.
2017-03-05 02:01:06 +02:00

167 lines
4.6 KiB
Plaintext

/var/datum/controller/process/scheduler/scheduler
/************
* Scheduler *
************/
/datum/controller/process/scheduler
var/list/datum/scheduled_task/tasks = list()
var/datum/scheduled_task/head
/datum/controller/process/scheduler/setup()
name = "scheduler"
schedule_interval = 2
scheduler = src
/datum/controller/process/scheduler/doWork()
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.
// 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))
old_tasks -= task
continue
if (task.destroyed)
old_tasks -= task
task.kill()
continue
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, "[tasks.len] task\s")
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
queue(st)
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
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)
/proc/schedule_task_with_source_in(var/in_time, var/source, var/procedure, var/list/arguments = list())
return schedule_task_with_source(world.time + in_time, source, procedure, arguments)
/proc/schedule_task(var/trigger_time, var/procedure, var/list/arguments)
var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/destroy_scheduled_task, list())
scheduler.schedule(st)
return st
/proc/schedule_task_with_source(var/trigger_time, var/source, var/procedure, var/list/arguments)
var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/destroy_scheduled_task, list())
scheduler.schedule(st)
return st
/proc/schedule_repeating_task(var/trigger_time, var/repeat_interval, var/procedure, var/list/arguments)
var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
scheduler.schedule(st)
return st
/proc/schedule_repeating_task_with_source(var/trigger_time, var/repeat_interval, var/source, var/procedure, var/list/arguments)
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