From fcabf5b85369b39d76b269787bc62a97f1b34a5d Mon Sep 17 00:00:00 2001 From: 20kdc Date: Fri, 30 Mar 2018 01:53:00 +0100 Subject: [PATCH] Somewhat smaller & less RAM-hungry kernel so the system is more usable. --- README.md | 26 ++++++++ code/init.lua | 182 +++++++++++++++++++++++++------------------------- 2 files changed, 116 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index b0396bf..cd8e208 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,32 @@ The first commit is after I got the installer working again after the new compre That's what the "SYSTEM HEROES" thing is about. +## Known Issues (That Aren't KittenOS NEO's Fault) + +Touch calibration is off because OC's setPrecise seems to offset coordinates down and right by a character. OCEmu's does not. I consider this an OC bug. This is known to occur on at least `OpenComputers-MC1.12.2-1.7.2.67.jar`. If you want to check my routines, see sys-everest, search for the lowest instance of `"touch"`. Or just use OCEmu, which doesn't change anything when precise is set, and thus can't be doing anything different than the setPrecise(false) behavior. + +Wide character support *may* encounter issues due to performance-saving tricks in some old OC versions. The 1.12.2 version being used at LimboCon doesn't have the issue, so it's been dealt with. Point is, not a KittenOS NEO bug if it happens. + +## Known Issues (That Are KittenOS NEO's Fault But Aren't Really Fixable) + +If you move a window over another window, that window has to rerender. The alternative is buffering the window. Since memory is a concern, that is not going to happen. Some windows are more expensive to render than others (`klogo` tries to use less RAM if the system is 192K, at the expense of disk access) - move the most expensive window out of the way, since once a window is top-most, moving it around is usually "free". + +If the system runs out of memory, the kernel could crash, or alternatively the system goes into a limbo state. You're more or less doomed. Given that almost everything in Lua causes a memory allocation, I'm not exactly sure how I'd be supposed to fix this properly. + +Any situation where the system fails to boot *may* be fixable with Safe Mode. +This includes if you copied a sufficiently large bit of text into the persistent clipboard, and now Icecap or Everest won't start. +The catch is, it wipes your settings. As the settings are always in RAM, and contain just about every *fixable* thing that can break your boot, + nuking them should bring you to defaults. + +It seems to take just under a second for a key event to get through, yet drags are fine. +I don't know why this is, but I suspect the answer involves the timing of a `computer.pullSignal()` with no timeout. +If you particularly find performance important, and don't mind the energy costs, you can modify the `init.lua` to make it so that the kernel wakes up every tick. + +And finally, just because a system can multitask somewhat on 192K doesn't mean it can do the impossible regarding memory usage. +Lesson learned: Cleaner design -> Higher memory usage. +So anyone who wants the design to be made even cleaner should probably reread this paragraph. +(In R0, editing the kernel causes 192K systems to fail to open filedialogs. I've fixed this in R1.) + ## Description At least in theory: "efficient. multi-tasking. clean. security-oriented". diff --git a/code/init.lua b/code/init.lua index b32975c..0ea16e5 100644 --- a/code/init.lua +++ b/code/init.lua @@ -106,12 +106,12 @@ function wrapMeta(t) local t2 = {} setmetatable(t2, { __index = function (a, k) return wrapMeta(t[k]) end, - __newindex = function (a, k, v) end, + __newindex = error, __pairs = function (a) return function (x, key) local k, v = next(t, k) if k then return k, wrapMeta(v) end - end, {}, nil + end, 9, nil end, __ipairs = function (a) return function (x, key) @@ -119,7 +119,7 @@ function wrapMeta(t) if t[key] then return key, wrapMeta(t[key]) end - end, {}, 0 + end, 9, 0 end, __metatable = uniqueNEOProtectionObject -- Don't protect this table - it'll make things worse @@ -138,36 +138,17 @@ function ensureType(a, t) end function ensurePathComponent(s) - if not s:match("^[a-zA-Z0-9_%-%+%,%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^]+") then error("chars disallowed") end + if not string.match(s, "^[a-zA-Z0-9_%-%+%,%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^]+") then error("chars disallowed") end if s == "." then error("single dot disallowed") end if s == ".." then error("double dot disallowed") end end function ensurePath(s, r) - -- Filter filename for anything "worrying". Note / is allowed, see further filters - if not s:match("^[a-zA-Z0-9_%-%+%,%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^%/]+") then error("chars disallowed") end + string.gsub(s, "[^/]+", ensurePathComponent) if s:sub(1, r:len()) ~= r then error("base disallowed") end if s:match("//") then error("// disallowed") end - if s:match("^%.%./") then error("../ disallowed") end - if s:match("/%.%./") then error("/../ disallowed") end - if s:match("/%.%.$") then error("/.. disallowed") end - if s:match("^%./") then error("./ disallowed") end - if s:match("/%./") then error("/./ disallowed") end - if s:match("/%.$") then error("/. disallowed") end end -wrapMath = wrapMeta(math) -wrapTable = wrapMeta(table) -wrapString = wrapMeta(string) -wrapUnicode = wrapMeta(unicode) -wrapCoroutine = wrapMeta(coroutine) -wrapOs = wrapMeta({ - totalMemory = computer.totalMemory, freeMemory = computer.freeMemory, - energy = computer.energy, maxEnergy = computer.maxEnergy, - clock = os.clock, date = os.date, difftime = os.difftime, - time = os.time, uptime = computer.uptime - }) - -- Use with extreme care. -- (A process killing itself will actually survive until the next yield... before any of the death events have run.) function termProc(pid, reason) @@ -218,7 +199,7 @@ function distEvent(pid, s, ...) if not v then return end - if not (v.access["s." .. s] or v.access["k.root"]) then + if not (s:sub(1, 2) == "k." or v.access["s." .. s] or v.access["k.root"]) then return end -- Schedule the timer to carry the event. @@ -243,57 +224,6 @@ function lister(pfx) end end -function baseProcEnv() - local pe = { - _VERSION = _VERSION, - math = wrapMath, - table = wrapTable, - string = wrapString, - unicode = wrapUnicode, - coroutine = wrapCoroutine, - os = wrapOs, - -- Note raw-methods are gone - these can interfere with the metatable safeties. - require = loadLibraryInner, - assert = assert, ipairs = ipairs, - load = load, next = next, - pairs = pairs, pcall = pcall, - xpcall = xpcall, select = select, - type = type, error = error, - tonumber = tonumber, tostring = tostring, - setmetatable = setmetatable, getmetatable = function (n) - local mt = getmetatable(n) - if mt == uniqueNEOProtectionObject then return "NEO-Protected Object" end - return mt - end, - rawset = function (t, i, v) - local mt = getmetatable(n) - if mt == uniqueNEOProtectionObject then error("NEO-Protected Object") end - return rawset(t, i, v) - end, rawget = rawget, rawlen = rawlen, rawequal = rawequal, - neo = { - emergency = emergencyFunction, - readBufSize = readBufSize, - wrapMeta = wrapMeta, - listProcs = function () - local n = {} - for k, v in pairs(processes) do - table.insert(n, {k, v.pkg, v.cpuUsage}) - end - return n - end, - listApps = lister("apps/"), - listLibs = lister("libs/"), - totalIdleTime = function () return idleTime end, - ensurePath = ensurePath, - ensurePathComponent = ensurePathComponent, - ensureType = ensureType - } - } - pe._G = pe - pe._ENV = pe - return pe -end - function loadLibraryInner(library) ensureType(library, "string") library = "libs/" .. library .. ".lua" @@ -313,6 +243,87 @@ function loadLibraryInner(library) return nil, r end +wrapMath = wrapMeta(math) +wrapTable = wrapMeta(table) +wrapString = wrapMeta(string) +wrapUnicode = wrapMeta(unicode) +wrapCoroutine = wrapMeta(coroutine) +wrapOs = wrapMeta({ + totalMemory = computer.totalMemory, freeMemory = computer.freeMemory, + energy = computer.energy, maxEnergy = computer.maxEnergy, + clock = os.clock, date = os.date, difftime = os.difftime, + time = os.time, uptime = computer.uptime + }) +wrapDebug = wrapMeta(debug) + +baseProcEnvCore = { + _VERSION = _VERSION, + math = wrapMath, + table = wrapTable, + string = wrapString, + unicode = wrapUnicode, + coroutine = wrapCoroutine, + os = wrapOs, + debug = wrapDebug, + require = loadLibraryInner, + assert = assert, ipairs = ipairs, + load = load, + next = function (t, k) + local mt = getmetatable(t) + if mt == uniqueNEOProtectionObject then error("NEO-Protected Object") end + return next(t, k) + end, + pairs = pairs, pcall = pcall, + xpcall = xpcall, select = select, + type = type, error = error, + tonumber = tonumber, tostring = tostring, + setmetatable = setmetatable, getmetatable = function (n) + local mt = getmetatable(n) + if mt == uniqueNEOProtectionObject then return "NEO-Protected Object" end + return mt + end, + rawset = function (t, i, v) + local mt = getmetatable(t) + if mt == uniqueNEOProtectionObject then error("NEO-Protected Object") end + return rawset(t, i, v) + end, rawget = rawget, rawlen = rawlen, rawequal = rawequal, +} +baseProcNeo = { + emergency = emergencyFunction, + readBufSize = readBufSize, + wrapMeta = wrapMeta, + listProcs = function () + local n = {} + for k, v in pairs(processes) do + table.insert(n, {k, v.pkg, v.cpuUsage}) + end + return n + end, + listApps = lister("apps/"), + listLibs = lister("libs/"), + totalIdleTime = function () return idleTime end, + ensurePath = ensurePath, + ensurePathComponent = ensurePathComponent, + ensureType = ensureType +} + +baseProcEnvMT = { + __index = baseProcEnvCore, + __metatable = uniqueNEOProtectionObject +} +baseProcNeoMT = { + __index = baseProcNeo, + __metatable = uniqueNEOProtectionObject +} + +function baseProcEnv() + local pe = setmetatable({}, baseProcEnvMT) + pe.neo = setmetatable({}, baseProcNeoMT) + pe._G = pe + pe._ENV = pe + return pe +end + -- These two are hooks for k.root level applications to change policy. -- Only a k.root application is allowed to do this for obvious reasons. function securityPolicy(pid, proc, perm, req) @@ -500,7 +511,6 @@ function start(pkg, ...) end local env = baseProcEnv() env.neo.pid = pid - env.neo.dead = false env.neo.executeAsync = startFromUser env.neo.execute = function (...) return osExecuteCore(function () end, ...) @@ -538,16 +548,7 @@ function start(pkg, ...) end proc.co = coroutine.create(function (...) local r = {xpcall(appfunc, debug.traceback, ...)} if not r[1] then error(table.unpack(r, 2)) end return table.unpack(r, 2) end) proc.pkg = pkg - proc.access = { - -- These permissions are the "critical set". - ["s.k.securityresponse"] = true, - ["s.k.timer"] = true, - ["s.k.procnew"] = true, - ["s.k.procdie"] = true, - -- Used when a registration is updated, in particular, as this signifies "readiness" - ["s.k.registration"] = true, - ["s.k.deregistration"] = true - } + proc.access = {} proc.denied = {} -- You are dead. Not big surprise. proc.deathCBs = {function () pcall(function () env.neo.dead = true end) end} @@ -571,15 +572,14 @@ while true do for i = 1, 16 do tmr = nil local now = computer.uptime() - local breaking = false -- Used when a process dies - in this case it's assumed OC just did something drastic - local didAnything = false + local didAnything = false -- for early exit local k = 1 while timers[k] do local v = timers[k] if v[1] <= now then table.remove(timers, k) if v[2](table.unpack(v, 3)) then - breaking = true + didAnything = false -- to break tmr = 0.05 break end @@ -593,8 +593,6 @@ while true do k = k + 1 end end - if breaking then break end - -- If the system didn't make any progress, then we're waiting for a signal (this includes timers) if not didAnything then break end end now = computer.uptime() -- the above probably took a while