lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


You can use C stackless coroutinues:

Here is simple example: ( https://cloud.mail.ru/public/Name/jLstqLvyk )

[[example.c]]

#include "lauxlib.h"
#include <stdio.h>

#ifdef __GNUC__
#define EXPORT
#else
#define EXPORT __declspec(dllexport)
#endif

//------------------------------------------------------------------------------

#define DECL_LUA_KFN(Name,CtxType) \
    static int lua_##Name##_kfn(lua_State* L, int status, lua_KContext kctx) { \
        CtxType *ctx=(CtxType*)kctx; \
        if (Name##_loop(ctx,L)) { \
        lua_settop(L,0); return lua_yieldk(L, 0, kctx, lua_##Name##_kfn); \
        } \
        Name##_done(ctx,L); lua_settop(L,0); return 0; \
    } \
    static int lua_##Name##_start(lua_State* L) { \
        CtxType *ctx=(CtxType*)lua_touserdata(L,lua_upvalueindex(1)); \
    return lua_##Name##_kfn(L,1,(lua_KContext)ctx); \
    } \
    static int lua_##Name##_gc(lua_State* L) { \
        CtxType *ctx=(CtxType*)lua_touserdata(L,1); \
        Name##_done(ctx,L); return 0; \
    } \
    static int lua_##Name(lua_State* L) { \
        lua_State* co = lua_newthread(L); \
        CtxType *ctx=(CtxType*)lua_newuserdata(co,sizeof(CtxType)); \
        Name##_init(ctx,L); \
        lua_newtable(co); \
        lua_pushstring(co, "__gc"); \
        lua_pushcfunction(co, lua_##Name##_gc); \
        lua_settable(co,-3); \
        lua_setmetatable(co, -2); \
        lua_pushcclosure(co,lua_##Name##_start,1); \
        return 1; \
    }

//------------------------------------------------------------------------------

typedef struct Seq { int line; } Seq;

#define SEQ_RESET(seq) { Seq *_seq=(seq); _seq->line=0; }
#define SEQ_BEGIN(seq) { Seq *_seq=(seq); _seq_switch: switch(_seq->line) { \
    default: _seq->line=-2; case -2: return 0; _seq->line=0; case 0: {} {
#define SEQ_NEXT  { _seq->line=__LINE__; goto _seq_next; case __LINE__: {} }
#define SEQ_STOP  { goto _seq_done; }
#define SEQ_RETRY { goto _seq_next; }
#define SEQ_END   } _seq_done: _seq->line=-1; case -1: return 0;\
    } _seq_next: return 1; }

//------------------------------------------------------------------------------

typedef struct MyfnState {
    int alive; Seq seq[1];
    int i,n;
} MyfnState;

static void mykfn_init(MyfnState *self,lua_State* L) {
    self->alive=1;
    self->n=lua_tointeger(L,1);
    SEQ_RESET(self->seq)
    printf("fn5_init n=%d\n",self->n);
}
static void mykfn_done(MyfnState *self,lua_State* L) {
    if (self->alive) {
    self->alive=0;
    printf("fn5_done n=%d %s ", self->n,
        self->seq->line<0 ? "finished":"not finished");
    } else {
        //printf("fn5_gc after done\n");
    }
}
static int mykfn_loop(MyfnState *self,lua_State* L) {
    SEQ_BEGIN(self->seq)
    printf("first step ");
    SEQ_NEXT
    for(self->i=0;self->i<self->n;self->i++) {
        printf("step i=%d ",self->i);
        SEQ_NEXT
    }
    printf("last step ");
    SEQ_END
}
DECL_LUA_KFN(mykfn,MyfnState)

//------------------------------------------------------------------------------

int EXPORT luaopen_example(lua_State* L) {
    lua_register(L, "mykfn",  lua_mykfn);
    return 0;
}

[[ test-kfn.lua ]]

require "example"

do
    local c1=mykfn(5)
    local c2=mykfn(9)
    for i=1,7 do
    io.write(i.."\t")
    coroutine.resume(c1)
    coroutine.resume(c2)
    print(coroutine.status(c1),coroutine.status(c2))
    end
end
print"--gc--"
collectgarbage "collect"
print""
print"--done--"

[[ build.sh ]]

gcc -shared -o example.so example.c

[[ output ]]

fn5_init n=5
fn5_init n=9
1    first step first step suspended    suspended
2    step i=0 step i=0 suspended    suspended
3    step i=1 step i=1 suspended    suspended
4    step i=2 step i=2 suspended    suspended
5    step i=3 step i=3 suspended    suspended
6    step i=4 step i=4 suspended    suspended
7    last step fn5_done n=5 finished step i=5 dead    suspended
--gc--
fn5_done n=9 not finished
--done--



вт, 28 мар. 2023 г. в 09:41, Marc Balmer <marc@msys.ch>:



I don’t know if it was ever discussed, that when you call C functions in Lua coroutines, the whole Lua system is blocked, i.e., no coroutine can advance.
Currently there is no way to yield from C. This behavior is problematic for time critical applications, e.g., as in our application, the execution environment for machine sequences.
 
We implemented a way to yield from C, i.e., only the coroutine executing the long running C code is blocked.
As a solution, we create a coroutine in the interpreter (C code) for each coroutine in the Lua code. We applied the solution to the blocking C functions in “io” and “os” (liolib.c and loslib.c).
Of course, we are aware, that this cannot be a general solution, as existing code would not expect yields from these libraries. Maybe a separate library with functions to yield from C would be a good approach.
The yield from C feature makes most sense for custom, application specific C libraries.
I think that the possibility to yield from C would be a useful feature for the interpreter, because not the whole Lua system needs to block when a long running C function is executed.
Please let me know what you think about this feature.

There are, fwiw, lua_yieldk() and lua_yield() in the C API.

- mb