added support for synchronized methods

This commit is contained in:
2026-04-27 20:07:57 +03:00
parent 7244174945
commit 72fd316310
4 changed files with 86 additions and 11 deletions

View File

@@ -377,6 +377,28 @@ static int luaArch_component_doc(lua_State *L) {
return 1; return 1;
} }
static int luaArch_component_getMethodFlags(lua_State *L) {
luaArch *arch = luaArch_from(L);
const char *address = luaL_checkstring(L, 1);
const char *method = luaL_checkstring(L, 2);
nn_Component *c = nn_getComponent(arch->computer, address);
if(c == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
nn_MethodFlags f = nn_getComponentMethodFlags(c, method);
lua_createtable(L, 0, 3);
lua_pushboolean(L, (f & NN_DIRECT) != 0);
lua_setfield(L, -2, "direct");
lua_pushboolean(L, (f & NN_GETTER) != 0);
lua_setfield(L, -2, "getter");
lua_pushboolean(L, (f & NN_SETTER) != 0);
lua_setfield(L, -2, "setter");
return 1;
}
static int luaArch_component_slot(lua_State *L) { static int luaArch_component_slot(lua_State *L) {
luaArch *arch = luaArch_from(L); luaArch *arch = luaArch_from(L);
const char *address = luaL_checkstring(L, 1); const char *address = luaL_checkstring(L, 1);
@@ -601,6 +623,8 @@ static void luaArch_loadEnv(lua_State *L) {
lua_setfield(L, component, "doc"); lua_setfield(L, component, "doc");
lua_pushcfunction(L, luaArch_component_type); lua_pushcfunction(L, luaArch_component_type);
lua_setfield(L, component, "type"); lua_setfield(L, component, "type");
lua_pushcfunction(L, luaArch_component_getMethodFlags);
lua_setfield(L, component, "getMethodFlags");
lua_pushcfunction(L, luaArch_component_slot); lua_pushcfunction(L, luaArch_component_slot);
lua_setfield(L, component, "slot"); lua_setfield(L, component, "slot");
lua_pushcfunction(L, luaArch_component_methods); lua_pushcfunction(L, luaArch_component_methods);
@@ -659,6 +683,8 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
nn_free(ctx, arch, sizeof(*arch)); nn_free(ctx, arch, sizeof(*arch));
return NN_OK; return NN_OK;
case NN_ARCH_TICK:; case NN_ARCH_TICK:;
lua_pushboolean(arch->L, req->synchronized);
lua_setglobal(arch->L, "_SYNCED");
lua_settop(arch->L, 1); lua_settop(arch->L, 1);
int ret = 0; int ret = 0;
#if LUA_VERSION_NUM >= 504L #if LUA_VERSION_NUM >= 504L

View File

@@ -2,6 +2,7 @@
-- Extremely bad. -- Extremely bad.
-- Do not use in a serious context, you will be hacked. -- Do not use in a serious context, you will be hacked.
-- There is no sandboxing here. -- There is no sandboxing here.
-- NOTE: we implemented method synchronization in here btw
os.exit = nil os.exit = nil
os.execute = nil os.execute = nil
@@ -67,11 +68,9 @@ function component.list(ctype, exact)
return desired return desired
end end
local allInvoks = 0 local syncedMethodStats
function component.invoke(address, method, ...) local function realInvoke(address, method, ...)
allInvoks = allInvoks + 1
--print(allInvoks)
local t = {pcall(cinvoke, address, method, ...)} local t = {pcall(cinvoke, address, method, ...)}
if computer.energy() <= 0 then sysyield() end -- out of power if computer.energy() <= 0 then sysyield() end -- out of power
if computer.isOverused() then sysyield() end -- overused if computer.isOverused() then sysyield() end -- overused
@@ -83,6 +82,19 @@ function component.invoke(address, method, ...)
return nil, t[2] return nil, t[2]
end end
function component.invoke(address, method, ...)
if component.getMethodFlags(address, method).direct then
return realInvoke(address, method, ...)
else
-- must sync
syncedMethodStats = {address, method, ...}
coroutine.yield()
local rets = syncedMethodStats
syncedMethodStats = nil
return table.unpack(rets)
end
end
local componentCallback = { local componentCallback = {
__call = function(self, ...) __call = function(self, ...)
return component.invoke(self.address, self.name, ...) return component.invoke(self.address, self.name, ...)
@@ -258,10 +270,17 @@ local f = assert(load(code, "=bios"))
local thread = coroutine.create(f) local thread = coroutine.create(f)
while true do while true do
if _SYNCED then
if syncedMethodStats then
--debug.print("calling synced method")
syncedMethodStats = {realInvoke(table.unpack(syncedMethodStats))}
end
else
local ok, err = resume(thread) local ok, err = resume(thread)
if not ok then if not ok then
print(debug.traceback(thread, err)) print(debug.traceback(thread, err))
end end
if coroutine.status(thread) == "dead" then break end if coroutine.status(thread) == "dead" then break end
end
coroutine.yield() coroutine.yield()
end end

View File

@@ -1608,6 +1608,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
req.computer = computer; req.computer = computer;
req.globalState = computer->arch.state; req.globalState = computer->arch.state;
req.localState = computer->archState; req.localState = computer->archState;
req.synchronized = false;
req.action = NN_ARCH_TICK; req.action = NN_ARCH_TICK;
err = computer->arch.handler(&req); err = computer->arch.handler(&req);
if(err) { if(err) {
@@ -1618,6 +1619,25 @@ nn_Exit nn_tick(nn_Computer *computer) {
return NN_OK; return NN_OK;
} }
nn_Exit nn_tickSynchronized(nn_Computer *computer) {
if(!nn_isComputerOn(computer)) return NN_OK;
// idling pootr
if(nn_isComputerIdle(computer)) return NN_OK;
nn_ArchitectureRequest req;
req.computer = computer;
req.globalState = computer->arch.state;
req.localState = computer->archState;
req.synchronized = true;
req.action = NN_ARCH_TICK;
nn_Exit err = computer->arch.handler(&req);
if(err) {
computer->state = NN_CRASHED;
nn_setErrorFromExit(computer, err);
return err;
}
return NN_OK;
}
static nn_Exit nn_defaultComponent(nn_ComponentRequest *request) { static nn_Exit nn_defaultComponent(nn_ComponentRequest *request) {
return NN_OK; return NN_OK;
} }

View File

@@ -220,8 +220,7 @@ typedef struct nn_LockRequest {
// Intended for a plain mutex. // Intended for a plain mutex.
// This is used for synchronization. OpenComputers achieves synchronization // This is used for synchronization. OpenComputers achieves synchronization
// between the worker threads by sending them as requests to a central thread (indirect methods). // between the worker threads by sending them as requests to a central thread (indirect methods).
// In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct, // In NeoNucleus, it is the same stuff, but direct ones may still be used across threads.
// however methods which are meant to be slow may become indirect, as indirect methods consume the entire call budget.
// Do note that locks are only used in "full" component implementations, such as the volatile storage devices. // Do note that locks are only used in "full" component implementations, such as the volatile storage devices.
// The interfaces do not do any automatic synchronization via locks, all synchronization is assumed // The interfaces do not do any automatic synchronization via locks, all synchronization is assumed
// to be handled in the implementer of the interface, because only you know how to best synchronize // to be handled in the implementer of the interface, because only you know how to best synchronize
@@ -347,7 +346,7 @@ typedef enum nn_ArchitectureAction {
NN_ARCH_INIT, NN_ARCH_INIT,
// destroy the local state // destroy the local state
NN_ARCH_DEINIT, NN_ARCH_DEINIT,
// run 1 tick // run 1 tick or synchronized task
NN_ARCH_TICK, NN_ARCH_TICK,
// get the free memory // get the free memory
NN_ARCH_FREEMEM, NN_ARCH_FREEMEM,
@@ -370,6 +369,8 @@ typedef struct nn_ArchitectureRequest {
// the action requested // the action requested
nn_ArchitectureAction action; nn_ArchitectureAction action;
union { union {
// in the case of NN_ARCH_TICK, where the tick is synchronized
bool synchronized;
// in the case of NN_ARCH_FREEMEM, the free memory // in the case of NN_ARCH_FREEMEM, the free memory
size_t freeMemory; size_t freeMemory;
// in the case of NN_ARCH_DESERIALIZE, NN_ARCH_SERIALIZE and NN_ARCH_DROPSERIALIZED, the buffer. // in the case of NN_ARCH_DESERIALIZE, NN_ARCH_SERIALIZE and NN_ARCH_DROPSERIALIZED, the buffer.
@@ -576,10 +577,18 @@ bool nn_isComputerIdle(nn_Computer *computer);
void nn_addIdleTime(nn_Computer *computer, double time); void nn_addIdleTime(nn_Computer *computer, double time);
void nn_resetIdleTime(nn_Computer *computer); void nn_resetIdleTime(nn_Computer *computer);
// runs a tick of the computer. Make sure to check the state as well! // runs a tick of the computer. Make sure to check the state as well!
// Does not do anything if we're currently waiting on a synced call
// This automatically resets the component budgets and call budget. // This automatically resets the component budgets and call budget.
// It also sets the idle timestamp to the current uptime. // It also sets the idle timestamp to the current uptime.
nn_Exit nn_tick(nn_Computer *computer); nn_Exit nn_tick(nn_Computer *computer);
// runs a synchronized tick of the computer. How this differs depends on architecture.
// Generally, this is meant to be in the same thread for all computers, and is if the external world is fundamentally not thread-safe,
// however components must interact with it.
// In this case, those component methods would be marked as NN_INDIRECT, or more accurately will not be marked as NN_DIRECT, and the architecture would queue them as synchronized tasks.
// Architectures should generally NOT ignore this if they can.
nn_Exit nn_tickSynchronized(nn_Computer *computer);
// raw component and methods // raw component and methods
typedef struct nn_Component nn_Component; typedef struct nn_Component nn_Component;
@@ -705,6 +714,7 @@ void nn_getComponents(nn_Computer *c, const char **components);
// Everything on-stack is taken as an argument. // Everything on-stack is taken as an argument.
// Will pop off trailing nulls. // Will pop off trailing nulls.
// Every remaining is what the component returned. // Every remaining is what the component returned.
// In the case of
nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method); nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method);
// send a signal to a component. // send a signal to a component.