nostrdb-rs

nostrdb in rust!
git clone git://jb55.com/nostrdb-rs
Log | Files | Refs | Submodules | README | LICENSE

commit 947ea5ed6e477a8016b38c02e1df7575e8abf2a8
parent 3deb94aef3f436469158c4424650d81be26f9315
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  9 Dec 2024 17:47:35 -0800

config: add friendly interface to sub callback

This adds a way to set a rust callback inside nostrdb using a helper
trampoline function.

This can be used to wakeup the render thread in notedeck, as well
lead into our Query Future implementation which will need to be
woken up and polled.

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Msrc/config.rs | 38+++++++++++++++++++++++++++++++++++---
Msrc/ndb.rs | 19++++++++++++++++++-
2 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/src/config.rs b/src/config.rs @@ -3,6 +3,8 @@ use crate::bindings; #[derive(Copy, Clone)] pub struct Config { pub config: bindings::ndb_config, + // We add a flag to know if we've installed a Rust closure so we can clean it up in Drop. + is_rust_closure: bool, } impl Default for Config { @@ -27,7 +29,11 @@ impl Config { bindings::ndb_default_config(&mut config); } - Config { config } + let is_rust_closure = false; + Config { + config, + is_rust_closure, + } } // @@ -48,6 +54,23 @@ impl Config { self } + /// Set a callback for when we have + pub fn set_sub_callback<F>(mut self, closure: F) -> Self + where + F: FnMut(u64) + 'static, + { + // Box the closure to ensure it has a stable address. + let boxed_closure: Box<dyn FnMut(u64)> = Box::new(closure); + + // Convert it to a raw pointer to store in sub_cb_ctx. + let ctx_ptr = Box::into_raw(Box::new(boxed_closure)) as *mut ::std::os::raw::c_void; + + self.config.sub_cb = Some(sub_callback_trampoline); + self.config.sub_cb_ctx = ctx_ptr; + self.is_rust_closure = true; + self + } + pub fn set_mapsize(mut self, bytes: usize) -> Self { self.config.mapsize = bytes; self @@ -58,10 +81,19 @@ impl Config { self } - // Add other setter methods as needed - // Internal method to get a raw pointer to the config, used in Ndb pub fn as_ptr(&self) -> *const bindings::ndb_config { &self.config } } + +extern "C" fn sub_callback_trampoline(ctx: *mut ::std::os::raw::c_void, subid: u64) { + unsafe { + // Convert the raw pointer back into a reference to our closure. + // We know this pointer was created by Box::into_raw in `set_sub_callback_rust`. + let closure_ptr = ctx as *mut Box<dyn FnMut(u64)>; + assert!(!closure_ptr.is_null()); + let closure = &mut *closure_ptr; + closure(subid); + } +} diff --git a/src/ndb.rs b/src/ndb.rs @@ -15,6 +15,11 @@ use tracing::debug; #[derive(Debug)] struct NdbRef { ndb: *mut bindings::ndb, + + /// Have we configured a rust closure for our callback? If so we need + /// to clean that up when this is dropped + has_rust_closure: bool, + rust_cb_ctx: *mut ::std::os::raw::c_void, } /// It's safe to have multi-threaded references to this because thread safety @@ -27,6 +32,11 @@ impl Drop for NdbRef { fn drop(&mut self) { unsafe { bindings::ndb_destroy(self.ndb); + + if self.has_rust_closure && !self.rust_cb_ctx.is_null() { + // Rebuild the Box from the raw pointer and drop it. + let _ = Box::from_raw(self.rust_cb_ctx as *mut Box<dyn FnMut()>); + } } } } @@ -79,7 +89,14 @@ impl Ndb { return Err(Error::DbOpenFailed); } - let refs = Arc::new(NdbRef { ndb }); + let has_rust_closure = !config.config.sub_cb_ctx.is_null(); + let rust_cb_ctx = config.config.sub_cb_ctx; + let refs = Arc::new(NdbRef { + ndb, + has_rust_closure, + rust_cb_ctx, + }); + Ok(Ndb { refs }) }