mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 13:58:18 +00:00
feat(lua): add proper support of luv threads
This commit is contained in:
@@ -45,11 +45,22 @@ static int in_fast_callback = 0;
|
||||
// Initialized in nlua_init().
|
||||
static lua_State *global_lstate = NULL;
|
||||
|
||||
static uv_thread_t main_thread;
|
||||
|
||||
typedef struct {
|
||||
Error err;
|
||||
String lua_err_str;
|
||||
} LuaError;
|
||||
|
||||
typedef struct {
|
||||
LuaRef nil_ref;
|
||||
LuaRef empty_dict_ref;
|
||||
int ref_count;
|
||||
#if __has_feature(address_sanitizer)
|
||||
PMap(handle_T) ref_markers;
|
||||
#endif
|
||||
} nlua_ref_state_t;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "lua/executor.c.generated.h"
|
||||
# include "lua/vim_module.generated.h"
|
||||
@@ -65,11 +76,16 @@ typedef struct {
|
||||
}
|
||||
|
||||
#if __has_feature(address_sanitizer)
|
||||
static PMap(handle_T) nlua_ref_markers = MAP_INIT;
|
||||
static bool nlua_track_refs = false;
|
||||
# define NLUA_TRACK_REFS
|
||||
#endif
|
||||
|
||||
typedef enum luv_err_type {
|
||||
kCallback,
|
||||
kThread,
|
||||
kThreadCallback,
|
||||
} luv_err_t;
|
||||
|
||||
/// Convert lua error into a Vim error message
|
||||
///
|
||||
/// @param lstate Lua interpreter state.
|
||||
@@ -122,8 +138,21 @@ static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
static void nlua_luv_error_event(void **argv)
|
||||
{
|
||||
char *error = (char *)argv[0];
|
||||
luv_err_t type = (luv_err_t)(intptr_t)argv[1];
|
||||
msg_ext_set_kind("lua_error");
|
||||
semsg_multiline("Error executing luv callback:\n%s", error);
|
||||
switch (type) {
|
||||
case kCallback:
|
||||
semsg_multiline("Error executing luv callback:\n%s", error);
|
||||
break;
|
||||
case kThread:
|
||||
semsg_multiline("Error in luv thread:\n%s", error);
|
||||
break;
|
||||
case kThreadCallback:
|
||||
semsg_multiline("Error in luv callback, thread:\n%s", error);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
xfree(error);
|
||||
}
|
||||
|
||||
@@ -148,7 +177,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags
|
||||
const char *error = lua_tostring(lstate, -1);
|
||||
|
||||
multiqueue_put(main_loop.events, nlua_luv_error_event,
|
||||
1, xstrdup(error));
|
||||
2, xstrdup(error), (intptr_t)kCallback);
|
||||
lua_pop(lstate, 1); // error message
|
||||
retval = -status;
|
||||
} else { // LUA_OK
|
||||
@@ -162,6 +191,106 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int nlua_luv_thread_cb_cfpcall(lua_State *lstate, int nargs, int nresult,
|
||||
int flags)
|
||||
{
|
||||
return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, true);
|
||||
}
|
||||
|
||||
static int nlua_luv_thread_cfpcall(lua_State *lstate, int nargs, int nresult,
|
||||
int flags)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, false);
|
||||
}
|
||||
|
||||
static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func,
|
||||
void *ud, int flags)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
lua_pushcfunction(lstate, func);
|
||||
lua_pushlightuserdata(lstate, ud);
|
||||
int retval = nlua_luv_thread_cfpcall(lstate, 1, 0, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nresult,
|
||||
int flags, bool is_callback)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
int retval;
|
||||
|
||||
int top = lua_gettop(lstate);
|
||||
int status = lua_pcall(lstate, nargs, nresult, 0);
|
||||
if (status) {
|
||||
if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) {
|
||||
// Terminate this thread, as the main thread may be able to continue
|
||||
// execution.
|
||||
mch_errmsg(e_outofmem);
|
||||
mch_errmsg("\n");
|
||||
lua_close(lstate);
|
||||
#ifdef WIN32
|
||||
ExitThread(0);
|
||||
#else
|
||||
pthread_exit(0);
|
||||
#endif
|
||||
}
|
||||
const char *error = lua_tostring(lstate, -1);
|
||||
|
||||
loop_schedule_deferred(&main_loop,
|
||||
event_create(nlua_luv_error_event, 2,
|
||||
xstrdup(error),
|
||||
is_callback
|
||||
? (intptr_t)kThreadCallback
|
||||
: (intptr_t)kThread));
|
||||
lua_pop(lstate, 1); // error message
|
||||
retval = -status;
|
||||
} else { // LUA_OK
|
||||
if (nresult == LUA_MULTRET) {
|
||||
nresult = lua_gettop(lstate) - top + nargs + 1;
|
||||
}
|
||||
retval = nresult;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
|
||||
{
|
||||
if (lua_gettop(lstate) != 3) {
|
||||
return luaL_error(lstate, "Expected 3 arguments");
|
||||
}
|
||||
|
||||
luaL_checktype(lstate, -1, LUA_TTABLE);
|
||||
lua_getfield(lstate, -1, "is_lua");
|
||||
if (!lua_isboolean(lstate, -1)) {
|
||||
return luaL_error(lstate, "is_lua is not a boolean");
|
||||
}
|
||||
bool is_lua = lua_toboolean(lstate, -1);
|
||||
lua_pop(lstate, 2);
|
||||
|
||||
luaL_checktype(lstate, -1, LUA_TBOOLEAN);
|
||||
bool all = lua_toboolean(lstate, -1);
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
Error err = ERROR_INIT;
|
||||
const Array pat = nlua_pop_Array(lstate, &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
luaL_where(lstate, 1);
|
||||
lua_pushstring(lstate, err.msg);
|
||||
api_clear_error(&err);
|
||||
lua_concat(lstate, 2);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
|
||||
ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all);
|
||||
nlua_push_Array(lstate, ret, true);
|
||||
api_free_array(ret);
|
||||
api_free_array(pat);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void nlua_schedule_event(void **argv)
|
||||
{
|
||||
LuaRef cb = (LuaRef)(ptrdiff_t)argv[0];
|
||||
@@ -302,6 +431,157 @@ static int nlua_wait(lua_State *lstate)
|
||||
return 2;
|
||||
}
|
||||
|
||||
static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
nlua_ref_state_t *ref_state = lua_newuserdata(lstate, sizeof(*ref_state));
|
||||
memset(ref_state, 0, sizeof(*ref_state));
|
||||
ref_state->nil_ref = LUA_NOREF;
|
||||
ref_state->empty_dict_ref = LUA_NOREF;
|
||||
return ref_state;
|
||||
}
|
||||
|
||||
static nlua_ref_state_t *nlua_get_ref_state(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
lua_getfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state");
|
||||
nlua_ref_state_t *ref_state = lua_touserdata(lstate, -1);
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
return ref_state;
|
||||
}
|
||||
|
||||
LuaRef nlua_get_nil_ref(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
return ref_state->nil_ref;
|
||||
}
|
||||
|
||||
LuaRef nlua_get_empty_dict_ref(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
return ref_state->empty_dict_ref;
|
||||
}
|
||||
|
||||
static int nlua_get_ref_count(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
return ref_state->ref_count;
|
||||
}
|
||||
|
||||
int nlua_get_global_ref_count(void)
|
||||
{
|
||||
lua_State *const lstate = global_lstate;
|
||||
return nlua_get_ref_count(lstate);
|
||||
}
|
||||
|
||||
static void nlua_common_vim_init(lua_State *lstate, bool is_thread)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_new_ref_state(lstate, is_thread);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state");
|
||||
|
||||
// vim.is_thread
|
||||
lua_pushboolean(lstate, is_thread);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
|
||||
lua_pushcfunction(lstate, &nlua_is_thread);
|
||||
lua_setfield(lstate, -2, "is_thread");
|
||||
|
||||
// vim.NIL
|
||||
lua_newuserdata(lstate, 0);
|
||||
lua_createtable(lstate, 0, 0);
|
||||
lua_pushcfunction(lstate, &nlua_nil_tostring);
|
||||
lua_setfield(lstate, -2, "__tostring");
|
||||
lua_setmetatable(lstate, -2);
|
||||
ref_state->nil_ref = nlua_ref(lstate, -1);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
|
||||
lua_setfield(lstate, -2, "NIL");
|
||||
|
||||
// vim._empty_dict_mt
|
||||
lua_createtable(lstate, 0, 0);
|
||||
lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
|
||||
lua_setfield(lstate, -2, "__tostring");
|
||||
ref_state->empty_dict_ref = nlua_ref(lstate, -1);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
|
||||
lua_setfield(lstate, -2, "_empty_dict_mt");
|
||||
|
||||
// vim.loop
|
||||
if (is_thread) {
|
||||
luv_set_callback(lstate, nlua_luv_thread_cb_cfpcall);
|
||||
luv_set_thread(lstate, nlua_luv_thread_cfpcall);
|
||||
luv_set_cthread(lstate, nlua_luv_thread_cfcpcall);
|
||||
} else {
|
||||
luv_set_loop(lstate, &main_loop.uv);
|
||||
luv_set_callback(lstate, nlua_luv_cfpcall);
|
||||
}
|
||||
luaopen_luv(lstate);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, -3, "loop");
|
||||
|
||||
// package.loaded.luv = vim.loop
|
||||
// otherwise luv will be reinitialized when require'luv'
|
||||
lua_getglobal(lstate, "package");
|
||||
lua_getfield(lstate, -1, "loaded");
|
||||
lua_pushvalue(lstate, -3);
|
||||
lua_setfield(lstate, -2, "luv");
|
||||
lua_pop(lstate, 3);
|
||||
}
|
||||
|
||||
static void nlua_common_package_init(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
{
|
||||
const char *code = (char *)&shared_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua")
|
||||
|| nlua_pcall(lstate, 0, 0)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const char *code = (char *)&lua_load_package_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(lua_load_package_module) - 1, "@vim/_load_package.lua")
|
||||
|| lua_pcall(lstate, 0, 0, 0)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
lua_getglobal(lstate, "package"); // [package]
|
||||
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
|
||||
|
||||
const char *code = (char *)&inspect_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua")
|
||||
|| nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// [package, loaded, inspect]
|
||||
lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded]
|
||||
}
|
||||
|
||||
{
|
||||
const char *code = (char *)&lua_F_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua")
|
||||
|| nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n"));
|
||||
return;
|
||||
}
|
||||
// [package, loaded, module]
|
||||
lua_setfield(lstate, -2, "vim.F"); // [package, loaded]
|
||||
|
||||
lua_pop(lstate, 2); // []
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize lua interpreter state
|
||||
///
|
||||
/// Called by lua interpreter itself to initialize state.
|
||||
@@ -362,80 +642,22 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
lua_pushcfunction(lstate, &nlua_wait);
|
||||
lua_setfield(lstate, -2, "wait");
|
||||
|
||||
// vim.NIL
|
||||
lua_newuserdata(lstate, 0);
|
||||
lua_createtable(lstate, 0, 0);
|
||||
lua_pushcfunction(lstate, &nlua_nil_tostring);
|
||||
lua_setfield(lstate, -2, "__tostring");
|
||||
lua_setmetatable(lstate, -2);
|
||||
nlua_nil_ref = nlua_ref(lstate, -1);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
|
||||
lua_setfield(lstate, -2, "NIL");
|
||||
|
||||
// vim._empty_dict_mt
|
||||
lua_createtable(lstate, 0, 0);
|
||||
lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
|
||||
lua_setfield(lstate, -2, "__tostring");
|
||||
nlua_empty_dict_ref = nlua_ref(lstate, -1);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
|
||||
lua_setfield(lstate, -2, "_empty_dict_mt");
|
||||
nlua_common_vim_init(lstate, false);
|
||||
|
||||
// internal vim._treesitter... API
|
||||
nlua_add_treesitter(lstate);
|
||||
|
||||
// vim.loop
|
||||
luv_set_loop(lstate, &main_loop.uv);
|
||||
luv_set_callback(lstate, nlua_luv_cfpcall);
|
||||
luaopen_luv(lstate);
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_setfield(lstate, -3, "loop");
|
||||
|
||||
// package.loaded.luv = vim.loop
|
||||
// otherwise luv will be reinitialized when require'luv'
|
||||
lua_getglobal(lstate, "package");
|
||||
lua_getfield(lstate, -1, "loaded");
|
||||
lua_pushvalue(lstate, -3);
|
||||
lua_setfield(lstate, -2, "luv");
|
||||
lua_pop(lstate, 3);
|
||||
|
||||
nlua_state_add_stdlib(lstate);
|
||||
nlua_state_add_stdlib(lstate, false);
|
||||
|
||||
lua_setglobal(lstate, "vim");
|
||||
|
||||
{
|
||||
const char *code = (char *)&shared_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua")
|
||||
|| nlua_pcall(lstate, 0, 0)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
nlua_common_package_init(lstate);
|
||||
|
||||
{
|
||||
lua_getglobal(lstate, "package"); // [package]
|
||||
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
|
||||
|
||||
const char *code = (char *)&inspect_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua")
|
||||
|| nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n"));
|
||||
return 1;
|
||||
}
|
||||
// [package, loaded, inspect]
|
||||
lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded]
|
||||
|
||||
code = (char *)&lua_F_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua")
|
||||
|| nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n"));
|
||||
return 1;
|
||||
}
|
||||
// [package, loaded, module]
|
||||
lua_setfield(lstate, -2, "vim.F"); // [package, loaded]
|
||||
|
||||
code = (char *)&lua_filetype_module[0];
|
||||
char *code = (char *)&lua_filetype_module[0];
|
||||
if (luaL_loadbuffer(lstate, code, sizeof(lua_filetype_module) - 1, "@vim/filetype.lua")
|
||||
|| nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("E5106: Error while creating vim.filetype module: %.*s"));
|
||||
@@ -495,9 +717,60 @@ void nlua_init(void)
|
||||
luaL_openlibs(lstate);
|
||||
nlua_state_init(lstate);
|
||||
|
||||
luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem);
|
||||
|
||||
global_lstate = lstate;
|
||||
|
||||
main_thread = uv_thread_self();
|
||||
}
|
||||
|
||||
static lua_State *nlua_thread_acquire_vm(void)
|
||||
{
|
||||
// If it is called from the main thread, it will attempt to rebuild the cache.
|
||||
const uv_thread_t self = uv_thread_self();
|
||||
if (uv_thread_equal(&main_thread, &self)) {
|
||||
runtime_search_path_validate();
|
||||
}
|
||||
|
||||
lua_State *lstate = luaL_newstate();
|
||||
|
||||
// Add in the lua standard libraries
|
||||
luaL_openlibs(lstate);
|
||||
|
||||
// print
|
||||
lua_pushcfunction(lstate, &nlua_print);
|
||||
lua_setglobal(lstate, "print");
|
||||
|
||||
lua_pushinteger(lstate, 0);
|
||||
lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.refcount");
|
||||
|
||||
// vim
|
||||
lua_newtable(lstate);
|
||||
|
||||
nlua_common_vim_init(lstate, true);
|
||||
|
||||
nlua_state_add_stdlib(lstate, true);
|
||||
|
||||
lua_setglobal(lstate, "vim");
|
||||
|
||||
nlua_common_package_init(lstate);
|
||||
|
||||
lua_getglobal(lstate, "vim");
|
||||
lua_getglobal(lstate, "package");
|
||||
lua_getfield(lstate, -1, "loaded");
|
||||
lua_getfield(lstate, -1, "vim.inspect");
|
||||
lua_setfield(lstate, -4, "inspect");
|
||||
lua_pop(lstate, 3);
|
||||
|
||||
lua_getglobal(lstate, "vim");
|
||||
lua_createtable(lstate, 0, 0);
|
||||
lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime);
|
||||
lua_setfield(lstate, -2, "nvim__get_runtime");
|
||||
lua_setfield(lstate, -2, "api");
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
return lstate;
|
||||
}
|
||||
|
||||
void nlua_free_all_mem(void)
|
||||
{
|
||||
@@ -505,26 +778,30 @@ void nlua_free_all_mem(void)
|
||||
return;
|
||||
}
|
||||
lua_State *lstate = global_lstate;
|
||||
nlua_common_free_all_mem(lstate);
|
||||
}
|
||||
|
||||
nlua_unref(lstate, nlua_nil_ref);
|
||||
nlua_unref(lstate, nlua_empty_dict_ref);
|
||||
static void nlua_common_free_all_mem(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
nlua_unref(lstate, nlua_get_nil_ref(lstate));
|
||||
nlua_unref(lstate, nlua_get_empty_dict_ref(lstate));
|
||||
|
||||
#ifdef NLUA_TRACK_REFS
|
||||
if (nlua_refcount) {
|
||||
fprintf(stderr, "%d lua references were leaked!", nlua_refcount);
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
if (ref_state->ref_count) {
|
||||
fprintf(stderr, "%d lua references were leaked!", ref_state->ref_count);
|
||||
}
|
||||
|
||||
if (nlua_track_refs) {
|
||||
// in case there are leaked luarefs, leak the associated memory
|
||||
// to get LeakSanitizer stacktraces on exit
|
||||
pmap_destroy(handle_T)(&nlua_ref_markers);
|
||||
pmap_destroy(handle_T)(&ref_state->ref_markers);
|
||||
}
|
||||
#endif
|
||||
|
||||
nlua_refcount = 0;
|
||||
lua_close(lstate);
|
||||
}
|
||||
|
||||
static void nlua_print_event(void **argv)
|
||||
{
|
||||
char *str = argv[0];
|
||||
@@ -602,9 +879,18 @@ static int nlua_print(lua_State *const lstate)
|
||||
#undef PRINT_ERROR
|
||||
ga_append(&msg_ga, NUL);
|
||||
|
||||
if (in_fast_callback) {
|
||||
lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
|
||||
bool is_thread = lua_toboolean(lstate, -1);
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
if (is_thread) {
|
||||
loop_schedule_deferred(&main_loop,
|
||||
event_create(nlua_print_event, 2,
|
||||
msg_ga.ga_data,
|
||||
(intptr_t)msg_ga.ga_len));
|
||||
} else if (in_fast_callback) {
|
||||
multiqueue_put(main_loop.events, nlua_print_event,
|
||||
2, msg_ga.ga_data, msg_ga.ga_len);
|
||||
2, msg_ga.ga_data, (intptr_t)msg_ga.ga_len);
|
||||
} else {
|
||||
nlua_print_event((void *[]){ msg_ga.ga_data,
|
||||
(void *)(intptr_t)msg_ga.ga_len });
|
||||
@@ -613,10 +899,12 @@ static int nlua_print(lua_State *const lstate)
|
||||
|
||||
nlua_print_error:
|
||||
ga_clear(&msg_ga);
|
||||
char *buff = xmalloc(IOSIZE);
|
||||
const char *fmt = _("E5114: Error while converting print argument #%i: %.*s");
|
||||
size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx,
|
||||
size_t len = (size_t)vim_snprintf(buff, IOSIZE, fmt, curargidx,
|
||||
(int)errmsg_len, errmsg);
|
||||
lua_pushlstring(lstate, (char *)IObuff, len);
|
||||
lua_pushlstring(lstate, buff, len);
|
||||
xfree(buff);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
|
||||
@@ -806,16 +1094,19 @@ static int nlua_getenv(lua_State *lstate)
|
||||
|
||||
|
||||
/// add the value to the registry
|
||||
/// The current implementation does not support calls from threads.
|
||||
LuaRef nlua_ref(lua_State *lstate, int index)
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
|
||||
lua_pushvalue(lstate, index);
|
||||
LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX);
|
||||
if (ref > 0) {
|
||||
nlua_refcount++;
|
||||
ref_state->ref_count++;
|
||||
#ifdef NLUA_TRACK_REFS
|
||||
if (nlua_track_refs) {
|
||||
// dummy allocation to make LeakSanitizer track our luarefs
|
||||
pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3));
|
||||
pmap_put(handle_T)(&ref_state->ref_markers, ref, xmalloc(3));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -825,12 +1116,14 @@ LuaRef nlua_ref(lua_State *lstate, int index)
|
||||
/// remove the value from the registry
|
||||
void nlua_unref(lua_State *lstate, LuaRef ref)
|
||||
{
|
||||
nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
|
||||
|
||||
if (ref > 0) {
|
||||
nlua_refcount--;
|
||||
ref_state->ref_count--;
|
||||
#ifdef NLUA_TRACK_REFS
|
||||
// NB: don't remove entry from map to track double-unref
|
||||
if (nlua_track_refs) {
|
||||
xfree(pmap_get(handle_T)(&nlua_ref_markers, ref));
|
||||
xfree(pmap_get(handle_T)(&ref_state->ref_markers, ref));
|
||||
}
|
||||
#endif
|
||||
luaL_unref(lstate, LUA_REGISTRYINDEX, ref);
|
||||
@@ -1391,6 +1684,13 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nlua_is_thread(lua_State *lstate)
|
||||
{
|
||||
lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Required functions for lua c functions as VimL callbacks
|
||||
|
||||
int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state)
|
||||
|
Reference in New Issue
Block a user