processScheduler Update to 510 (#398)

Updates code to be 510 compile compatible.

Also introduces the new updates to the GOON processScheduler, which should make for better gameplay and less lag. Specially on high population.
This commit is contained in:
skull132
2016-06-23 02:47:12 +03:00
committed by GitHub
parent 6dfd6bf3a0
commit 02b42862c1
47 changed files with 725 additions and 1303 deletions
@@ -1,94 +0,0 @@
/datum/processSchedulerView
/datum/processSchedulerView/Topic(href, href_list)
if (!href_list["action"])
return
switch (href_list["action"])
if ("kill")
var/toKill = href_list["name"]
processScheduler.killProcess(toKill)
refreshProcessTable()
if ("enable")
var/toEnable = href_list["name"]
processScheduler.enableProcess(toEnable)
refreshProcessTable()
if ("disable")
var/toDisable = href_list["name"]
processScheduler.disableProcess(toDisable)
refreshProcessTable()
if ("refresh")
refreshProcessTable()
/datum/processSchedulerView/proc/refreshProcessTable()
windowCall("handleRefresh", getProcessTable())
/datum/processSchedulerView/proc/windowCall(var/function, var/data = null)
usr << output(data, "processSchedulerContext.browser:[function]")
/datum/processSchedulerView/proc/getProcessTable()
var/text = "<table class=\"table table-striped\"><thead><tr><td>Name</td><td>Avg(s)</td><td>Last(s)</td><td>Highest(s)</td><td>Tickcount</td><td>Tickrate</td><td>State</td><td>Action</td></tr></thead><tbody>"
// and the context of each
for (var/list/data in processScheduler.getStatusData())
text += "<tr>"
text += "<td>[data["name"]]</td>"
text += "<td>[num2text(data["averageRunTime"]/10,3)]</td>"
text += "<td>[num2text(data["lastRunTime"]/10,3)]</td>"
text += "<td>[num2text(data["highestRunTime"]/10,3)]</td>"
text += "<td>[num2text(data["ticks"],4)]</td>"
text += "<td>[data["schedule"]]</td>"
text += "<td>[data["status"]]</td>"
text += "<td><button class=\"btn kill-btn\" data-process-name=\"[data["name"]]\" id=\"kill-[data["name"]]\">Kill</button>"
if (data["disabled"])
text += "<button class=\"btn enable-btn\" data-process-name=\"[data["name"]]\" id=\"enable-[data["name"]]\">Enable</button>"
else
text += "<button class=\"btn disable-btn\" data-process-name=\"[data["name"]]\" id=\"disable-[data["name"]]\">Disable</button>"
text += "</td>"
text += "</tr>"
text += "</tbody></table>"
return text
/**
* getContext
* Outputs an interface showing stats for all processes.
*/
/datum/processSchedulerView/proc/getContext()
bootstrap_browse()
usr << browse('processScheduler.js', "file=processScheduler.js;display=0")
var/text = {"<html><head>
<title>Process Scheduler Detail</title>
<script type="text/javascript">var ref = '\ref[src]';</script>
[bootstrap_includes()]
<script type="text/javascript" src="processScheduler.js"></script>
</head>
<body>
<h2>Process Scheduler</h2>
<div class="btn-group">
<button id="btn-refresh" class="btn">Refresh</button>
</div>
<h3>The process scheduler controls [processScheduler.getProcessCount()] loops.<h3>"}
text += "<div id=\"processTable\">"
text += getProcessTable()
text += "</div></body></html>"
usr << browse(text, "window=processSchedulerContext;size=800x600")
/datum/processSchedulerView/proc/bootstrap_browse()
usr << browse('bower_components/jquery/dist/jquery.min.js', "file=jquery.min.js;display=0")
usr << browse('bower_components/bootstrap2.3.2/bootstrap/js/bootstrap.min.js', "file=bootstrap.min.js;display=0")
usr << browse('bower_components/bootstrap2.3.2/bootstrap/css/bootstrap.min.css', "file=bootstrap.min.css;display=0")
usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings-white.png', "file=glyphicons-halflings-white.png;display=0")
usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings.png', "file=glyphicons-halflings.png;display=0")
usr << browse('bower_components/json2/json2.js', "file=json2.js;display=0")
/datum/processSchedulerView/proc/bootstrap_includes()
return {"
<link rel="stylesheet" href="bootstrap.min.css" />
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="bootstrap.js"></script>
"}
@@ -1,27 +0,0 @@
/**
* testDyingUpdateQueueProcess
* This process is an example of a process using an updateQueue.
* The datums updated by this process behave badly and block the update loop
* by sleeping. If you #define UPDATE_QUEUE_DEBUG, you will see the updateQueue
* killing off its worker processes and spawning new ones to work around slow
* updates. This means that if you have a code path that sleeps for a long time
* in mob.Life once in a blue moon, the mob update loop will not hang.
*/
/datum/slowTestDatum/proc/wackyUpdateProcessName()
sleep(rand(0,20)) // Randomly REALLY slow :|
/datum/controller/process/testDyingUpdateQueueProcess
var/tmp/datum/updateQueue/updateQueueInstance
var/tmp/list/testDatums = list()
/datum/controller/process/testDyingUpdateQueueProcess/setup()
name = "Dying UpdateQueue Process"
schedule_interval = 30 // every 3 seconds
updateQueueInstance = new
for(var/i = 1, i < 30, i++)
testDatums.Add(new /datum/slowTestDatum)
/datum/controller/process/testDyingUpdateQueueProcess/doWork()
updateQueueInstance.init(testDatums, "wackyUpdateProcessName")
updateQueueInstance.Run()
@@ -1,35 +0,0 @@
/*
These are simple defaults for your project.
*/
#define DEBUG
var/global/datum/processSchedulerView/processSchedulerView
world
loop_checks = 0
New()
..()
processScheduler = new
processSchedulerView = new
mob
step_size = 8
New()
..()
verb
startProcessScheduler()
set name = "Start Process Scheduler"
processScheduler.setup()
processScheduler.start()
getProcessSchedulerContext()
set name = "Get Process Scheduler Status Panel"
processSchedulerView.getContext()
runUpdateQueueTests()
set name = "Run Update Queue Testsuite"
var/datum/updateQueueTests/t = new
t.runTests()
@@ -1,15 +0,0 @@
/**
* testHungProcess
* This process is an example of a simple update loop process that hangs.
*/
/datum/controller/process/testHungProcess/setup()
name = "Hung Process"
schedule_interval = 30 // every 3 seconds
/datum/controller/process/testHungProcess/doWork()
sleep(1000) // FUCK
// scheck is also responsible for handling hung processes. If a process
// hangs, and later resumes, but has already been killed by the scheduler,
// scheck will force the process to bail out.
scheck()
@@ -1,13 +0,0 @@
/**
* testNiceProcess
* This process is an example of a simple update loop process that is
* relatively fast.
*/
/datum/controller/process/testNiceProcess/setup()
name = "Nice Process"
schedule_interval = 10 // every second
/datum/controller/process/testNiceProcess/doWork()
sleep(rand(1,5)) // Just to pretend we're doing something
@@ -1,28 +0,0 @@
/**
* testSlowProcess
* This process is an example of a simple update loop process that is slow.
* The update loop here sleeps inside to provide an example, but if you had
* a computationally intensive loop process that is simply slow, you can use
* scheck() inside the loop to force it to yield periodically according to
* the sleep_interval var. By default, scheck will cause a loop to sleep every
* 2 ticks.
*/
/datum/controller/process/testSlowProcess/setup()
name = "Slow Process"
schedule_interval = 30 // every 3 seconds
/datum/controller/process/testSlowProcess/doWork()
// set background = 1 will cause loop constructs to sleep periodically,
// whenever the BYOND scheduler deems it productive to do so.
// This behavior is not always sufficient, nor is it always consistent.
// Rather than leaving it up to the BYOND scheduler, we can control it
// ourselves and leave nothing to the black box.
set background = 1
for(var/i=1,i<30,i++)
// Just to pretend we're doing something here
sleep(rand(3, 5))
// Forces this loop to yield(sleep) periodically.
scheck()
@@ -1,209 +0,0 @@
var/global/list/updateQueueTestCount = list()
/datum/updateQueueTests
var/start
proc
runTests()
world << "<b>Running 9 tests...</b>"
testUpdateQueuePerformance()
sleep(1)
testInplace()
sleep(1)
testInplaceUpdateQueuePerformance()
sleep(1)
testUpdateQueueReinit()
sleep(1)
testCrashingQueue()
sleep(1)
testEmptyQueue()
sleep(1)
testManySlowItemsInQueue()
sleep(1)
testVariableWorkerTimeout()
sleep(1)
testReallySlowItemInQueue()
sleep(1)
world << "<b>Finished!</b>"
beginTiming()
start = world.time
endTiming(text)
var/time = (world.time - start) / world.tick_lag
world << {"<b><font color="blue">Performance - [text] - <font color="green">[time]</font> ticks</font></b>"}
getCount()
return updateQueueTestCount[updateQueueTestCount.len]
incrementTestCount()
updateQueueTestCount.len++
updateQueueTestCount[updateQueueTestCount.len] = 0
assertCountEquals(count, text)
assertThat(getCount() == count, text)
assertCountLessThan(count, text)
assertThat(getCount() < count, text)
assertCountGreaterThan(count, text)
assertThat(getCount() > count, text)
assertThat(condition, text)
if (condition)
world << {"<font color="green"><b>PASS</b></font>: [text]"}
else
world << {"<b><font color="red">FAIL</font>: [text]</b>"}
testUpdateQueuePerformance()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=100000,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
beginTiming()
uq.Run()
endTiming("updating 100000 simple objects")
assertCountEquals(100000, "test that update queue updates all objects expected")
del(objs)
del(uq)
testUpdateQueueReinit()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=100,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
uq.Run()
objs = new
for(var/i=1,i<=100,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
uq.init(objs)
uq.Run()
assertCountEquals(200, "test that update queue reinitializes properly and updates all objects as expected.")
del(objs)
del(uq)
testInplace()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=100,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objects = objs, inplace = 1)
uq.Run()
assertThat(objs.len == 0, "test that update queue inplace option really works inplace")
assertCountEquals(100, "test that inplace update queue updates the right number of objects")
del(objs)
del(uq)
testInplaceUpdateQueuePerformance()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=100000,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
beginTiming()
uq.Run()
endTiming("updating 100000 simple objects in place")
del(objs)
del(uq)
testCrashingQueue()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=10,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
objs.Add(new /datum/uqTestDatum/crasher(updateQueueTestCount.len))
for(var/i=1,i<=10,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
uq.Run()
assertCountEquals(20, "test that update queue handles crashed update procs OK")
del(objs)
del(uq)
testEmptyQueue()
incrementTestCount()
var/list/objs = new
var/datum/updateQueue/uq = new(objs)
uq.Run()
assertCountEquals(0, "test that update queue doesn't barf on empty lists")
del(objs)
del(uq)
testManySlowItemsInQueue()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=30,i++)
objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
uq.Run()
assertCountEquals(30, "test that update queue slows down execution if too many objects are slow to update")
del(objs)
del(uq)
testVariableWorkerTimeout()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=20,i++)
objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs, workerTimeout=6)
uq.Run()
assertCountEquals(20, "test that variable worker timeout works properly")
del(objs)
del(uq)
testReallySlowItemInQueue()
incrementTestCount()
var/list/objs = new
for(var/i=1,i<=10,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
objs.Add(new /datum/uqTestDatum/reallySlow(updateQueueTestCount.len))
for(var/i=1,i<=10,i++)
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
var/datum/updateQueue/uq = new(objs)
uq.Run()
assertCountEquals(20, "test that update queue skips objects that are too slow to update")
del(objs)
del(uq)
datum/uqTestDatum
var/testNum
New(testNum)
..()
src.testNum = testNum
proc/update()
updateQueueTestCount[testNum]++
proc/lag(cycles)
set background = 1
for(var/i=0,i<cycles,)
i++
datum/uqTestDatum/fast
datum/uqTestDatum/slow
update()
set background = 1
var/start = world.timeofday
while(world.timeofday - start < 5) // lag 4 deciseconds
..()
datum/uqTestDatum/reallySlow
update()
set background = 1
var/start = world.timeofday
while(world.timeofday - start < 300) // lag 30 seconds
..()
datum/uqTestDatum/crasher
update()
CRASH("I crashed! (I am supposed to crash XD)")
..() // This should do nothing lol
@@ -1,24 +0,0 @@
/**
* testUpdateQueueProcess
* This process is an example of a process using an updateQueue.
* The datums updated by this process behave nicely and do not block.
*/
/datum/fastTestDatum/proc/wackyUpdateProcessName()
sleep(prob(10)) // Pretty quick, usually instant
/datum/controller/process/testUpdateQueueProcess
var/tmp/datum/updateQueue/updateQueueInstance
var/tmp/list/testDatums = list()
/datum/controller/process/testUpdateQueueProcess/setup()
name = "UpdateQueue Process"
schedule_interval = 20 // every 2 seconds
updateQueueInstance = new
for(var/i = 1, i < 30, i++)
testDatums.Add(new /datum/fastTestDatum)
/datum/controller/process/testUpdateQueueProcess/doWork()
updateQueueInstance.init(testDatums, "wackyUpdateProcessName")
updateQueueInstance.Run()
@@ -1,13 +0,0 @@
/**
* testBadZombieProcess
* This process is an example of a simple update loop process that hangs.
*/
/datum/controller/process/testZombieProcess/setup()
name = "Zombie Process"
schedule_interval = 30 // every 3 seconds
/datum/controller/process/testZombieProcess/doWork()
for (var/i = 0, i < 1000, i++)
sleep(1)
scheck()