ugh this was dumb
[QuestHelper.git] / timeslice.lua
blob3110defcb56a9221d3cb13a99f1cf808c49f2c9a
1 QuestHelper_File["timeslice.lua"] = "Development Version"
2 QuestHelper_Loadtime["timeslice.lua"] = GetTime()
4 -- Any non-local item here is part of an available public interface.
6 local coroutine_running = false
7 local coroutine_stop_time = 0
8 local coroutine_list = {}
9 local coroutine_route_pass = 1
11 local coroutine_verbose = false
13 local coroutine_time_used = {}
14 local coroutine_power_up = GetTime()
16 local coroutine_time_exceeded = 0
18 function QH_Timeslice_DumpPerf()
19 local sortable = {}
20 for k, v in pairs(coroutine_time_used) do
21 table.insert(sortable, {name = k, amount = v})
22 end
23 table.sort(sortable, function(a, b) return a.name < b.name end)
24 for _, v in pairs(sortable) do
25 QuestHelper:TextOut(string.format("%s: %f", QuestHelper:HighlightText(v.name), v.amount))
26 end
27 QuestHelper:TextOut(string.format("%s: %f", QuestHelper:HighlightText("poweron"), GetTime() - coroutine_power_up))
28 end
30 function QH_Timeslice_Yield()
31 if coroutine_running then
32 -- Check if we've run our alotted time
33 if GetTime() > coroutine_stop_time then
34 -- As a safety, reset stop time to 0. If somehow we fail to set it next time,
35 -- we'll be sure to yield promptly.
36 coroutine_stop_time = 0
37 coroutine.yield()
38 end
39 end
40 end
42 function QH_Timeslice_Bonus(quantity)
43 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %d bonus", quantity)) end
44 coroutine_route_pass = coroutine_route_pass + quantity
45 end
47 local prioritize = {
48 init = {100},
49 criteria = {10},
50 lzw = {-5},
51 compress = {-8, 5},
52 routing = {-10},
55 function QH_Timeslice_Add(workfunc, name)
56 local priority = prioritize[name] and prioritize[name][1] or 0
57 local sharding = prioritize[name] and prioritize[name][2] or 1
58 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s added (%s, %d)", name, tostring(workfunc), priority)) end
59 local ncoro = coroutine.create(workfunc)
60 QuestHelper: Assert(ncoro)
61 table.insert(coroutine_list, {priority = priority, sharding = sharding, name = name, coro = ncoro, active = true})
62 end
64 function QH_Timeslice_Toggle(name, flag)
65 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s toggled to %s", name, tostring(not not flag))) end
66 for _, v in pairs(coroutine_list) do
67 if v.name == name then v.active = flag end
68 end
69 end
71 local started = false
73 function QH_Timeslice_Work()
74 -- There's probably a better way to do this, but. Eh. Lua.
75 coro = nil
76 key = nil
77 for k, v in pairs(coroutine_list) do
78 if v.active then
79 --if v.sharding then QuestHelper:TextOut(string.format("%d mod %d is %d, %s", time(), v.sharding, bit.mod(time(), v.sharding), tostring(bit.mod(time(), v.sharding) == 0))) end
80 if (not v.sharding or bit.mod(time(), v.sharding) == 0) and (not coro or (v.priority > coro.priority)) then
81 coro = v
82 key = k
83 end
84 end
85 end
87 if coro then
88 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s running", coro.name)) end
90 if coroutine.status(coro.coro) == "dead" then -- Someone was claiming to get an infinite loop with this. I don't see how it's possible, but this should at least fix the infinite loop.
91 coroutine_list[key] = nil
92 QuestHelper: Assert(coroutine.status(coro.coro) ~= "dead")
93 end
95 local slicefactor = (QuestHelper_Pref.hide and 0.01 or (QuestHelper_Pref.perf_scale * math.min(coroutine_route_pass, 5)))
96 if not started then slicefactor = 5 * QuestHelper_Pref.perfload_scale * math.min(coroutine_route_pass, 5) end -- the init process gets much higher priority so we get done with it faster
97 local coroutine_intended_stop_time = GetTime() + 4e-3 * slicefactor
98 coroutine_stop_time = coroutine_intended_stop_time - coroutine_time_exceeded
99 coroutine_route_pass = coroutine_route_pass - 5
100 if coroutine_route_pass <= 0 then coroutine_route_pass = 1 end
102 local start = GetTime()
103 local state, err = true, nil -- default values for "we're fine"
104 if start < coroutine_stop_time then -- We don't want to just return on failure because we want to credit the exceeded time properly.
105 coroutine_running = true
106 state, err = coroutine.resume(coro.coro)
107 coroutine_running = false
109 local total = GetTime() - start
111 local coroutine_this_cycle_exceeded = GetTime() - coroutine_intended_stop_time -- may be either positive or negative
112 coroutine_time_exceeded = coroutine_time_exceeded + coroutine_this_cycle_exceeded
114 coroutine_time_used[coro.name] = (coroutine_time_used[coro.name] or 0) + total
116 if not state then
117 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s errored", coro.name)) end
118 QuestHelper_ErrorCatcher_ExplicitError(true, err, "", string.format("(Coroutine error in %s)\n", coro.name))
121 QuestHelper: Assert(coro.coro)
122 if coroutine.status(coro.coro) == "dead" then
123 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s complete", coro.name)) end
124 coroutine_list[key] = nil
127 if coro.name == "routing" then started = true end
128 else
129 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: no available tasks")) end
133 function QH_Timeslice_Increment(quantity, name)
134 local an = "(nc) " .. name
135 coroutine_time_used[an] = (coroutine_time_used[an] or 0) + quantity