added support for synchronized methods
This commit is contained in:
@@ -377,6 +377,28 @@ static int luaArch_component_doc(lua_State *L) {
|
||||
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) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
@@ -601,6 +623,8 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_setfield(L, component, "doc");
|
||||
lua_pushcfunction(L, luaArch_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_setfield(L, component, "slot");
|
||||
lua_pushcfunction(L, luaArch_component_methods);
|
||||
@@ -659,6 +683,8 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
||||
nn_free(ctx, arch, sizeof(*arch));
|
||||
return NN_OK;
|
||||
case NN_ARCH_TICK:;
|
||||
lua_pushboolean(arch->L, req->synchronized);
|
||||
lua_setglobal(arch->L, "_SYNCED");
|
||||
lua_settop(arch->L, 1);
|
||||
int ret = 0;
|
||||
#if LUA_VERSION_NUM >= 504L
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
-- Extremely bad.
|
||||
-- Do not use in a serious context, you will be hacked.
|
||||
-- There is no sandboxing here.
|
||||
-- NOTE: we implemented method synchronization in here btw
|
||||
|
||||
os.exit = nil
|
||||
os.execute = nil
|
||||
@@ -67,11 +68,9 @@ function component.list(ctype, exact)
|
||||
return desired
|
||||
end
|
||||
|
||||
local allInvoks = 0
|
||||
local syncedMethodStats
|
||||
|
||||
function component.invoke(address, method, ...)
|
||||
allInvoks = allInvoks + 1
|
||||
--print(allInvoks)
|
||||
local function realInvoke(address, method, ...)
|
||||
local t = {pcall(cinvoke, address, method, ...)}
|
||||
if computer.energy() <= 0 then sysyield() end -- out of power
|
||||
if computer.isOverused() then sysyield() end -- overused
|
||||
@@ -83,6 +82,19 @@ function component.invoke(address, method, ...)
|
||||
return nil, t[2]
|
||||
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 = {
|
||||
__call = function(self, ...)
|
||||
return component.invoke(self.address, self.name, ...)
|
||||
@@ -258,10 +270,17 @@ local f = assert(load(code, "=bios"))
|
||||
local thread = coroutine.create(f)
|
||||
|
||||
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)
|
||||
if not ok then
|
||||
print(debug.traceback(thread, err))
|
||||
end
|
||||
if coroutine.status(thread) == "dead" then break end
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
@@ -1608,6 +1608,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
|
||||
req.computer = computer;
|
||||
req.globalState = computer->arch.state;
|
||||
req.localState = computer->archState;
|
||||
req.synchronized = false;
|
||||
req.action = NN_ARCH_TICK;
|
||||
err = computer->arch.handler(&req);
|
||||
if(err) {
|
||||
@@ -1618,6 +1619,25 @@ nn_Exit nn_tick(nn_Computer *computer) {
|
||||
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) {
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
@@ -220,8 +220,7 @@ typedef struct nn_LockRequest {
|
||||
// Intended for a plain mutex.
|
||||
// This is used for synchronization. OpenComputers achieves synchronization
|
||||
// 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,
|
||||
// however methods which are meant to be slow may become indirect, as indirect methods consume the entire call budget.
|
||||
// In NeoNucleus, it is the same stuff, but direct ones may still be used across threads.
|
||||
// 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
|
||||
// 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,
|
||||
// destroy the local state
|
||||
NN_ARCH_DEINIT,
|
||||
// run 1 tick
|
||||
// run 1 tick or synchronized task
|
||||
NN_ARCH_TICK,
|
||||
// get the free memory
|
||||
NN_ARCH_FREEMEM,
|
||||
@@ -370,6 +369,8 @@ typedef struct nn_ArchitectureRequest {
|
||||
// the action requested
|
||||
nn_ArchitectureAction action;
|
||||
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
|
||||
size_t freeMemory;
|
||||
// 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_resetIdleTime(nn_Computer *computer);
|
||||
// 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.
|
||||
// It also sets the idle timestamp to the current uptime.
|
||||
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
|
||||
|
||||
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.
|
||||
// Will pop off trailing nulls.
|
||||
// Every remaining is what the component returned.
|
||||
// In the case of
|
||||
nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method);
|
||||
|
||||
// send a signal to a component.
|
||||
|
||||
Reference in New Issue
Block a user