diff options
author | cbdev <cb@cbcdn.com> | 2020-03-23 00:15:00 +0100 |
---|---|---|
committer | cbdev <cb@cbcdn.com> | 2020-03-23 00:15:00 +0100 |
commit | 37e712edf23a49be5387f945ab9ea57cc0b57f22 (patch) | |
tree | 5037b14bdbd08baa8f104f7e4946aa5643281a94 /backends/lua.c | |
parent | 666aec036f9bf0de82c435bd7eace271613cee1e (diff) | |
parent | aa02ccf3abf183207b24fcbb9460cbd904a698e2 (diff) | |
download | midimonster-37e712edf23a49be5387f945ab9ea57cc0b57f22.tar.gz midimonster-37e712edf23a49be5387f945ab9ea57cc0b57f22.tar.bz2 midimonster-37e712edf23a49be5387f945ab9ea57cc0b57f22.zip |
Merge current master
Diffstat (limited to 'backends/lua.c')
-rw-r--r-- | backends/lua.c | 285 |
1 files changed, 229 insertions, 56 deletions
diff --git a/backends/lua.c b/backends/lua.c index ee9e03f..7f80cc7 100644 --- a/backends/lua.c +++ b/backends/lua.c @@ -9,15 +9,19 @@ #endif #define LUA_REGISTRY_KEY "_midimonster_lua_instance" +#define LUA_REGISTRY_CURRENT_CHANNEL "_midimonster_lua_channel" +#define LUA_REGISTRY_CURRENT_THREAD "_midimonster_lua_thread" static size_t timers = 0; static lua_timer* timer = NULL; uint64_t timer_interval = 0; #ifdef MMBACKEND_LUA_TIMERFD static int timer_fd = -1; -#else -static uint64_t last_timestamp; #endif +static uint64_t last_timestamp = 0; + +static size_t threads = 0; +static lua_thread* thread = NULL; MM_PLUGIN_API int init(){ backend lua = { @@ -63,6 +67,7 @@ static uint32_t lua_interval(){ next_timer = timer[n].interval - timer[n].delta; } } + DBGPF("Next timer fires in %" PRIu32, next_timer); return next_timer; } return 1000; @@ -74,7 +79,7 @@ static int lua_update_timerfd(){ size_t n = 0; #ifdef MMBACKEND_LUA_TIMERFD struct itimerspec timer_config = { - 0 + {0} }; #endif @@ -85,6 +90,13 @@ static int lua_update_timerfd(){ } } + for(n = 0; n < threads; n++){ + if(thread[n].timeout && (!interval || thread[n].timeout < interval)){ + interval = thread[n].timeout; + } + } + DBGPF("Recalculating timers, minimum is %" PRIu64, interval); + //calculate gcd of all timers if any are active if(interval){ for(n = 0; n < timers; n++){ @@ -97,7 +109,8 @@ static int lua_update_timerfd(){ gcd = residual; } //since we round everything, 10 is the lowest interval we get - if(interval == 10){ + if(interval <= 10){ + interval = 10; break; } } @@ -110,22 +123,106 @@ static int lua_update_timerfd(){ } if(interval == timer_interval){ + DBGPF("Keeping interval at %" PRIu64, interval); return 0; } #ifdef MMBACKEND_LUA_TIMERFD - //configure the new interval + //configure the new interval, 0.0 disarms the timer + DBGPF("Reconfiguring timerfd to %" PRIu64 ".%" PRIu64, timer_config.it_interval.tv_sec, timer_config.it_interval.tv_nsec); timerfd_settime(timer_fd, 0, &timer_config, NULL); #endif timer_interval = interval; return 0; } +static void lua_thread_resume(size_t current_thread){ + //push coroutine reference + lua_pushstring(thread[current_thread].thread, LUA_REGISTRY_CURRENT_THREAD); + lua_pushnumber(thread[current_thread].thread, current_thread); + lua_settable(thread[current_thread].thread, LUA_REGISTRYINDEX); + + //call thread main + DBGPF("Resuming thread %" PRIsize_t " on %s", current_thread, thread[current_thread].instance->name); + if(lua_resume(thread[current_thread].thread, NULL, 0) != LUA_YIELD){ + DBGPF("Thread %" PRIsize_t " on %s terminated", current_thread, thread[current_thread].instance->name); + thread[current_thread].timeout = 0; + } + + //remove coroutine reference + lua_pushstring(thread[current_thread].thread, LUA_REGISTRY_CURRENT_THREAD); + lua_pushnil(thread[current_thread].thread); + lua_settable(thread[current_thread].thread, LUA_REGISTRYINDEX); +} + +static int lua_callback_thread(lua_State* interpreter){ + instance* inst = NULL; + size_t u = threads; + if(lua_gettop(interpreter) != 1){ + LOGPF("Thread function called with %d arguments, expected function", lua_gettop(interpreter)); + return 0; + } + + luaL_checktype(interpreter, 1, LUA_TFUNCTION); + + //get instance pointer from registry + lua_pushstring(interpreter, LUA_REGISTRY_KEY); + lua_gettable(interpreter, LUA_REGISTRYINDEX); + inst = (instance*) lua_touserdata(interpreter, -1); + + //make space for a new thread + thread = realloc(thread, (threads + 1) * sizeof(lua_thread)); + if(!thread){ + threads = 0; + LOG("Failed to allocate memory"); + return 0; + } + threads++; + + thread[u].thread = lua_newthread(interpreter); + thread[u].instance = inst; + thread[u].timeout = 0; + thread[u].reference = luaL_ref(interpreter, LUA_REGISTRYINDEX); + + DBGPF("Registered thread %" PRIsize_t " on %s", threads, inst->name); + + //push thread main + luaL_checktype(interpreter, 1, LUA_TFUNCTION); + lua_pushvalue(interpreter, 1); + lua_xmove(interpreter, thread[u].thread, 1); + + lua_thread_resume(u); + lua_update_timerfd(); + return 0; +} + +static int lua_callback_sleep(lua_State* interpreter){ + uint64_t timeout = 0; + size_t current_thread = threads; + if(lua_gettop(interpreter) != 1){ + LOGPF("Sleep function called with %d arguments, expected number", lua_gettop(interpreter)); + return 0; + } + + timeout = luaL_checkinteger(interpreter, 1); + + lua_pushstring(interpreter, LUA_REGISTRY_CURRENT_THREAD); + lua_gettable(interpreter, LUA_REGISTRYINDEX); + + current_thread = luaL_checkinteger(interpreter, -1); + + if(current_thread < threads){ + DBGPF("Yielding for %" PRIu64 "msec on thread %" PRIsize_t, timeout, current_thread); + thread[current_thread].timeout = timeout; + lua_yield(interpreter, 0); + } + return 0; +} + static int lua_callback_output(lua_State* interpreter){ size_t n = 0; channel_value val; const char* channel_name = NULL; - channel* channel = NULL; instance* inst = NULL; lua_instance_data* data = NULL; @@ -144,15 +241,21 @@ static int lua_callback_output(lua_State* interpreter){ channel_name = lua_tostring(interpreter, 1); val.normalised = clamp(luaL_checknumber(interpreter, 2), 1.0, 0.0); + //if not started yet, create any requested channels so scripts may set them at load time + if(!last_timestamp && channel_name){ + lua_channel(inst, (char*) channel_name, mmchannel_output); + } + //find correct channel & output value for(n = 0; n < data->channels; n++){ - if(!strcmp(channel_name, data->channel_name[n])){ - channel = mm_channel(inst, n, 0); - if(!channel){ - return 0; + if(!strcmp(channel_name, data->channel[n].name)){ + data->channel[n].out = val.normalised; + if(!last_timestamp){ + data->channel[n].mark = 1; + } + else{ + mm_channel_event(mm_channel(inst, n, 0), val); } - mm_channel_event(channel, val); - data->output[n] = val.normalised; return 0; } } @@ -189,6 +292,7 @@ static int lua_callback_interval(lua_State* interpreter){ if(lua_gettable(interpreter, LUA_REGISTRYINDEX) == LUA_TNUMBER){ //already interval'd reference = luaL_checkinteger(interpreter, 4); + DBGPF("Updating interval to %" PRIu64 " msec", interval); } else if(interval){ //get a reference to the function @@ -199,6 +303,8 @@ static int lua_callback_interval(lua_State* interpreter){ lua_pushvalue(interpreter, 1); lua_pushinteger(interpreter, reference); lua_settable(interpreter, LUA_REGISTRYINDEX); + + DBGPF("Registered interval with %" PRIu64 " msec", interval); } //find matching timer @@ -255,8 +361,8 @@ static int lua_callback_value(lua_State* interpreter, uint8_t input){ //find correct channel & return value for(n = 0; n < data->channels; n++){ - if(!strcmp(channel_name, data->channel_name[n])){ - lua_pushnumber(data->interpreter, (input) ? data->input[n] : data->output[n]); + if(!strcmp(channel_name, data->channel[n].name)){ + lua_pushnumber(interpreter, (input) ? data->channel[n].in : data->channel[n].out); return 1; } } @@ -273,6 +379,17 @@ static int lua_callback_output_value(lua_State* interpreter){ return lua_callback_value(interpreter, 0); } +static int lua_callback_input_channel(lua_State* interpreter){ + lua_pushstring(interpreter, LUA_REGISTRY_CURRENT_CHANNEL); + lua_gettable(interpreter, LUA_REGISTRYINDEX); + return 1; +} + +static int lua_callback_timestamp(lua_State* interpreter){ + lua_pushnumber(interpreter, mm_timestamp()); + return 1; +} + static int lua_configure(char* option, char* value){ LOG("No backend configuration possible"); return 1; @@ -289,21 +406,21 @@ static int lua_configure_instance(instance* inst, char* option, char* value){ } return 0; } + else if(!strcmp(option, "default-handler")){ + free(data->default_handler); + data->default_handler = strdup(value); + return 0; + } LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name); return 1; } -static instance* lua_instance(){ - instance* inst = mm_instance(); - if(!inst){ - return NULL; - } - +static int lua_instance(instance* inst){ lua_instance_data* data = calloc(1, sizeof(lua_instance_data)); if(!data){ LOG("Failed to allocate memory"); - return NULL; + return 1; } //load the interpreter @@ -311,7 +428,7 @@ static instance* lua_instance(){ if(!data->interpreter){ LOG("Failed to initialize interpreter"); free(data); - return NULL; + return 1; } luaL_openlibs(data->interpreter); @@ -320,6 +437,10 @@ static instance* lua_instance(){ lua_register(data->interpreter, "interval", lua_callback_interval); lua_register(data->interpreter, "input_value", lua_callback_input_value); lua_register(data->interpreter, "output_value", lua_callback_output_value); + lua_register(data->interpreter, "input_channel", lua_callback_input_channel); + lua_register(data->interpreter, "timestamp", lua_callback_timestamp); + lua_register(data->interpreter, "thread", lua_callback_thread); + lua_register(data->interpreter, "sleep", lua_callback_sleep); //store instance pointer to the lua state lua_pushstring(data->interpreter, LUA_REGISTRY_KEY); @@ -327,7 +448,7 @@ static instance* lua_instance(){ lua_settable(data->interpreter, LUA_REGISTRYINDEX); inst->impl = data; - return inst; + return 0; } static channel* lua_channel(instance* inst, char* spec, uint8_t flags){ @@ -336,26 +457,23 @@ static channel* lua_channel(instance* inst, char* spec, uint8_t flags){ //find matching channel for(u = 0; u < data->channels; u++){ - if(!strcmp(spec, data->channel_name[u])){ + if(!strcmp(spec, data->channel[u].name)){ break; } } //allocate new channel if(u == data->channels){ - data->channel_name = realloc(data->channel_name, (u + 1) * sizeof(char*)); - data->reference = realloc(data->reference, (u + 1) * sizeof(int)); - data->input = realloc(data->input, (u + 1) * sizeof(double)); - data->output = realloc(data->output, (u + 1) * sizeof(double)); - if(!data->channel_name || !data->reference || !data->input || !data->output){ + data->channel = realloc(data->channel, (data->channels + 1) * sizeof(lua_channel_data)); + if(!data->channel){ LOG("Failed to allocate memory"); + data->channels = 0; return NULL; } - data->reference[u] = LUA_NOREF; - data->input[u] = data->output[u] = 0.0; - data->channel_name[u] = strdup(spec); - if(!data->channel_name[u]){ + data->channel[u].in = data->channel[u].out = 0.0; + data->channel[u].name = strdup(spec); + if(!data->channel[u].name){ LOG("Failed to allocate memory"); return NULL; } @@ -366,22 +484,33 @@ static channel* lua_channel(instance* inst, char* spec, uint8_t flags){ } static int lua_set(instance* inst, size_t num, channel** c, channel_value* v){ - size_t n = 0; + size_t n = 0, ident; lua_instance_data* data = (lua_instance_data*) inst->impl; //handle all incoming events for(n = 0; n < num; n++){ - data->input[c[n]->ident] = v[n].normalised; + ident = c[n]->ident; + data->channel[ident].in = v[n].normalised; //call lua channel handlers if present - if(data->reference[c[n]->ident] != LUA_NOREF){ - lua_rawgeti(data->interpreter, LUA_REGISTRYINDEX, data->reference[c[n]->ident]); + if(data->channel[ident].reference != LUA_NOREF){ + //push the channel name + lua_pushstring(data->interpreter, LUA_REGISTRY_CURRENT_CHANNEL); + lua_pushstring(data->interpreter, data->channel[ident].name); + lua_settable(data->interpreter, LUA_REGISTRYINDEX); + + lua_rawgeti(data->interpreter, LUA_REGISTRYINDEX, data->channel[ident].reference); lua_pushnumber(data->interpreter, v[n].normalised); if(lua_pcall(data->interpreter, 1, 0, 0) != LUA_OK){ - LOGPF("Failed to call handler for %s.%s: %s", inst->name, data->channel_name[c[n]->ident], lua_tostring(data->interpreter, -1)); + LOGPF("Failed to call handler for %s.%s: %s", inst->name, data->channel[ident].name, lua_tostring(data->interpreter, -1)); lua_pop(data->interpreter, 1); } } } + + //clear the channel name + lua_pushstring(data->interpreter, LUA_REGISTRY_CURRENT_CHANNEL); + lua_pushnil(data->interpreter); + lua_settable(data->interpreter, LUA_REGISTRYINDEX); return 0; } @@ -401,9 +530,6 @@ static int lua_handle(size_t num, managed_fd* fds){ return 1; } #else - if(!last_timestamp){ - last_timestamp = mm_timestamp(); - } delta = mm_timestamp() - last_timestamp; last_timestamp = mm_timestamp(); #endif @@ -422,30 +548,75 @@ static int lua_handle(size_t num, managed_fd* fds){ timer[n].delta %= timer[n].interval; lua_rawgeti(timer[n].interpreter, LUA_REGISTRYINDEX, timer[n].reference); lua_pcall(timer[n].interpreter, 0, 0, 0); + DBGPF("Calling interval timer function %" PRIsize_t, n); } } } + + //check for threads to wake up + for(n = 0; n < threads; n++){ + if(thread[n].timeout && delta >= thread[n].timeout){ + lua_thread_resume(n); + lua_update_timerfd(); + } + else if(thread[n].timeout){ + thread[n].timeout -= delta; + } + } return 0; } +static int lua_resolve_symbol(lua_State* interpreter, char* symbol){ + int reference = LUA_REFNIL; + + //exclude reserved names + if(!strcmp(symbol, "output") + || !strcmp(symbol, "thread") + || !strcmp(symbol, "sleep") + || !strcmp(symbol, "input_value") + || !strcmp(symbol, "output_value") + || !strcmp(symbol, "input_channel") + || !strcmp(symbol, "timestamp") + || !strcmp(symbol, "interval")){ + return LUA_NOREF; + } + + lua_getglobal(interpreter, symbol); + reference = luaL_ref(interpreter, LUA_REGISTRYINDEX); + if(reference == LUA_REFNIL){ + return LUA_NOREF; + } + return reference; +} + static int lua_start(size_t n, instance** inst){ size_t u, p; lua_instance_data* data = NULL; + int default_handler; + channel_value v; //resolve channels to their handler functions for(u = 0; u < n; u++){ data = (lua_instance_data*) inst[u]->impl; + default_handler = LUA_NOREF; + + //try to resolve default handler if given + if(data->default_handler){ + default_handler = lua_resolve_symbol(data->interpreter, data->default_handler); + if(default_handler == LUA_NOREF){ + LOGPF("Failed to resolve default handler %s on %s", data->default_handler, inst[u]->name); + } + } + for(p = 0; p < data->channels; p++){ - //exclude reserved names - if(strcmp(data->channel_name[p], "output") - && strcmp(data->channel_name[p], "input_value") - && strcmp(data->channel_name[p], "output_value") - && strcmp(data->channel_name[p], "interval")){ - lua_getglobal(data->interpreter, data->channel_name[p]); - data->reference[p] = luaL_ref(data->interpreter, LUA_REGISTRYINDEX); - if(data->reference[p] == LUA_REFNIL){ - data->reference[p] = LUA_NOREF; - } + data->channel[p].reference = default_handler; + if(!data->default_handler){ + data->channel[p].reference = lua_resolve_symbol(data->interpreter, data->channel[p].name); + } + //push initial values + if(data->channel[p].mark){ + v.normalised = data->channel[p].out; + mm_channel_event(mm_channel(inst[u], p, 0), v); } } } @@ -457,6 +628,7 @@ static int lua_start(size_t n, instance** inst){ return 1; } #endif + last_timestamp = mm_timestamp(); return 0; } @@ -470,12 +642,10 @@ static int lua_shutdown(size_t n, instance** inst){ lua_close(data->interpreter); //cleanup channel data for(p = 0; p < data->channels; p++){ - free(data->channel_name[p]); + free(data->channel[p].name); } - free(data->channel_name); - free(data->reference); - free(data->input); - free(data->output); + free(data->channel); + free(data->default_handler); free(inst[u]->impl); } @@ -483,6 +653,9 @@ static int lua_shutdown(size_t n, instance** inst){ free(timer); timer = NULL; timers = 0; + free(thread); + thread = NULL; + threads = 0; #ifdef MMBACKEND_LUA_TIMERFD close(timer_fd); timer_fd = -1; |