myspace / CommonLua /BufferedProcess.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
10.1 kB
--- Holds a flag indicating whether there are any pending reasons to suspend the process.
--- Holds a flag indicating whether there are any pending reasons to suspend the process.
---
--- @type boolean
SuspendProcessReasons = nil
--- Holds a flag indicating whether the process is currently suspended.
---
--- @type boolean
SuspendedProcessing = nil
--- A function that checks the current execution timestamp.
---
--- @type function
CheckExecutionTimestamp = empty_func
--- A function that checks for any remaining reasons to suspend the process.
---
--- @type function
CheckRemainingReason = empty_func
--- A function that unpacks a table.
---
--- @type function
table_unpack = table.unpack
--- A function that checks if two tables are equal.
---
--- @type function
table_iequal = table.iequal
local SuspendProcessReasons
local SuspendedProcessing
local CheckExecutionTimestamp = empty_func
local CheckRemainingReason = empty_func
local table_unpack = table.unpack
local table_iequal = table.iequal
if FirstLoad then
__process_params_meta = {
__eq = function(t1, t2)
if type(t1) ~= type(t2) or not rawequal(getmetatable(t1), getmetatable(t2)) then
return false
end
local count = t1[1]
if count ~= t2[1] then
return false
end
for i=2,count do
if t1[i] ~= t2[i] then
return false
end
end
return true
end
}
end
---
--- Packs the provided parameters into a table with a metatable that allows for efficient equality comparison.
---
--- @param obj any The first parameter to be packed.
--- @param ... any Additional parameters to be packed.
--- @return table A table containing the packed parameters, with a metatable that allows for efficient equality comparison.
---
local function PackProcessParams(obj, ...)
local count = select("#", ...)
if count == 0 then
return obj or false
end
return setmetatable({count + 2, obj, ...}, __process_params_meta)
end
---
--- Unpacks the parameters from a table that was packed using the `PackProcessParams` function.
---
--- @param params table A table containing the packed parameters, with a metatable that allows for efficient equality comparison.
--- @return any The unpacked parameters.
---
function UnpackProcessParams(params)
if type(params) ~= "table" or getmetatable(params) ~= __process_params_meta then
return params
end
return table_unpack(params, 2, params[1])
end
function OnMsg.DoneMap()
CheckRemainingReason()
SuspendProcessReasons = false
SuspendedProcessing = false
end
---
--- Executes any suspended functions for the given process.
---
--- @param process string The name of the process for which to execute suspended functions.
---
function ExecuteSuspended(process)
-- Implementation details omitted for brevity
end
local function ExecuteSuspended(process)
local delayed = SuspendedProcessing
local funcs_to_params = delayed and delayed[process]
if not funcs_to_params then
return
end
delayed[process] = nil
local procall = procall
for _, funcname in ipairs(funcs_to_params) do
local func = _G[funcname]
for _, params in ipairs(funcs_to_params[funcname]) do
dbg(CheckExecutionTimestamp(process, funcname, params, true))
procall(func, UnpackProcessParams(params))
end
end
end
---
--- Cancels the processing of routines from a named process.
---
--- @param process string The name of the process for which to cancel processing.
---
function CancelProcessing(process)
if not SuspendProcessReasons or not SuspendProcessReasons[process] then
return
end
if SuspendedProcessing then
SuspendedProcessing[process] = nil
end
SuspendProcessReasons[process] = nil
Msg("ProcessingResumed", process, "cancel")
end
--[[@@@
Checks if the processing of routines from a named process is currently suspended
@function bool IsProcessingSuspended(string process)
--]]
function IsProcessingSuspended(process)
local process_to_reasons = SuspendProcessReasons
return process_to_reasons and next(process_to_reasons[process])
end
--[[@@@
Suspends the processing of routines from a named process. Multiple suspending with the same reason would lead to an error.
@function void SuspendProcessing(string process, type reason, bool ignore_errors)
@param string process - the name of the process, which routines should be suspended.
@param type reason - the reason to be used in order to resume the processing later. Could be any type.
@param bool ignore_errors - ignore suspending errors (e.g. process already suspended).
--]]
function SuspendProcessing(process, reason, ignore_errors)
reason = reason or ""
local reasons = SuspendProcessReasons and SuspendProcessReasons[process]
if reasons and reasons[reason] then
assert(ignore_errors)
return
end
local now = GameTime()
if reasons then
reasons[reason] = now
return
end
SuspendProcessReasons = table.set(SuspendProcessReasons, process, reason, now)
Msg("ProcessingSuspended", process)
end
--[[@@@
Resumes the processing of routines from a named process. Resuming an already resumed process, or resuming it with time delay, would lead to an error.
@function void ResumeProcessing(string process, type reason, bool ignore_errors)
@param string process - the name of the process, which routines should be suspended.
@param type reason - the reason to be used in order to resume the processing later. Could be any type.
@param bool ignore_errors - ignore resume errors (e.g. process already resumed).
--]]
function ResumeProcessing(process, reason, ignore_errors)
reason = reason or ""
local reasons = SuspendProcessReasons and SuspendProcessReasons[process]
local suspended = reasons and reasons[reason]
if not suspended then
return
end
assert(ignore_errors or suspended == GameTime())
local now = GameTime()
reasons[reason] = nil
if next(reasons) ~= nil then
return
end
assert(not IsProcessingSuspended(process))
ExecuteSuspended(process)
Msg("ProcessingResumed", process)
end
--[[@@@
Execute a routine from a named process. If the process is currently suspended, the call will be registered in ordered to be executed once the process is resumed. Multiple calls with the same context will be registered as one.
@function void ExecuteProcess(string process, function func, table obj)
@param string process - the name of the process, which routines should be suspended.
@param function func - the function to be executed.
@param table obj - optional function context.
--]]
function ExecuteProcess(process, funcname, obj, ...)
if not IsProcessingSuspended(process) then
dbg(CheckExecutionTimestamp(process, funcname, obj))
return procall(_G[funcname], obj, ...)
end
local params = PackProcessParams(obj, ...)
local suspended = SuspendedProcessing
if not suspended then
suspended = {}
SuspendedProcessing = suspended
end
local funcs_to_params = suspended[process]
if not funcs_to_params then
suspended[process] = { funcname, [funcname] = {params} }
return
end
local objs = funcs_to_params[funcname]
if not objs then
funcs_to_params[#funcs_to_params + 1] = funcname
funcs_to_params[funcname] = {params}
return
end
table.insert_unique(objs, params)
end
----
if Platform.asserts then
local ExecutionTimestamps
function OnMsg.DoneMap()
ExecutionTimestamps = false
end
-- Rise an error if a routine from a process is executed twice in the same time
--[[@@@
Checks if a process routine has been executed more than once in the same time frame.
@function void CheckExecutionTimestamp(string process, string funcname, table obj, boolean delayed)
@param string process - the name of the process
@param string funcname - the name of the function being executed
@param table obj - the object context of the function being executed
@param boolean delayed - whether the function call is delayed
@return boolean - true if the function has been executed more than once, false otherwise
--]]
CheckExecutionTimestamp = function(process, funcname, obj, delayed)
if not config.DebugSuspendProcess then
return
end
if not ExecutionTimestamps then
ExecutionTimestamps = {}
CreateRealTimeThread(function()
Sleep(1)
ExecutionTimestamps = false
end)
end
local func_to_objs = ExecutionTimestamps[process]
if not func_to_objs then
func_to_objs = {}
ExecutionTimestamps[process] = func_to_objs
end
local objs_to_timestamp = func_to_objs[funcname]
if not objs_to_timestamp then
objs_to_timestamp = {}
func_to_objs[funcname] = objs_to_timestamp
end
obj = obj or false
local rtime, gtime = RealTime(), GameTime()
local timestamp = xxhash(rtime, gtime)
if timestamp == objs_to_timestamp[obj] then
print("Duplicated processing:", process, funcname, "time:", gtime, "obj:", obj and obj.class,
obj and obj.handle)
assert(false, string.format("Duplicated process routine: %s.%s", process, funcname))
else
objs_to_timestamp[obj] = timestamp
--[[
if IsValid(obj) then
local pos = obj:GetVisualPos()
local seed = xxhash(obj and obj.handle)
local len = 5*guim + BraidRandom(seed, 10*guim)
DbgAddVector(pos, len, RandColor(seed))
DbgAddText(funcname, pos + point(0, 0, len), RandColor(obj and obj.handle))
end
--]]
end
end
---
--- Checks if there are any remaining reasons for suspending a process.
--- If there are any remaining reasons, an assertion is triggered with the process name and reason.
---
--- @function CheckRemainingReason
--- @return nil
CheckRemainingReason = function()
local process = next(SuspendProcessReasons)
local reason = process and next(SuspendProcessReasons[process])
if reason then
assert(false, string.format("Process '%s' not resumed: %s", process, ValueToStr(reason)))
end
end
end -- Platform.asserts