--:Minify:-- local kernel = ... local tasks = {} local sys = {} local nextpid = 2 kernel.exitMain=false function sys.spawn(func, name, envars, args, tgid) local id = nextpid nextpid = nextpid + 1 tasks[tostring(id)] = { coro=coroutine.create(function() local ok, err = xpcall(func, debug.traceback, table.unpack(args or {})) if not ok then if kernel.config.logTaskExit then kernel.log("Task "..tostring(id).." exited with err: "..tostring(err), "ERROR", 2) end tasks[tostring(id)].status="Z" if type(err)=="number" then tasks[tostring(id)].exit=err end else if kernel.config.logTaskExit then if err then kernel.log("Task "..tostring(id).." exited with code: "..tostring(err), "INFO") else kernel.log("Task "..tostring(id).." exited without code", "INFO") end end tasks[tostring(id)].status="Z" if type(err)=="number" then tasks[tostring(id)].exit=err end end end), name=name or ("task"..tostring(id)), envars=envars or kernel.currentTask.envars, args=args or {}, status="R", pid=id, tgid=tgid or kernel.currentTask.tgid, username=kernel.username, uid=kernel.uid, fd={}, exit="", sleep=0, ivs=0, vs=0, children={}, parent=kernel.currentTask, siblings=kernel.currentTask.children, syscallReturn={}, cwd=kernel.currentTask.cwd, term=kernel.currentTask.term, timeSlice=0, lastTime=0, totalTime=0, numRuns=0, privacy=0, debugger=false, eventq=kernel.currentTask.eventq } table.insert(kernel.currentTask.children, tasks[tostring(id)]) return id end function sys.sleep(s) kernel.currentTask.status="S" kernel.currentTask.sleep=kernel.computer:time()+s*1000 coroutine.yield() end function sys.getTask(pid) if tasks[tostring(pid)] then local task = tasks[tostring(pid)] local children = {} local siblings = {} for i,v in ipairs(task.children) do children[i]=v.pid end for i,v in ipairs(task.siblings) do siblings[i]=v.pid end return { name=task.name, status=task.status, pid=task.pid, tgid=task.tgid, username=task.username, uid=task.uid, exit=task.exit, sleep=task.sleep, ivs=task.ivs, vs=task.vs, children=children, siblings=siblings, parent=task.parent.pid, cwd=task.cwd, term=task.term } end end function sys.collect(pid) local children={} for i,v in ipairs(kernel.currentTask.children) do children[i]=v.pid end if not tasks[tostring(pid)] then return false, "Task does not exist" elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) then return false, "You do not own this task" elseif tasks[tostring(pid)].status~="Z" then return false, "Task must exit to collect status" else tasks[tostring(pid)].reapTime=0 return true, tasks[tostring(pid)].exit end end function sys.kill(pid) local children={} for i,v in ipairs(kernel.currentTask.children) do children[i]=v.pid end if not tasks[tostring(pid)] then return false, "Task does not exist" elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) and kernel.uid~=0 then return false, "You do not own this task" elseif tasks[tostring(pid)].status=="Z" then return false, "Task is already dead" else tasks[tostring(pid)].status="Z" return true end end function sys.stop(pid) local children={} for i,v in ipairs(kernel.currentTask.children) do children[i]=v.pid end if not tasks[tostring(pid)] then return false, "Task does not exist" elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) and kernel.uid~=0 then return false, "You do not own this task" elseif tasks[tostring(pid)].status~="R" then return false, "Cannot stop non running task" else tasks[tostring(pid)].status="T" return true end end function sys.continue(pid) local children={} for i,v in ipairs(kernel.currentTask.children) do children[i]=v.pid end if not tasks[tostring(pid)] then return false, "Task does not exist" elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) and kernel.uid~=0 then return false, "You do not own this task" elseif tasks[tostring(pid)].status~="T" then return false, "Task is not stopped" else tasks[tostring(pid)].status="R" return true end end function sys.getpid() return kernel.currentTask.pid end function sys.getppid() return kernel.currentTask.parent.pid end function sys.getTasks() local ret={} for i,v in pairs(tasks) do ret[i]=v.pid end return ret end function sys.getEnviron(key) return kernel.currentTask.envars[key] end function sys.setEnviron(key, value) kernel.currentTask.envars[key]=value end function sys.exit(code) if kernel.config.logTaskExit then if code then kernel.log("Task "..tostring(kernel.currentTask.pid).." exited with code: "..tostring(code), "INFO") else kernel.log("Task "..tostring(kernel.currentTask.pid).." exited without code", "INFO") end end tasks[tostring(kernel.currentTask.pid)].status="Z" if type(code)=="number" then tasks[tostring(kernel.currentTask.pid)].exit=code end end function sys.setuid(uid) if kernel.uid~=0 then error("EACCES") end kernel.currentTask.uid=uid end function sys.getuid() return kernel.currentTask.uid end local sysc=kernel.syscalls sysc["spawn"]=sys.spawn sysc["sleep"]=sys.sleep sysc["getTask"]=sys.getTask sysc["collect"]=sys.collect sysc["kill"]=sys.kill sysc["stop"]=sys.stop sysc["continue"]=sys.continue sysc["getpid"]=sys.getpid sysc["getppid"]=sys.getppid sysc["getTasks"]=sys.getTasks sysc["setEnviron"]=sys.setEnviron sysc["getEnviron"]=sys.getEnviron sysc["exit"]=sys.exit sysc["setuid"]=sys.setuid sysc["getuid"]=sys.getuid kernel._G.sleep=function(...)coroutine.yield("syscall","sleep",...)end local function reapDeadTasks() for pid, task in pairs(tasks) do if task.status == "Z" and not task.reapTime then task.coro = nil task.ivs = nil task.vs = nil task.args = nil task.envars = nil task.cwd = nil task.term = nil task.numRuns = nil task.totalTime = nil task.lastTime = nil task.timeSlice = nil task.syscallReturn = nil task.sleep = nil for v,_ in ipairs(task.fd) do kernel.vfs.close(v) end task.fd = nil task.debugger=nil task.privacy=nil task.reapTime = kernel.computer:time() + 30000 elseif task.reapTime and kernel.computer:time() > task.reapTime and task.status=="Z" then for _,child in ipairs(task.children) do child.parent = tasks["1"] child.siblings = tasks["1"].children table.insert(tasks["1"].children, child) end for i, sibling in ipairs(task.siblings) do if sibling.pid == task.pid then table.remove(task.siblings, i) break end end tasks[pid] = nil end end end local alpha = 0.85 local C_target = 0.01 local Tmin = 0.0005 local Tmax = 0.5 local lambda_budget = 0.08 local lambda_clamp = 0.03 local lambda_var = 0.02 local k_min = 0.5 local k_max = 0.5 local B = 0.01 function kernel.main() while not kernel.exitMain do local N = 0 local Tmin_hit = 0 local Tmax_hit = 0 local totalTaskTime = 0 local taskTimes = {} for pid, task in pairs(tasks) do if task.status == "S" then if kernel.computer:time() >= task.sleep then task.status="R" task.sleep=0 end end if task.status == "R" then kernel.currentTask = task kernel.username = task.username kernel.uid = task.uid kernel.process = task.name N = N + 1 -- assign adaptive time slice task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha))) -- measure execution time local startTime = kernel.computer:time() local ret if kernel.config.preempt then ret = {coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn))} else ret = {coroutine.resume(task.coro, table.unpack(task.syscallReturn))} end local elapsed = kernel.computer:time() - startTime task.lastTime = elapsed task.totalTime = (task.totalTime or 0) + elapsed task.numRuns = (task.numRuns or 0) + 1 taskTimes[#taskTimes + 1] = elapsed totalTaskTime = totalTaskTime + elapsed if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end -- handle task results if ret[1] == "error" or ret[1]==false then kernel.log("processHandlerException: "..ret[2], "ERROR", 2) task.status = "Z" task.exit = "processHandlerException: "..ret[2] elseif ret[1] == "timeout" then task.ivs=task.ivs+1 task.syscallReturn = {} elseif ret[1] == "success" or ret[1] == true then task.vs=task.vs+1 if ret[2]=="syscall" then if kernel.syscalls[ret[3]] then if kernel.config.debugSyscalls then kernel.log("Task "..task.pid.." invoking syscall: "..ret[3], "DBUG", 5) for i=4,#ret do kernel.log(" inval["..tostring(i-3).."] = "..tostring(ret[i]), "DBUG", 5) end end local sysret = {xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))} if kernel.config.debugSyscalls then if not sysret[1] then kernel.log("Task "..task.pid.." syscall "..ret[3].." failed: "..tostring(sysret[2]), "ERROR", 2) else kernel.log("Task "..task.pid.." syscall "..ret[3].." completed returning "..tostring(#sysret-1).." values", "DBUG", 5) for i=2,#sysret do if type(sysret[i])=="table" then kernel.log(" retval["..tostring(i-1).."] = "..table.serialize(sysret[i]), "DBUG", 5) else kernel.log(" retval["..tostring(i-1).."] = "..tostring(sysret[i]), "DBUG", 5) end end end end if not sysret[1] then task.syscallReturn={false, sysret[2]} else task.syscallReturn={true, table.unpack(sysret,2)} end else task.syscallReturn={false, "Unknown syscall: "..tostring(ret[3])} end end end end end local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0 local T_prev_var = 0 for _, t in ipairs(taskTimes) do T_prev_var = T_prev_var + (t - T_prev_avg)^2 end if N > 0 then T_prev_var = T_prev_var / N end if N > 0 then local f_clamp = k_min*(Tmin_hit/N) - k_max*(Tmax_hit/N) local B_budget = (C_target * (N^(alpha-1))) / math.max(T_prev_avg, 1e-8) B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp - lambda_var * T_prev_var end -- clean up dead tasks reapDeadTasks() end end kernel.tasks=tasks kernel.hpv=sys