use std::panic;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use crate::options::NetworkOption;
use crate::{error, FdbResult};
use foundationdb_sys as fdb_sys;
pub fn get_max_api_version() -> i32 {
unsafe { fdb_sys::fdb_get_max_api_version() }
}
static VERSION_SELECTED: AtomicBool = AtomicBool::new(false);
pub struct FdbApiBuilder {
runtime_version: i32,
}
impl FdbApiBuilder {
pub fn runtime_version(&self) -> i32 {
self.runtime_version
}
pub fn set_runtime_version(mut self, version: i32) -> Self {
self.runtime_version = version;
self
}
pub fn build(self) -> FdbResult<NetworkBuilder> {
if VERSION_SELECTED
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
panic!("the fdb select api version can only be run once per process");
}
error::eval(unsafe {
fdb_sys::fdb_select_api_version_impl(
self.runtime_version,
fdb_sys::FDB_API_VERSION as i32,
)
})?;
Ok(NetworkBuilder { _private: () })
}
}
impl Default for FdbApiBuilder {
fn default() -> Self {
FdbApiBuilder {
runtime_version: fdb_sys::FDB_API_VERSION as i32,
}
}
}
pub struct NetworkBuilder {
_private: (),
}
impl NetworkBuilder {
pub fn set_option(self, option: NetworkOption) -> FdbResult<Self> {
unsafe { option.apply()? };
Ok(self)
}
#[allow(clippy::mutex_atomic)]
pub fn build(self) -> FdbResult<(NetworkRunner, NetworkWait)> {
unsafe { error::eval(fdb_sys::fdb_setup_network())? }
let cond = Arc::new((Mutex::new(false), Condvar::new()));
Ok((NetworkRunner { cond: cond.clone() }, NetworkWait { cond }))
}
pub unsafe fn boot(self) -> FdbResult<NetworkAutoStop> {
let (runner, cond) = self.build()?;
let net_thread = runner.spawn();
let network = cond.wait();
Ok(NetworkAutoStop {
handle: Some(net_thread),
network: Some(network),
})
}
}
pub struct NetworkRunner {
cond: Arc<(Mutex<bool>, Condvar)>,
}
impl NetworkRunner {
pub unsafe fn run(self) -> FdbResult<()> {
self._run()
}
fn _run(self) -> FdbResult<()> {
{
let (lock, cvar) = &*self.cond;
let mut started = lock.lock().unwrap();
*started = true;
cvar.notify_one();
}
error::eval(unsafe { fdb_sys::fdb_run_network() })
}
unsafe fn spawn(self) -> thread::JoinHandle<()> {
thread::spawn(move || {
self.run().expect("failed to run network thread");
})
}
}
pub struct NetworkWait {
cond: Arc<(Mutex<bool>, Condvar)>,
}
impl NetworkWait {
pub fn wait(self) -> NetworkStop {
{
let (lock, cvar) = &*self.cond;
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
}
NetworkStop { _private: () }
}
}
pub struct NetworkStop {
_private: (),
}
impl NetworkStop {
pub fn stop(self) -> FdbResult<()> {
error::eval(unsafe { fdb_sys::fdb_stop_network() })
}
}
pub struct NetworkAutoStop {
network: Option<NetworkStop>,
handle: Option<std::thread::JoinHandle<()>>,
}
impl Drop for NetworkAutoStop {
fn drop(&mut self) {
if let Err(err) = self.network.take().unwrap().stop() {
eprintln!("failed to stop network: {}", err);
std::process::abort();
}
self.handle
.take()
.unwrap()
.join()
.expect("failed to join fdb thread");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_api() {
assert!(get_max_api_version() > 0);
}
}