Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion bridge/meta/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ local timer = {}
local mq = {}

---Get current time in milliseconds.
---@return integer time
function core.time() end

---Cause normal program termination.
Expand Down Expand Up @@ -47,10 +48,21 @@ function mq:send(...) end
---
---When the message queue is empty, the current coroutine
---waits here until a message is received.
---@return ...
---@return any ...
---@nodiscard
function mq:recv() end

---Receive message until the deadline.
---
---When the message queue is empty, the current coroutine
---waits here until a message is received or the deadline expires.
---Returns ``true, ...`` on success, or ``false, "timeout"`` on timeout.
---@param deadline integer Absolute deadline in milliseconds.
---@return boolean success
---@return any ...
---@nodiscard
function mq:recvUntil(deadline) end

---Create a message queue.
---@param size integer Queue size.
---@return MessageQueue
Expand Down
4 changes: 4 additions & 0 deletions bridge/meta/netif.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ function M.find(name) end
---@return netif
function M.wait(event, netif) end

---Get interface name.
---@return string
function M.getName(netif) end

---Whether the interface is up.
---@return boolean
function M.isUp(netif) end
Expand Down
4 changes: 2 additions & 2 deletions bridge/meta/socket.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ function socket:reuseaddr() end
---Bind a socket to a network interface.
---
---Call this before `bind`, `connect` or `listen` when you need traffic to stay on a specific interface.
---@param netif netif|string Network interface object or interface name.
function socket:bindif(netif) end
---@param ifname string Network interface name.
function socket:bindif(ifname) end

---Bind a socket to a local IP address and port.
---@param addr string Local address to use.
Expand Down
262 changes: 222 additions & 40 deletions bridge/src/lcorelib.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ typedef struct {
size_t size;
} lcore_mq;

typedef struct {
lua_State *co;
HAPPlatformTimerRef timer;
bool with_status;
} lcore_mq_wait_ctx;

static int lcore_time(lua_State *L) {
lua_pushnumber(L, HAPPlatformClockGetCurrent());
return 1;
Expand Down Expand Up @@ -272,10 +278,189 @@ static size_t lcore_mq_size(lcore_mq *obj) {
return obj->first > obj->last ? obj->size - obj->first + obj->last : obj->last - obj->first;
}

static void lcore_mq_resume(lua_State *L, lua_State *co, int nargs) {
int status, nres;
lua_xmove(L, co, nargs);
status = lc_resume(co, L, nargs, &nres);
if (luai_unlikely(status != LUA_OK && status != LUA_YIELD)) {
HAPLogError(&lcore_log, "%s: %s", __func__, lua_tostring(L, -1));
}
lua_pop(L, nres);
}

static int lcore_mq_recv_ready(lua_State *L, int mq_idx, lcore_mq *obj, bool with_status) {
mq_idx = lua_absindex(L, mq_idx);
HAPAssert(lua_getuservalue(L, mq_idx) == LUA_TTABLE);
int store_idx = lua_gettop(L);
lua_geti(L, store_idx, obj->first);
lua_pushnil(L);
lua_seti(L, store_idx, obj->first);
obj->first++;
if (obj->first > obj->size + 1) {
obj->first = 1;
}
int nargs = luaL_len(L, store_idx + 1);
if (with_status) {
lua_pushboolean(L, true);
}
for (int i = 1; i <= nargs; i++) {
lua_geti(L, store_idx + 1, i);
}
return with_status ? nargs + 1 : nargs;
}

static bool lcore_mq_wait_remove_at(lua_State *L, int wait_idx, int pos) {
wait_idx = lua_absindex(L, wait_idx);
int wait_count = luaL_len(L, wait_idx);
if (pos != wait_count) {
lua_geti(L, wait_idx, wait_count);
lua_seti(L, wait_idx, pos);
}
lua_pushnil(L);
lua_seti(L, wait_idx, wait_count);
return wait_count == 1;
}

static void lcore_mq_wait_remove(lua_State *L, lcore_mq_wait_ctx *ctx) {
if (lua_rawgetp(L, LUA_REGISTRYINDEX, ctx) != LUA_TUSERDATA) {
lua_pop(L, 1);
return;
}

int ctx_idx = lua_gettop(L);
HAPAssert(lua_getiuservalue(L, ctx_idx, 1) == LUA_TUSERDATA);
int mq_idx = lua_gettop(L);
HAPAssert(lua_getuservalue(L, mq_idx) == LUA_TTABLE);
int store_idx = lua_gettop(L);
if (lua_getfield(L, store_idx, "wait") == LUA_TTABLE) {
int wait_idx = lua_gettop(L);
int wait_count = luaL_len(L, wait_idx);
for (int i = 1; i <= wait_count; i++) {
if (lua_geti(L, wait_idx, i) == LUA_TUSERDATA && lua_touserdata(L, -1) == ctx) {
lua_pop(L, 1);
if (lcore_mq_wait_remove_at(L, wait_idx, i)) {
lua_pushnil(L);
lua_setfield(L, store_idx, "wait");
}
break;
}
lua_pop(L, 1);
}
lua_pop(L, 1);
} else {
lua_pop(L, 1);
}
lua_pop(L, 3);
}

static void lcore_mq_waitctx_release(lua_State *L, int idx, bool cancel_timer) {
idx = lua_absindex(L, idx);
lcore_mq_wait_ctx *ctx = lua_touserdata(L, idx);

if (cancel_timer && ctx->timer) {
HAPPlatformTimerDeregister(ctx->timer);
}
ctx->timer = 0;

lua_pushnil(L);
lua_rawsetp(L, LUA_REGISTRYINDEX, ctx);
lua_pushnil(L);
lua_setiuservalue(L, idx, 1);
ctx->co = NULL;
}

static int lcore_mq_wait_timeout_resume(lua_State *L) {
lcore_mq_wait_ctx *ctx = lua_touserdata(L, 1);
lua_pop(L, 1);

if (lua_rawgetp(L, LUA_REGISTRYINDEX, ctx) != LUA_TUSERDATA) {
lua_pop(L, 1);
return 0;
}

lua_State *co = ctx->co;
lcore_mq_wait_remove(L, ctx);
lcore_mq_waitctx_release(L, -1, false);
lua_pop(L, 1);

if (!co) {
return 0;
}

lua_pushboolean(L, false);
lua_pushliteral(L, "timeout");
lcore_mq_resume(L, co, 2);
return 0;
}

static void lcore_mq_wait_timeout_cb(HAPPlatformTimerRef timer, void *context) {
lcore_mq_wait_ctx *ctx = context;
if (!ctx->co) {
return;
}
lua_State *L = lc_getmainthread(ctx->co);

ctx->timer = 0;

HAPAssert(lua_gettop(L) == 0);

lc_pushtraceback(L);
lua_pushcfunction(L, lcore_mq_wait_timeout_resume);
lua_pushlightuserdata(L, ctx);
int status = lua_pcall(L, 1, 0, 1);
if (luai_unlikely(status != LUA_OK)) {
HAPLogError(&lcore_log, "%s: %s", __func__, lua_tostring(L, -1));
}

lua_settop(L, 0);
lc_collectgarbage(L);
}

static int lcore_mq_wait(lua_State *L, int mq_idx, bool with_status, HAPTime deadline) {
mq_idx = lua_absindex(L, mq_idx);
HAPAssert(lua_getuservalue(L, mq_idx) == LUA_TTABLE);
int store_idx = lua_gettop(L);
int type = lua_getfield(L, store_idx, "wait");
if (type == LUA_TNIL) {
lua_pop(L, 1);
lua_createtable(L, 1, 0);
lua_pushvalue(L, -1);
lua_setfield(L, store_idx, "wait");
} else {
HAPAssert(type == LUA_TTABLE);
}

int wait_idx = lua_gettop(L);
lcore_mq_wait_ctx *ctx = lua_newuserdatauv(L, sizeof(*ctx), 1);
ctx->co = L;
ctx->timer = 0;
ctx->with_status = with_status;
lua_pushvalue(L, mq_idx);
lua_setiuservalue(L, -2, 1);

int wait_pos = luaL_len(L, wait_idx) + 1;
lua_pushvalue(L, -1);
lua_seti(L, wait_idx, wait_pos);
if (deadline) {
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, ctx);
if (luai_unlikely(HAPPlatformTimerRegister(&ctx->timer,
deadline, lcore_mq_wait_timeout_cb, ctx) != kHAPError_None)) {
if (lcore_mq_wait_remove_at(L, wait_idx, wait_pos)) {
lua_pushnil(L);
lua_setfield(L, store_idx, "wait");
}
lcore_mq_waitctx_release(L, -1, false);
luaL_error(L, "failed to create a timer");
}
}
lua_pop(L, 3);
return lua_yield(L, 0);
}

static int lcore_mq_send(lua_State *L) {
lcore_mq *obj = luaL_checkudata(L, 1, LUA_MQ_OBJ_NAME);
int narg = lua_gettop(L) - 1;
int status, nres;

lua_getuservalue(L, 1);

Expand All @@ -284,22 +469,25 @@ static int lcore_mq_send(lua_State *L) {
lua_setfield(L, -3, "wait"); // que.wait = nil
int waiting = luaL_len(L, -1);
for (int i = 1; i <= waiting; i++) {
HAPAssert(lua_geti(L, -1, i) == LUA_TTHREAD);
lua_State *co = lua_tothread(L, -1);
HAPAssert(lua_geti(L, -1, i) == LUA_TUSERDATA);
lcore_mq_wait_ctx *ctx = lua_touserdata(L, -1);
lua_State *co = ctx->co;
bool with_status = ctx->with_status;
lcore_mq_waitctx_release(L, -1, true);
lua_pop(L, 1);
int max = 1 + narg;
if (luai_unlikely(!lua_checkstack(L, narg))) {
int nargs = narg + with_status;
if (luai_unlikely(!lua_checkstack(L, nargs))) {
luaL_error(L, "stack overflow");
}
for (int i = 2; i <= max; i++) {
lua_pushvalue(L, i);
if (with_status) {
lua_pushboolean(L, true);
}
lua_xmove(L, co, narg);
status = lc_resume(co, L, narg, &nres);
if (luai_unlikely(status != LUA_OK && status != LUA_YIELD)) {
HAPLogError(&lcore_log, "%s: %s", __func__, lua_tostring(L, -1));
for (int j = 2; j <= 1 + narg; j++) {
lua_pushvalue(L, j);
}
if (co) {
lcore_mq_resume(L, co, nargs);
}
lua_pop(L, nres);
}
} else {
if (lcore_mq_size(obj) == obj->size) {
Expand All @@ -326,37 +514,30 @@ static int lcore_mq_recv(lua_State *L) {
if (lua_gettop(L) != 1) {
luaL_error(L, "invalid arguements");
}
lua_getuservalue(L, 1);
if (obj->last == obj->first) {
int type = lua_getfield(L, 2, "wait");
if (type == LUA_TNIL) {
lua_pop(L, 1);
lua_createtable(L, 1, 0);
lua_pushthread(L);
lua_seti(L, 3, 1);
lua_setfield(L, 2, "wait");
} else {
HAPAssert(type == LUA_TTABLE);
lua_pushthread(L);
lua_seti(L, 3, luaL_len(L, 3) + 1);
lua_pop(L, 1);
}
lua_pop(L, 1);
return lua_yield(L, 0);
return lcore_mq_wait(L, 1, false, 0);
} else {
lua_geti(L, 2, obj->first);
lua_pushnil(L);
lua_seti(L, 2, obj->first);
obj->first++;
if (obj->first > obj->size + 1) {
obj->first = 1;
}
int nargs = luaL_len(L, 3);
for (int i = 1; i <= nargs; i++) {
lua_geti(L, 3, i);
}
return nargs;
return lcore_mq_recv_ready(L, 1, obj, false);
}
}

static int lcore_mq_recv_until(lua_State *L) {
lcore_mq *obj = luaL_checkudata(L, 1, LUA_MQ_OBJ_NAME);
lua_Integer deadline = luaL_checkinteger(L, 2);
luaL_argcheck(L, deadline >= 0, 2, "deadline out of range");
if (lua_gettop(L) != 2) {
luaL_error(L, "invalid arguements");
}
if (obj->last != obj->first) {
return lcore_mq_recv_ready(L, 1, obj, true);
}

if ((HAPTime)deadline <= HAPPlatformClockGetCurrent()) {
lua_pushboolean(L, false);
lua_pushliteral(L, "timeout");
return 2;
}
return lcore_mq_wait(L, 1, true, (HAPTime)deadline);
}

static int lcore_mq_tostring(lua_State *L) {
Expand All @@ -380,6 +561,7 @@ static const luaL_Reg lcore_mq_metameth[] = {
static const luaL_Reg lcore_mq_meth[] = {
{"send", lcore_mq_send},
{"recv", lcore_mq_recv},
{"recvUntil", lcore_mq_recv_until},
{NULL, NULL},
};

Expand Down
14 changes: 14 additions & 0 deletions bridge/src/lnetiflib.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ static int lnetif_wait(lua_State *L) {
return lua_yieldk(L, 0, (lua_KContext)ctx, finishwait);
}

static int lnetif_get_name(lua_State *L) {
luaL_argcheck(L, lua_islightuserdata(L, 1), 1, "not a lightuserdata");
pal_net_if *netif = lua_touserdata(L, 1);

char buf[PAL_NET_IF_NAME_MAX_LEN];
pal_err err = pal_net_if_get_name(netif, buf);
if (err != PAL_ERR_OK) {
luaL_error(L, "failed to get name: %s", pal_err_string(err));
}
lua_pushstring(L, buf);
return 1;
}

static int lnetif_is_up(lua_State *L) {
luaL_argcheck(L, lua_islightuserdata(L, 1), 1, "not a lightuserdata");
pal_net_if *netif = lua_touserdata(L, 1);
Expand Down Expand Up @@ -176,6 +189,7 @@ static const luaL_Reg lnetif_funcs[] = {
{"getInterfaces", lnetif_get_interfaces},
{"find", lnetif_find},
{"wait", lnetif_wait},
{"getName", lnetif_get_name},
{"isUp", lnetif_is_up},
{"getIpv4Addr", lnetif_get_ipv4_addr},
{"getIpv6Addrs", lnetif_get_ipv6_addrs},
Expand Down
Loading
Loading