neonucleus/data/OpenOS/lib/process.lua
IonutParau 687cfebd00 testing version of LuaBIOS and OpenOS
people were having issues getting them to work so now we promote consistency
2025-06-28 20:41:49 +02:00

196 lines
5.2 KiB
Lua

local process = {}
-------------------------------------------------------------------------------
--Initialize coroutine library--
process.list = setmetatable({}, {__mode="k"})
function process.findProcess(co)
co = co or coroutine.running()
for main, p in pairs(process.list) do
if main == co then
return p
end
for _, instance in pairs(p.instances) do
if instance == co then
return p
end
end
end
end
-------------------------------------------------------------------------------
function process.load(path, env, init, name)
checkArg(1, path, "string", "function")
checkArg(2, env, "table", "nil")
checkArg(3, init, "function", "nil")
checkArg(4, name, "string", "nil")
assert(type(path) == "string" or env == nil, "process cannot load function environments")
local p = process.findProcess()
env = env or p.env
local code
if type(path) == "string" then
code = function(...)
local fs, shell = require("filesystem"), require("shell")
local program, reason = shell.resolve(path, "lua")
if not program then
return require("tools/programLocations").reportNotFound(path, reason)
end
os.setenv("_", program)
local f = fs.open(program)
if f then
local shebang = (f:read(1024) or ""):match("^#!([^\n]+)")
f:close()
if shebang then
path = shebang:gsub("%s","")
return code(program, ...)
end
end
-- local command
return assert(loadfile(program, "bt", env))(...)
end
else -- path is code
code = path
end
local thread = nil
thread = coroutine.create(function(...)
-- pcall code so that we can remove it from the process list on exit
local result =
{
xpcall(function(...)
init = init or function(...) return ... end
return code(init(...))
end,
function(msg)
if type(msg) == "table" and msg.reason == "terminated" then
return msg.code or 0
end
return {msg, debug.traceback()}
end, ...)
}
if not result[1] and type(result[2]) == "table" then
-- run exception handler
xpcall(function()
local stack = result[2][2]:gsub("^([^\n]*\n)[^\n]*\n[^\n]*\n","%1")
io.stderr:write(string.format("%s:\n%s", result[2][1] or "", stack))
end,
function(msg)
io.stderr:write("process library exception handler crashed: ", tostring(msg))
end)
result[2] = 128
end
-- onError opens a file, you can't open a file without a process, we close the process last
process.internal.close(thread, result)
return select(2, table.unpack(result))
end, true)
local new_proc =
{
path = path,
command = name or tostring(path),
env = env,
data =
{
handles = {},
io = {},
},
parent = p,
instances = setmetatable({}, {__mode="v"}),
}
for i,fd in pairs(p.data.io) do
new_proc.data.io[i] = io.dup(fd)
end
setmetatable(new_proc.data, {__index=p.data})
process.list[thread] = new_proc
return thread
end
function process.info(levelOrThread)
checkArg(1, levelOrThread, "thread", "number", "nil")
local p
if type(levelOrThread) == "thread" then
p = process.findProcess(levelOrThread)
else
local level = levelOrThread or 1
p = process.findProcess()
while level > 1 and p do
p = p.parent
level = level - 1
end
end
if p then
return {path=p.path, env=p.env, command=p.command, data=p.data}
end
end
--table of undocumented api subject to change and intended for internal use
process.internal = {}
--this is a future stub for a more complete method to kill a process
function process.internal.close(thread, result)
checkArg(1,thread,"thread")
local pdata = process.info(thread).data
pdata.result = result
while pdata.handles[1] do
local h = table.remove(pdata.handles)
if h.close then
pcall(h.close, h)
end
end
process.list[thread] = nil
end
function process.internal.continue(co, ...)
local result = {}
-- Emulate CC behavior by making yields a filtered event.pull()
local args = table.pack(...)
while coroutine.status(co) ~= "dead" do
result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
if coroutine.status(co) ~= "dead" then
args = table.pack(coroutine.yield(table.unpack(result, 2, result.n)))
elseif not result[1] then
io.stderr:write(result[2])
end
end
return table.unpack(result, 2, result.n)
end
function process.removeHandle(handle, proc)
local handles = (proc or process.info()).data.handles
for pos, h in ipairs(handles) do
if h == handle then
return table.remove(handles, pos)
end
end
end
function process.addHandle(handle, proc)
local _close = handle.close
local handles = (proc or process.info()).data.handles
table.insert(handles, handle)
function handle:close(...)
if _close then
self.close = _close
_close = nil
process.removeHandle(self, proc)
return self:close(...)
end
end
return handle
end
function process.running(level) -- kept for backwards compat, prefer process.info
local info = process.info(level)
if info then
return info.path, info.env, info.command
end
end
return process