Skip to content

Commit

Permalink
feat(lua): back to using official mlua
Browse files Browse the repository at this point in the history
since we can't have `send` and `module` we store `LuaFunctions` inside
the registry and pass around keys to them. ehh, basically what i was
doing with the callback channel but now twice i guess, idk i hope this
can get better eventually ™️
  • Loading branch information
alemidev committed Nov 16, 2024
1 parent 03a158e commit 456a78d
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 59 deletions.
17 changes: 9 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jni = { version = "0.21", features = ["invocation"], optional = true }
jni-toolbox = { version = "0.2", optional = true, features = ["uuid"] }

# glue (lua)
mlua-codemp-patch = { version = "0.10.0-beta.2", features = ["module", "send", "serialize"], optional = true }
mlua = { version = "0.10", features = ["module", "serialize", "error-send"], optional = true }

# glue (js)
napi = { version = "2.16", features = ["full"], optional = true }
Expand Down Expand Up @@ -74,10 +74,10 @@ test-e2e = []
java = ["dep:lazy_static", "dep:jni", "dep:tracing-subscriber", "dep:jni-toolbox"]
js = ["dep:napi-build", "dep:tracing-subscriber", "dep:napi", "dep:napi-derive"]
py = ["dep:pyo3", "dep:tracing-subscriber", "dep:pyo3-build-config"]
lua = ["serialize", "dep:mlua-codemp-patch", "dep:tracing-subscriber", "dep:lazy_static"]
lua = ["serialize", "dep:mlua", "dep:tracing-subscriber", "dep:lazy_static"]
# ffi variants
lua-jit = ["mlua-codemp-patch?/luajit"]
lua-54 = ["mlua-codemp-patch?/lua54"]
lua-jit = ["mlua?/luajit"]
lua-54 = ["mlua?/lua54"]
py-abi3 = ["pyo3?/abi3-py38"]


Expand Down
19 changes: 15 additions & 4 deletions src/ffi/lua/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

use super::ext::a_sync::a_sync;

Expand Down Expand Up @@ -31,11 +30,23 @@ impl LuaUserData for CodempBufferController {
|_, this, ()| a_sync! { this => this.content().await? },
);

methods.add_method("clear_callback", |_, this, ()| Ok(this.clear_callback()));
methods.add_method("callback", |_, this, (cb,): (LuaFunction,)| {
methods.add_method("clear_callback", move |lua, this, ()| {
this.clear_callback();
lua.unset_named_registry_value(&this.lua_callback_id())
});

methods.add_method("callback", move |lua, this, (cb,): (LuaFunction,)| {
let key = this.lua_callback_id();
lua.set_named_registry_value(&key, cb)?;
Ok(this.callback(move |controller: CodempBufferController| {
super::ext::callback().invoke(cb.clone(), controller)
super::ext::callback().invoke(key.clone(), controller, false)
}))
});
}
}

impl CodempBufferController {
fn lua_callback_id(&self) -> String {
format!("codemp-buffercontroller({}:{})-callback-registry", self.workspace_id(), self.path())
}
}
1 change: 0 additions & 1 deletion src/ffi/lua/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

use super::ext::a_sync::a_sync;

Expand Down
18 changes: 14 additions & 4 deletions src/ffi/lua/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

use super::ext::a_sync::a_sync;

Expand All @@ -22,11 +21,22 @@ impl LuaUserData for CodempCursorController {
methods.add_method("recv", |_, this, ()| a_sync! { this => this.recv().await? });
methods.add_method("poll", |_, this, ()| a_sync! { this => this.poll().await? });

methods.add_method("clear_callback", |_, this, ()| Ok(this.clear_callback()));
methods.add_method("callback", |_, this, (cb,): (LuaFunction,)| {
methods.add_method("clear_callback", |lua, this, ()| {
this.clear_callback();
lua.unset_named_registry_value(&this.lua_callback_id())
});
methods.add_method("callback", |lua, this, (cb,): (LuaFunction,)| {
let key = this.lua_callback_id();
lua.set_named_registry_value(&key, cb)?;
Ok(this.callback(move |controller: CodempCursorController| {
super::ext::callback().invoke(cb.clone(), controller)
super::ext::callback().invoke(key.clone(), controller, false)
}))
});
}
}

impl CodempCursorController {
fn lua_callback_id(&self) -> String {
format!("codemp-cursorcontroller({})-callback-registry", self.workspace_id())
}
}
19 changes: 13 additions & 6 deletions src/ffi/lua/ext/a_sync.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

pub(crate) fn tokio() -> &'static tokio::runtime::Runtime {
use std::sync::OnceLock;
Expand All @@ -21,7 +20,8 @@ macro_rules! a_sync {
Some(
crate::ffi::lua::ext::a_sync::tokio()
.spawn(async move {
Ok(crate::ffi::lua::ext::callback::CallbackArg::from($x))
let res = $x;
Ok(crate::ffi::lua::ext::callback::CallbackArg::from(res))
})
)
)
Expand All @@ -47,13 +47,20 @@ impl LuaUserData for Promise {
// TODO: await MUST NOT be used in callbacks!!
methods.add_method_mut("await", |_, this, ()| match this.0.take() {
None => Err(LuaError::runtime("Promise already awaited")),
Some(x) => tokio().block_on(x).map_err(LuaError::runtime)?,
Some(x) => Ok(
tokio()
.block_on(x)
.map_err(LuaError::runtime)?
.map_err(LuaError::runtime)?
),
});
methods.add_method_mut("cancel", |_, this, ()| match this.0.take() {
None => Err(LuaError::runtime("Promise already awaited")),
Some(x) => Ok(x.abort()),
});
methods.add_method_mut("and_then", |_, this, (cb,): (LuaFunction,)| {
methods.add_method_mut("and_then", |lua, this, (cb,): (LuaFunction,)| {
let key = uuid::Uuid::new_v4().to_string();
lua.set_named_registry_value(&key, cb)?;
match this.0.take() {
None => Err(LuaError::runtime("Promise already awaited")),
Some(x) => {
Expand All @@ -64,9 +71,9 @@ impl LuaUserData for Promise {
}
Ok(res) => match res {
Err(e) => super::callback().failure(e),
Ok(val) => super::callback().invoke(cb, val),
Ok(val) => super::callback().invoke(key, val, true),
},
}
};
});
Ok(())
}
Expand Down
33 changes: 24 additions & 9 deletions src/ffi/lua/ext/callback.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::ext::IgnorableError;
use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

pub(crate) fn callback() -> &'static CallbackChannel<LuaCallback> {
static CHANNEL: std::sync::OnceLock<CallbackChannel<LuaCallback>> = std::sync::OnceLock::new();
Expand All @@ -25,21 +24,19 @@ impl Default for CallbackChannel<LuaCallback> {
}

impl CallbackChannel<LuaCallback> {
pub(crate) fn invoke(&self, cb: LuaFunction, arg: impl Into<CallbackArg>) {
pub(crate) fn invoke(&self, key: String, arg: impl Into<CallbackArg>, cleanup: bool) {
self.tx
.send(LuaCallback::Invoke(cb, arg.into()))
.send(LuaCallback::Invoke(key, arg.into(), cleanup))
.unwrap_or_warn("error scheduling callback")
}

pub(crate) fn failure(&self, err: impl std::error::Error) {
self.tx
.send(LuaCallback::Fail(format!(
"promise failed with error: {err:?}"
)))
.send(LuaCallback::Fail(format!("callback returned error: {err:?}")))
.unwrap_or_warn("error scheduling callback failure")
}

pub(crate) fn recv(&self) -> Option<LuaCallback> {
pub(crate) fn recv(&self, lua: &Lua) -> Option<(LuaFunction, CallbackArg)> {
match self.rx.try_lock() {
Err(e) => {
tracing::debug!("backing off from callback mutex: {e}");
Expand All @@ -51,15 +48,33 @@ impl CallbackChannel<LuaCallback> {
None
}
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => None,
Ok(cb) => Some(cb),
Ok(LuaCallback::Fail(msg)) => {
tracing::error!("callback returned error: {msg}");
None
},
Ok(LuaCallback::Invoke(key, arg, cleanup)) => {
let cb = match lua.named_registry_value::<LuaFunction>(&key) {
Ok(x) => x,
Err(e) => {
tracing::error!("could not get callback to invoke: {e}");
return None;
},
};
if cleanup {
if let Err(e) = lua.unset_named_registry_value(&key) {
tracing::warn!("could not unset callback from registry: {e}");
}
}
Some((cb, arg))
},
},
}
}
}

pub(crate) enum LuaCallback {
Fail(String),
Invoke(LuaFunction, CallbackArg),
Invoke(String, CallbackArg, bool),
}

macro_rules! callback_args {
Expand Down
18 changes: 6 additions & 12 deletions src/ffi/lua/ext/log.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::{io::Write, sync::Mutex};

use mlua::prelude::*;
use mlua_codemp_patch as mlua;
use tokio::sync::mpsc;

#[derive(Debug, Clone)]
Expand All @@ -19,7 +18,7 @@ impl Write for LuaLoggerProducer {

// TODO can we make this less verbose?
pub(crate) fn setup_tracing(
_: &Lua,
lua: &Lua,
(printer, debug): (LuaValue, Option<bool>),
) -> LuaResult<bool> {
let level = if debug.unwrap_or_default() {
Expand All @@ -37,14 +36,6 @@ pub(crate) fn setup_tracing(
.with_source_location(false);

let success = match printer {
LuaValue::Boolean(_)
| LuaValue::LightUserData(_)
| LuaValue::Integer(_)
| LuaValue::Number(_)
| LuaValue::Table(_)
| LuaValue::Thread(_)
| LuaValue::UserData(_)
| LuaValue::Error(_) => return Err(LuaError::BindError), // TODO full BadArgument type??
LuaValue::Nil => tracing_subscriber::fmt()
.event_format(format)
.with_max_level(level)
Expand All @@ -63,6 +54,8 @@ pub(crate) fn setup_tracing(
.is_ok()
}
LuaValue::Function(cb) => {
let key = uuid::Uuid::new_v4().to_string();
lua.set_named_registry_value(&key, cb)?;
let (tx, mut rx) = mpsc::unbounded_channel();
let res = tracing_subscriber::fmt()
.event_format(format)
Expand All @@ -74,12 +67,13 @@ pub(crate) fn setup_tracing(
if res {
super::a_sync::tokio().spawn(async move {
while let Some(msg) = rx.recv().await {
super::callback().invoke(cb.clone(), msg);
super::callback().invoke(key.clone(), msg, false);
}
});
}
res
}
},
_ => return Err(LuaError::BindError), // TODO full BadArgument type??
};

Ok(success)
Expand Down
9 changes: 2 additions & 7 deletions src/ffi/lua/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod workspace;

use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

// define multiple entrypoints, so this library can have multiple names and still work
#[mlua::lua_module(name = "codemp")]
Expand Down Expand Up @@ -57,16 +56,12 @@ fn entrypoint(lua: &Lua) -> LuaResult<LuaTable> {
"poll_callback",
lua.create_function(|lua, ()| {
let mut val = LuaMultiValue::new();
match ext::callback().recv() {
match ext::callback().recv(lua) {
None => {}
Some(ext::callback::LuaCallback::Invoke(cb, arg)) => {
Some((cb, arg)) => {
val.push_back(LuaValue::Function(cb));
val.push_back(arg.into_lua(lua)?);
}
Some(ext::callback::LuaCallback::Fail(msg)) => {
val.push_back(false.into_lua(lua)?);
val.push_back(msg.into_lua(lua)?);
}
}
Ok(val)
})?,
Expand Down
18 changes: 14 additions & 4 deletions src/ffi/lua/workspace.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;

use super::ext::a_sync::a_sync;

Expand Down Expand Up @@ -67,12 +66,23 @@ impl LuaUserData for CodempWorkspace {

methods.add_method("poll", |_, this, ()| a_sync! { this => this.poll().await? });

methods.add_method("callback", |_, this, (cb,): (LuaFunction,)| {
methods.add_method("callback", |lua, this, (cb,): (LuaFunction,)| {
let key = this.lua_callback_id();
lua.set_named_registry_value(&key, cb)?;
Ok(this.callback(move |controller: CodempWorkspace| {
super::ext::callback().invoke(cb.clone(), controller)
super::ext::callback().invoke(key.clone(), controller, false)
}))
});

methods.add_method("clear_callback", |_, this, ()| Ok(this.clear_callback()));
methods.add_method("clear_callback", |lua, this, ()| {
this.clear_callback();
lua.unset_named_registry_value(&this.lua_callback_id())
});
}
}

impl CodempWorkspace {
fn lua_callback_id(&self) -> String {
format!("codemp-workspace({})-callback-registry", self.id())
}
}

0 comments on commit 456a78d

Please sign in to comment.