Skip to content
Snippets Groups Projects
Unverified Commit 0a132cbc authored by Sergey V's avatar Sergey V
Browse files

refactor: rm Stash

parent 51e20a36
No related branches found
No related tags found
1 merge request!64refactor: rm Stash
Pipeline #3664 passed
......@@ -10,45 +10,29 @@ use ::tarantool::set_error;
use ::tarantool::tlua;
use ::tarantool::tuple::{FunctionArgs, FunctionCtx, Tuple};
use indoc::indoc;
use message::Message;
use std::convert::TryFrom;
use std::time::Duration;
mod app;
mod args;
mod error;
mod message;
pub mod stash;
mod tarantool;
mod tlog;
mod traft;
inventory::collect!(InnerTest);
pub struct InnerTest {
pub name: &'static str,
pub body: fn(),
}
inventory::collect!(InnerTest);
use message::Message;
use std::cell::Ref;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::time::Duration;
static mut NODE: Option<&'static traft::Node> = None;
#[derive(Default)]
pub struct Stash {
raft_node: RefCell<Option<traft::Node>>,
}
impl Stash {
pub fn access() -> &'static Self {
stash::access("_picolib_stash")
}
pub fn set_raft_node(&self, raft_node: traft::Node) {
*self.raft_node.borrow_mut() = Some(raft_node);
}
pub fn raft_node(&self) -> Ref<Option<traft::Node>> {
self.raft_node.borrow()
}
fn node() -> &'static traft::Node {
unsafe { NODE.expect("Picodata not running yet") }
}
fn picolib_setup(args: args::Run) {
......@@ -79,17 +63,17 @@ fn picolib_setup(args: args::Run) {
//
// Export public API
luamod.set("run", tlua::function0(move || start(&args)));
luamod.set("raft_status", tlua::function0(raft_status));
luamod.set("raft_status", tlua::function0(|| node().status()));
luamod.set("raft_tick", tlua::function1(raft_tick));
luamod.set(
"raft_propose_info",
tlua::function1(|x: String| raft_propose(Message::Info { msg: x })),
tlua::function1(|x: String| node().propose(&Message::Info { msg: x })),
);
luamod.set(
"raft_propose_eval",
tlua::function2(|timeout: f64, x: String| {
raft_propose_wait_applied(
Message::EvalLua { code: x },
node().propose_wait_applied(
&Message::EvalLua { code: x },
Duration::from_secs_f64(timeout),
)
}),
......@@ -110,8 +94,6 @@ fn picolib_setup(args: args::Run) {
}
fn start(args: &args::Run) {
let stash = Stash::access();
if tarantool::cfg().is_some() {
// Already initialized
return;
......@@ -175,8 +157,12 @@ fn start(args: &args::Run) {
applied: traft::Storage::applied().unwrap().unwrap_or_default(),
..Default::default()
};
let node = traft::Node::new(&raft_cfg, handle_committed_data).unwrap();
stash.set_raft_node(node);
unsafe {
NODE = Some(Box::leak(Box::new(
traft::Node::new(&raft_cfg, handle_committed_data).unwrap(),
)));
};
cfg.listen = Some(args.listen.clone());
tarantool::set_cfg(&cfg);
......@@ -191,35 +177,12 @@ fn start(args: &args::Run) {
}
fn raft_tick(n_times: u32) {
let stash = Stash::access();
let raft_ref = stash.raft_node();
let raft_node = raft_ref.as_ref().expect("Picodata not running yet");
let raft_node = node();
for _ in 0..n_times {
raft_node.tick();
}
}
fn raft_status() -> traft::Status {
let stash = Stash::access();
let raft_ref = stash.raft_node();
let raft_node = raft_ref.as_ref().expect("Picodata not running yet");
raft_node.status()
}
fn raft_propose(msg: Message) {
let stash = Stash::access();
let raft_ref = stash.raft_node();
let raft_node = raft_ref.as_ref().expect("Picodata not running yet");
raft_node.propose(&msg)
}
fn raft_propose_wait_applied(msg: Message, timeout: Duration) -> bool {
let stash = Stash::access();
let raft_ref = stash.raft_node();
let raft_node = raft_ref.as_ref().expect("Picodata not running yet");
raft_node.propose_wait_applied(&msg, timeout)
}
fn handle_committed_data(data: &[u8]) {
use Message::*;
......@@ -246,11 +209,7 @@ fn init_handlers() {
#[no_mangle]
pub extern "C" fn raft_interact(_: FunctionCtx, args: FunctionArgs) -> c_int {
let stash = Stash::access();
let raft_ref = stash.raft_node();
let raft_node = raft_ref
.as_ref()
.expect("picodata should already be running");
let raft_node = node();
// Conversion pipeline:
// FunctionArgs -> Tuple -?-> traft::row::Message -?-> raft::Message;
......
//! Tarantool-flavored singleton pattern implementation.
//!
//! Tarantool has a peculiarity - when calling stored procedures with `{language = "C"}` option, it
//! disregards the shared object (`.so` / `.dylib`) already loaded by Lua and makes the second
//! independent `dlopen`. As a result, Lua and C stored procedures can't share state, because even
//! static variables point to different memory locations.
//!
//! As a workaround, this module provides the API for hiding the state inside `lua_State`. Under the
//! hood, the stash consumes a custom struct and leaks the wrapping box. Inside Lua, it's
//! represented by a userdata, which the `access()` function provides access to.
//!
//! Example:
//!
//! ```
//! use std::cell::Ref;
//! use std::cell::RefCell;
//!
//! /// The particular singleton structure
//! #[derive(Default)]
//! struct Stash {
//! x: RefCell<u32>,
//! }
//!
//! unsafe impl Send for Stash {}
//! unsafe impl Sync for Stash {}
//! impl Stash {
//! pub fn access() -> &'static Self {
//! stash::access("my_stash")
//! }
//! pub fn set_x(&self, x: u32) {
//! *self.x.borrow_mut() = x;
//! }
//! pub fn x(&self) -> Ref<u32> {
//! self.x.borrow()
//! }
//! }
//!
//! #[no_mangle]
//! pub extern "C" fn luaopen_easy(_l: std::ffi::c_void) -> std::os::raw::c_int {
//! // Tarantool calls this function upon require("easy")
//! let stash = Stash::access();
//! stash.set_x(100);
//! assert_eq!(*stash.x(), 100);
//! 0
//! }
//! ```
//!
use ::tarantool::tlua;
use std::ops::Deref;
#[derive(Default)]
struct Inner<S>(S);
impl<S> std::ops::Deref for Inner<S> {
type Target = S;
#[inline]
fn deref(&self) -> &S {
&self.0
}
}
impl<L, S> tlua::PushInto<L> for Inner<S>
where
L: tlua::AsLua,
S: 'static,
{
type Err = tlua::Void;
fn push_into_lua(self, lua: L) -> Result<tlua::PushGuard<L>, (tlua::Void, L)> {
let boxed = Box::new(self);
let ptr: &'static Inner<S> = Box::leak(boxed);
Ok(tlua::push_userdata(ptr, lua, |_| {}))
}
}
impl<L, S> tlua::PushOneInto<L> for Inner<S>
where
L: tlua::AsLua,
S: 'static,
{
}
impl<L, S> tlua::LuaRead<L> for &Inner<S>
where
L: tlua::AsLua,
{
fn lua_read_at_position(lua: L, index: std::num::NonZeroI32) -> Result<&'static Inner<S>, L> {
let val: tlua::UserdataOnStack<&Inner<S>, _> =
tlua::LuaRead::lua_read_at_position(lua, index)?;
Ok(val.deref())
}
}
/// Returns reference to the particular singleton structure.
///
/// When called for the first time, initializes it with the default values.
pub fn access<S>(name: &str) -> &'static S
where
S: Default,
{
// TODO: this place is a potential performance bottleneck.
// Every access to the stash implies a call to `lua_newthread()`
// and further garbage collection.
let l = tarantool::lua_state();
let get = || l.get::<&Inner<S>, _>(name);
match get() {
Some(Inner(v)) => v,
None => {
l.set(name, Inner::<S>::default());
get().expect("impossible")
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment