Skip to content

Commit

Permalink
tmp commit 2
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannes Wünsche committed Jun 29, 2023
1 parent 1bc5258 commit c00342b
Show file tree
Hide file tree
Showing 27 changed files with 1,133 additions and 183 deletions.
2 changes: 1 addition & 1 deletion betree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ criterion = "0.3"
tempfile = "3.6.0"

[features]
default = ["init_env_logger", "figment_config"]
default = ["init_env_logger", "figment_config", "nvm"]

# unlock unstable API for consumption by bectl and other debugging tools
internal-api = []
Expand Down
8 changes: 7 additions & 1 deletion betree/include/betree.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef betree_h
#define betree_h

/* Generated with cbindgen:0.24.3 */
/* Generated with cbindgen:0.24.5 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

Expand Down Expand Up @@ -467,6 +467,12 @@ int betree_object_read_at(struct obj_t *obj,
unsigned long *n_read,
struct err_t **err);

/**
* Return the objects size in bytes. If the size could not be determined
* it is assumed the object is zero-sized.
*/
unsigned long betree_object_size(const struct obj_t *obj, struct err_t **err);

/**
* Try to write `buf_len` bytes from `buf` into `obj`, starting at `offset` bytes into the objects
* data.
Expand Down
1 change: 1 addition & 0 deletions betree/pmem-hashmap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2021"
bindgen = "0.65"

[dependencies]
core_affinity = "0.8.0"
errno = "0.3.1"
thiserror = "1.0.40"
twox-hash = "1.6.3"
Expand Down
9 changes: 9 additions & 0 deletions betree/pmem-hashmap/c_impl/hashmap_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,12 @@ int hm_tx_cmd(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, unsigned cmd,
return -EINVAL;
}
}

int empty_constr(PMEMobjpool *pop, void *ptr, void *arg) { return 0; }

int haura_alloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num,
void *arg) {
return pmemobj_alloc(pop, oidp, size, type_num, &empty_constr, arg);
}

void *haura_direct(PMEMoid oid) { return pmemobj_direct(oid); }
6 changes: 6 additions & 0 deletions betree/pmem-hashmap/c_impl/hashmap_tx.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
#define HASHMAP_TX_TYPE_OFFSET 1004
#endif

int empty_constr(PMEMobjpool *pop, void *ptr, void *arg);

int haura_alloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num,
void *arg);
void *haura_direct(PMEMoid oid);

struct hashmap_tx;
TOID_DECLARE(struct hashmap_tx, HASHMAP_TX_TYPE_OFFSET + 0);

Expand Down
292 changes: 292 additions & 0 deletions betree/pmem-hashmap/src/allocator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
use super::*;
use errno::errno;
use std::alloc::{AllocError, Allocator};
use std::{ffi::c_void, ptr::NonNull, sync::Arc};
use thiserror::Error;

// A friendly persistent memory allocator.
#[derive(Clone, Debug)]
pub struct Pal {
pool: Arc<NonNull<pmemobjpool>>,
}

// Parallel access should be fine, but can be exploited. TODO: Add safeguard for this?
unsafe impl Sync for Pal {}
// No thread-local data
unsafe impl Send for Pal {}

impl Drop for Pal {
fn drop(&mut self) {
// there should exist no further reference to this resources otherwise we risk some invalid fetches
if Arc::strong_count(&self.pool) == 0 && Arc::weak_count(&self.pool) == 0 {
self.close()
}
}
}

impl Into<AllocError> for PalError {
fn into(self) -> AllocError {
AllocError
}
}

unsafe impl Allocator for Pal {
fn allocate(&self, layout: std::alloc::Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = self.allocate(layout.size()).map_err(|_| AllocError)?;
Ok(NonNull::new(unsafe {
core::slice::from_raw_parts_mut(ptr.load() as *mut u8, layout.size())
})
.ok_or_else(|| AllocError)?)
}

unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: std::alloc::Layout) {
let mut oid = unsafe { pmemobj_oid(ptr.as_ptr() as *const c_void) };
unsafe { pmemobj_free(&mut oid) }
}
}

#[inline]
fn oid_is_null(oid: PMEMoid) -> bool {
oid.off == 0
}

#[derive(Debug, Error)]
pub enum PalError {
#[error("Opening failed (`{0}`). This error originates in libpmemobj.")]
OpenFailed(String),
#[error("Allocation failed (`{0}`). This error originates in libpmemobj.")]
AllocationFailed(String),
#[error("Null")]
NullEncountered,
#[error("Could not deduce path: `{0}`")]
InvalidPath(#[from] std::ffi::NulError),
}

// A friendly persistent pointer. Useless without the according handle to the
// original arena.
#[derive(Debug, Clone, Copy)]
pub struct PalPtr {
inner: PMEMoid,
size: usize,
}

impl PalPtr {
/// Translate this persistent ptr to a volatile one.
pub fn load(&self) -> *mut c_void {
unsafe { haura_direct(self.inner) }
}

/// Copy a range of bytes behind this pointer to a given buffer. Data is
/// copied until the allocated area is read entirely or the given buffer is
/// filled entirely.
pub fn copy_to(&self, other: &mut [u8], arena: &Pal) {
unsafe {
pmemobj_memcpy(
arena.pool.as_ptr(),
other.as_mut_ptr() as *mut c_void,
self.load(),
self.size.min(other.len()),
PMEMOBJ_F_MEM_NOFLUSH,
)
};
}

/// Copy a range of bytes to the location of this pointer. Data is copied
/// until the allocated area is filled or the given buffer ends.
pub fn copy_from(&self, other: &[u8], arena: &Pal) {
unsafe {
pmemobj_memcpy(
arena.pool.as_ptr(),
self.load(),
other.as_ptr() as *const c_void,
self.size.min(other.len()),
PMEMOBJ_F_MEM_NONTEMPORAL,
);
};
}

/// Deallocate this object. Required if this value is no longer needed.
/// There is *no* automatic deallocation logic.
pub fn free(mut self) {
unsafe { pmemobj_free(&mut self.inner) }
}
}

// TODO: Impl Deref with typization?

impl Pal {
/// Open an existing file representing an arena.
pub fn open<P: Into<std::path::PathBuf>>(path: P) -> Result<Self, PalError> {
let pobjpool = {
let path =
std::ffi::CString::new(path.into().to_string_lossy().into_owned())?.into_raw();
unsafe { pmemobj_open(path, std::ptr::null()) }
};
Self::new(pobjpool)
}

/// Create a new arena on persistent memory.
pub fn create<P: Into<std::path::PathBuf>>(
path: P,
bytes: usize,
permissions: u32,
) -> Result<Self, PalError> {
let pobjpool = {
let path =
std::ffi::CString::new(path.into().to_string_lossy().into_owned())?.into_raw();
unsafe { pmemobj_create(path, std::ptr::null(), bytes, permissions) }
};
Self::new(pobjpool)
}

fn new(pool: *mut pmemobjpool) -> Result<Self, PalError> {
NonNull::new(pool)
.map(|valid| Pal {
pool: Arc::new(valid),
})
.ok_or_else(|| {
let err = unsafe { std::ffi::CString::from_raw(pmemobj_errormsg() as *mut i8) };
let err_msg = format!(
"Failed to create memory pool. filepath: {}",
err.to_string_lossy()
);
PalError::OpenFailed(err_msg)
})
}

fn close(&mut self) {
unsafe { pmemobj_close(self.pool.as_ptr()) };
}

/// Allocate an area of size in the persistent memory.
pub fn allocate(&self, size: usize) -> Result<PalPtr, PalError> {
let mut oid = std::mem::MaybeUninit::<PMEMoid>::uninit();
if unsafe {
haura_alloc(
self.pool.as_ptr(),
oid.as_mut_ptr(),
size,
0, // BOGUS
std::ptr::null_mut(),
) != 0
} {
let err = unsafe { std::ffi::CString::from_raw(pmemobj_errormsg() as *mut i8) };
let err_msg = format!(
"Failed to create memory pool. filepath: {}",
err.to_string_lossy()
);
return Err(PalError::AllocationFailed(err_msg));
}

if unsafe { oid_is_null(oid.assume_init_read()) } {
return Err(PalError::NullEncountered);
}
Ok(PalPtr {
inner: unsafe { oid.assume_init() },
size,
})
}

/// Access and allocate the root object if needed. The root object may be
/// extended by calling this function again with a larger value. It may
/// never be shrunken.
///
/// If called with size 0 an existing root object might be opened, if none
/// exists EINVAL is returned.
pub fn root(&self, size: usize) -> Result<PalPtr, PalError> {
let oid = unsafe { pmemobj_root(self.pool.as_ptr(), size) };
if oid_is_null(oid) {
return Err(PalError::AllocationFailed(format!("{}", errno())));
}
Ok(PalPtr { inner: oid, size })
}

/// Return the maximum size of the current root object.
pub fn root_size(&self) -> usize {
unsafe { pmemobj_root_size(self.pool.as_ptr()) }
}
}

#[cfg(test)]
mod tests {

extern crate alloc;

use std::{collections::BTreeMap, path::PathBuf, process::Command};

use alloc::collections::vec_deque::VecDeque;
use tempfile::Builder;

use super::*;

struct TestFile(PathBuf);

impl TestFile {
pub fn new() -> Self {
TestFile(
Builder::new()
.tempfile()
.expect("Could not get tmpfile")
.path()
.to_path_buf(),
)
}

pub fn path(&self) -> &PathBuf {
&self.0
}
}
impl Drop for TestFile {
fn drop(&mut self) {
if !Command::new("rm")
.arg(self.0.to_str().expect("Could not pass tmpfile"))
.output()
.expect("Could not delete")
.status
.success()
{
eprintln!("Could not delete tmpfile");
}
}
}

#[test]
fn alloc_vec_deque() {
let file = TestFile::new();
const SIZE: usize = 64 * 1024 * 1024;
let pal = Pal::create(file.path(), 128 * 1024 * 1024, 0o666).unwrap();
let mut list: VecDeque<u8, Pal> = VecDeque::with_capacity_in(SIZE, pal);
for _ in 0..SIZE {
list.push_back(0);
}
}

#[test]
fn alloc_btree_map() {
let file = TestFile::new();
{
let mut pal = Pal::create(file.path(), 128 * 1024 * 1024, 0o666).unwrap();
let mut map: BTreeMap<u8, u8, Pal> = BTreeMap::new_in(pal.clone());
for id in 0..100 {
map.insert(id, id);
}
for id in 100..0 {
assert_eq!(map.get(&id), Some(&id));
}
drop(map);
pal.close();
}
{
let mut pal = Pal::open(file.path()).unwrap();
let mut map: BTreeMap<u8, u8, Pal> = BTreeMap::new_in(pal.clone());
for id in 0..100 {
map.insert(id, id);
}
for id in 100..0 {
assert_eq!(map.get(&id), Some(&id));
}
drop(map);
pal.close();
}
}
}
Loading

0 comments on commit c00342b

Please sign in to comment.