Recently I started playing around with rust ffi. I’m trying to wrap a small postgres driver and provide a c interop interface. I’m using the sqlx
pg driver under the hood.
I’m having the following piece of code.
static mut SQLX4K: OnceLock<Sqlx4k> = OnceLock::new();
#[derive(Debug)]
struct Sqlx4k<'a> {
runtime: Runtime,
pool: PgPool,
tx_id: RwLock<Vec<u8>>,
tx: &'a mut [*mut Transaction<'a, Postgres>],
}
impl<'a> Sqlx4k<'a> {
fn tx_begin(&mut self) -> i64 {
let tx = self.runtime.block_on(self.pool.begin()).unwrap();
let id = { self.tx_id.write().unwrap().pop().unwrap() };
let tx = Box::new(tx);
let tx = Box::leak(tx);
self.tx[id as usize] = tx;
id.into()
}
fn tx_commit(&mut self, tx: i64) {
let id = tx;
let tx = unsafe { *Box::from_raw(self.tx[id as usize]) };
self.tx[id as usize] = null_mut();
self.runtime.block_on(tx.commit()).unwrap();
self.tx_id.write().unwrap().push(id as u8)
}
fn tx_rollback(&mut self, tx: i64) {
let id = tx;
let tx = unsafe { *Box::from_raw(self.tx[id as usize]) };
self.tx[id as usize] = null_mut();
self.runtime.block_on(tx.rollback()).unwrap();
self.tx_id.write().unwrap().push(id as u8)
}
fn tx_query(&mut self, tx: i64, sql: &str) {
let id = tx;
unsafe {
let mut tx = Box::from_raw(self.tx[id as usize]);
let query = tx.fetch_optional(sql);
let _result = self.runtime.block_on(query);
};
}
}
The main concept is that I have a static memory space that I store all the necessary parts of the custom wrapper driver.
Then I expose a C function like:
#[no_mangle]
pub extern "C" fn sqlx4k_tx_begin() -> c_long {
unsafe { SQLX4K.get_mut().unwrap() }.tx_begin().into()
}
#[no_mangle]
pub extern "C" fn sqlx4k_tx_commit(tx: c_long) {
unsafe { SQLX4K.get_mut().unwrap() }.tx_commit(tx);
}
#[no_mangle]
pub extern "C" fn sqlx4k_tx_rollback(tx: c_long) {
unsafe { SQLX4K.get_mut().unwrap() }.tx_rollback(tx);
}
#[no_mangle]
pub extern "C" fn sqlx4k_tx_query(tx: c_long, sql: *const c_char) -> *mut Sqlx4kResult {
let sql = c_chars_to_str(sql).unwrap();
unsafe { SQLX4K.get_mut().unwrap() }.tx_query(tx, sql);
ok()
}
My main goal is to support transactions.
So for this particular reason I decided to create a mutable slice and store all the generated transactions. Also, for each transaction I have an id
that I return to the C
code as a reference.
The problem is that each time I call the tx_query
method I get this functionality requires a Tokio context
error
Also my Cargo.toml
:
[dependencies]
once_cell = { version = "1.19.0" }
tokio = { version = "1.38.0", features = ["full"] }
sqlx = { version = "0.7.4", features = [
"runtime-tokio", # Use the tokio runtime without enabling a TLS backend.
"postgres", # Add support for the Postgres database server.
] }
Here is the backtrace:
thread '<unnamed>' panicked at /Users/smyrgeorge/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/pool/connection.rs:169:13:
this functionality requires a Tokio context
stack backtrace:
0: 0x101065ff4 - std::backtrace_rs::backtrace::libunwind::trace::h6de1cbf3f672a4f8
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5
1: 0x101065ff4 - std::backtrace_rs::backtrace::trace_unsynchronized::hd0de2d5ef13b6f4d
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x101065ff4 - std::sys_common::backtrace::_print_fmt::h2a33510d9b3bb866
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys_common/backtrace.rs:68:5
3: 0x101065ff4 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h01b2beffade888b2
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys_common/backtrace.rs:44:22
4: 0x10100048c - core::fmt::rt::Argument::fmt::h5ddc0f22b2928899
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/fmt/rt.rs:142:9
5: 0x10100048c - core::fmt::write::hbadb443a71b75f23
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/fmt/mod.rs:1153:17
6: 0x10104988c - std::io::Write::write_fmt::hc09d7755e3ead5f0
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/io/mod.rs:1843:15
7: 0x101069a48 - std::sys_common::backtrace::_print::h3cd1786cbb1caf0f
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys_common/backtrace.rs:47:5
8: 0x101069a48 - std::sys_common::backtrace::print::h28349e5c25acbac7
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys_common/backtrace.rs:34:9
9: 0x101069394 - std::panicking::default_hook::{{closure}}::hd24b6196784d991e
10: 0x101068f60 - std::panicking::default_hook::hfcec80a2720c8c73
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:292:9
11: 0x10106a2a4 - std::panicking::rust_panic_with_hook::h84760468187ddc85
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:779:13
12: 0x101069d20 - std::panicking::begin_panic_handler::{{closure}}::he666a5eb600a7203
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:649:13
13: 0x101069cb0 - std::sys_common::backtrace::__rust_end_short_backtrace::h592f44d2bf9f843f
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys_common/backtrace.rs:171:18
14: 0x101069ca4 - rust_begin_unwind
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5
15: 0x1010849b4 - core::panicking::panic_fmt::h98bbf7bdf4994454
at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:72:14
16: 0x101033624 - sqlx_core::rt::missing_rt::hd58e9fd585cd27b3
17: 0x1010474e8 - <sqlx_core::pool::connection::PoolConnection<DB> as core::ops::drop::Drop>::drop::hbaea548330ec54f2
18: 0x1010142f8 - core::ptr::drop_in_place<alloc::boxed::Box<sqlx_core::transaction::Transaction<sqlx_postgres::database::Postgres>>>::h65e711f5dba2104f
19: 0x101014498 - _sqlx4k_tx_fetch_all
20: 0x100fd7ea0 - kfun:#main(){}
at /Users/smyrgeorge/dev/projects/test/sqlx4k/src/nativeMain/kotlin/Main.kt:59:5
21: 0x100ff8040 - _Init_and_run_start
22: 0x100ff820c - _main
The strange is that the tx_commit
and tx_rollback
function work ok.
What is the origin of the problem.
Also I have to mention that I started this project just to obtain experience in rust ffi.
So, there is a lot of unsafe code.
I also have an implementation that makes use of a HashMap
(to store the transactions).
But, i just wanted to try something another approach (with less locks)
PS: I understand that may be some memory issues in this piece of code. I’m just trying to understand what is happening