-- :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 = {}, sleep = 0, ivs = 0, vs = 0, children = {}, parent = kernel.currentTask, siblings = kernel.currentTask.children, syscallReturn = {}, cwd = kernel.currentTask.cwd, timeSlice = 0, lastTime = 0, totalTime = 0, numRuns = 0 } 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[#ret + 1] = 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 kernel.currentTask = task kernel.username = task.username kernel.uid = task.uid kernel.process = task.name task.coro = nil task.ivs = nil task.vs = nil task.args = nil task.envars = nil task.cwd = 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 pcall(kernel.vfs.close,v) end task.fd = 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))) if task.sigq and #task.sigq~=0 and task.sigh then local coro = coroutine.create(task.sigh) if kernel.config.preempt then coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1)) else coroutine.resume(coro, table.remove(task.sigq, 1)) end end -- check for exit/stop if task.status=="R" then -- 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 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