Correct kvengine defs and add custom `Lazy` type

next
Sayan Nandan 3 years ago
parent 74126ec7cb
commit 7ea890765d

@ -0,0 +1,174 @@
/*
* Created on Sat Jul 03 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use core::hint::spin_loop as let_the_cpu_relax;
use core::mem;
use core::ops::Deref;
use core::ptr;
use core::sync::atomic::AtomicBool;
use core::sync::atomic::AtomicPtr;
use core::sync::atomic::Ordering;
const ORD_ACQ: Ordering = Ordering::Acquire;
const ORD_SEQ: Ordering = Ordering::SeqCst;
/// A lazily intialized, or _call by need_ value
#[derive(Debug)]
pub struct Lazy<T, F> {
/// the value (null at first)
value: AtomicPtr<T>,
/// the function that will init the value
init_func: F,
/// is some thread trying to initialize the value
init_state: AtomicBool,
}
impl<T, F> Lazy<T, F> {
pub const fn new(init_func: F) -> Self {
Self {
value: AtomicPtr::new(ptr::null_mut()),
init_func,
init_state: AtomicBool::new(false),
}
}
}
impl<T, F> Deref for Lazy<T, F>
where
F: Fn() -> T,
{
type Target = T;
fn deref(&self) -> &Self::Target {
let value_ptr = self.value.load(ORD_ACQ);
if !value_ptr.is_null() {
// the value has already been initialized, return
unsafe {
// UNSAFE(@ohsayan): We've just asserted that the value is not null
return &*value_ptr;
}
}
// it's null, so it's useless
// hold on until someone is trying to init
while self
.init_state
.compare_exchange(false, true, ORD_SEQ, ORD_SEQ)
.is_err()
{
let_the_cpu_relax();
}
/*
see the value before the last store. while we were one the loop,
some other thread could have initialized it already
*/
let value_ptr = self.value.load(ORD_ACQ);
if !value_ptr.is_null() {
// no more init, someone initialized it already
assert!(self.init_state.swap(false, ORD_SEQ));
unsafe {
// UNSAFE(@ohsayan): We've already loaded the value checked
// that it isn't null
&*value_ptr
}
} else {
// so no one cared to initialize the value in between
// fine, we'll init it
let value = (self.init_func)();
let value_ptr = Box::into_raw(Box::new(value));
// now swap out the older value and check it for sanity
assert!(self.value.swap(value_ptr, ORD_SEQ).is_null());
// set trying to init flag to false
assert!(self.init_state.swap(false, ORD_SEQ));
unsafe {
// UNSAFE(@ohsayan): We just initialized the value ourselves
// so it is not null!
&*value_ptr
}
}
}
}
impl<T, F> Drop for Lazy<T, F> {
fn drop(&mut self) {
if mem::needs_drop::<T>() {
// this needs drop
let value_ptr = self.value.load(ORD_ACQ);
if !value_ptr.is_null() {
unsafe {
// UNSAFE(@ohsayan): We've just checked if the value is null or not
mem::drop(Box::from_raw(value_ptr))
}
}
}
}
}
cfg_test!(
use crate::coredb::htable::{HTable, Data};
use crate::coredb::lazy;
use std::thread;
typedef!(
/// A function that returns a [`HTable`]
RetNs = fn() -> HTable<Data, Data>;
/// A [`Htable<Data, Data>`] typedef
Htable = HTable<Data, Data>;
);
static LAZY_VALUE: lazy::Lazy<Htable, RetNs> = lazy::Lazy::new(|| {
let ht = HTable::new();
ht.true_if_insert("sayan".into(), "is doing something".into());
ht
});
#[test]
fn test_lazy() {
assert_eq!(
LAZY_VALUE.get("sayan".as_bytes()).unwrap().clone(),
Data::from("is doing something")
);
}
#[test]
fn test_two_threads_trying_to_get_at_once() {
let (t1, t2) = (
thread::spawn(|| {
assert_eq!(
LAZY_VALUE.get("sayan".as_bytes()).unwrap().clone(),
Data::from("is doing something")
);}),
thread::spawn(|| {
assert_eq!(
LAZY_VALUE.get("sayan".as_bytes()).unwrap().clone(),
Data::from("is doing something")
);
})
);
{
t1.join().unwrap();
t2.join().unwrap();
}
}
);

@ -24,11 +24,42 @@
* *
*/ */
//! # In-memory store
//!
//! This is what things look like:
//! ```text
//! ------------------------------------------------------
//! | | |
//! | |-------------------| | |-------------------| |
//! | |-------------------| | |-------------------| |
//! | | | TABLE | TABLE | | | | | TABLE | TABLE | | |
//! | | |-------|-------| | | | |-------|-------| | |
//! | | Keyspace | | | Keyspace | |
//! | |-------------------| | |-------------------| |
//! | |
//! | |-------------------| | |-------------------| |
//! | | |-------|-------| | | | |-------|-------| | |
//! | | | TABLE | TABLE | | | | | TABLE | TABLE | | |
//! | | |-------|-------| | | | |-------|-------| | |
//! | | Keyspace | | | Keyspace | |
//! | |-------------------| | |-------------------| |
//! | | |
//! | | |
//! | NAMESPACE | NAMESPACE |
//! ------------------------------------------------------
//! | NODE |
//! |----------------------------------------------------|
//! ```
//!
//! So, all your data is at the mercy of [`Memstore`]'s constructor
//! and destructor.
#![allow(dead_code)] // TODO(@ohsayan): Remove this onece we're done #![allow(dead_code)] // TODO(@ohsayan): Remove this onece we're done
use crate::coredb::htable::Data; use crate::coredb::htable::Data;
use crate::coredb::htable::HTable; use crate::coredb::htable::HTable;
use crate::coredb::SnapshotStatus; use crate::coredb::SnapshotStatus;
use crate::kvengine::KVEngine;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
@ -63,8 +94,8 @@ impl Default for ReplicationStrategy {
/// This in-memory table that houses all keyspaces and namespaces along with other node /// This in-memory table that houses all keyspaces and namespaces along with other node
/// properties /// properties
pub struct Memstore { pub struct Memstore {
/// the namespaces /// the keyspaces
namespace: HTable<Data, Arc<Namespace>>, keyspaces: HTable<Data, Arc<Keyspace>>,
/// the shard range /// the shard range
shard_range: ClusterShardRange, shard_range: ClusterShardRange,
} }
@ -72,10 +103,10 @@ pub struct Memstore {
// TODO(@ohsayan): Optimize the memory layouts of the UDFs to ensure that sharing is very cheap // TODO(@ohsayan): Optimize the memory layouts of the UDFs to ensure that sharing is very cheap
#[derive(Debug)] #[derive(Debug)]
/// The namespace that houses all the other tables /// The keyspace that houses all the other tables
pub struct Namespace { pub struct Keyspace {
/// the tables /// the tables
tables: HTable<Data, Arc<Keyspace>>, tables: HTable<Data, Arc<Table>>,
/// current state of the disk flush status. if this is true, we're safe to /// current state of the disk flush status. if this is true, we're safe to
/// go ahead with writes /// go ahead with writes
flush_state_healthy: AtomicBool, flush_state_healthy: AtomicBool,
@ -88,16 +119,8 @@ pub struct Namespace {
// same 8 byte ptrs; any chance of optimizations? // same 8 byte ptrs; any chance of optimizations?
#[derive(Debug)] #[derive(Debug)]
/// The underlying keyspace type. This is the place for the other data models (soon!) /// The underlying table type. This is the place for the other data models (soon!)
pub enum Keyspace { pub enum Table {
/// a key/value store /// a key/value store
KV(KVStore), KV(KVEngine),
}
#[derive(Debug)]
/// The keyspace that houses atomic references to the actual key value pairs. Again, no one
/// owns anything: just pointers
pub struct KVStore {
/// the inner table
table: HTable<Data, Data>,
} }

@ -39,7 +39,8 @@ pub use htable::Data;
use libsky::TResult; use libsky::TResult;
use std::sync::Arc; use std::sync::Arc;
pub mod htable; pub mod htable;
mod lock; pub mod lazy;
pub mod lock;
mod memstore; mod memstore;
/// This is a thread-safe database handle, which on cloning simply /// This is a thread-safe database handle, which on cloning simply

@ -72,6 +72,7 @@ impl<'a> ShardLock<'a> {
// DROP impl isn't required as ShardLock's field types need-drop (std::mem) // DROP impl isn't required as ShardLock's field types need-drop (std::mem)
/// The key/value engine that acts as the in-memory backing store for the database /// The key/value engine that acts as the in-memory backing store for the database
#[derive(Debug)]
pub struct KVEngine { pub struct KVEngine {
/// the atomic table /// the atomic table
table: HTable<Data, Data>, table: HTable<Data, Data>,

@ -73,3 +73,20 @@ macro_rules! consts {
)* )*
}; };
} }
#[macro_export]
macro_rules! typedef {
($($(#[$attr:meta])* $ident:ident = $ty:ty;)*) => {
$($(#[$attr])* type $ident = $ty;)*
};
($($(#[$attr:meta])* $vis:vis $ident:ident = $ty:ty;)*) => {
$($(#[$attr])* $vis type $ident = $ty;)*
};
}
#[macro_export]
macro_rules! cfg_test {
($($item:item)*) => {
$(#[cfg(test)] $item)*
};
}

Loading…
Cancel
Save