added support for synchronized methods
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user