local process={} process.list=setmetatable({},{__mode="k"}) local owner=setmetatable({},{__mode="k"}) function process.findProcess(co) co=co or coroutine.running() local main=owner[co] if main then local p=process.list[main] if p then return p end end for main,p in pairs(process.list)do if main==co then owner[co]=main return p end for _,instance in pairs(p.instances)do if instance==co then owner[co]=main 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,why=fs.open(program) if not f then return assert(loadfile(program,"bt",env))(...) end local chunks={} repeat local data=f:read(math.huge) chunks[#chunks+1]=data until not data f:close() local source=table.concat(chunks) local shebang=source:match("^#!([^\n]+)") if shebang then path=shebang:gsub("%s","") return code(program,...) end return assert(load(source,"="..program,"bt",env))(...) end else code=path end local thread=nil thread=coroutine.create(function(...) 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 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 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 process.internal={} 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={} 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) local info=process.info(level) if info then return info.path,info.env,info.command end end return process