Skip to content

Commit

Permalink
replace lifetimes with refcounts
Browse files Browse the repository at this point in the history
  • Loading branch information
vhdirk committed Oct 20, 2021
1 parent 73977bc commit 96e31ba
Show file tree
Hide file tree
Showing 23 changed files with 856 additions and 1,099 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "notmuch"
version = "0.6.1"
version = "0.7.0"
authors = ["Dirk Van Haerenborgh <vhdirk@gmail.com>"]
homepage = "https://github.com/vhdirk/notmuch-rs"
repository = "https://github.com/vhdirk/notmuch-rs"
Expand All @@ -16,7 +16,7 @@ travis-ci = { repository = "vhdirk/notmuch-rs" }
[dependencies]
libc = "0.2"
# clippy = { version = "0.0.211", optional = true }
supercow = "0.1.0"
from_variants = "0.6.0"

[dev-dependencies]
dirs = "1.0"
Expand Down
39 changes: 1 addition & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,44 +52,7 @@ fn main() {

Notmuch makes no claims regarding thread safety. It does not seem to use any
thread locals, but I did not spot any locks. So, as far as I am concerned, it is
not thread safe.
So why do all structs implement ```Send``` and ```Sync```? Well, it _is_ safe to
access pointers from different threads (as long as you know what you are doing :) ).
Up till now I haven't done a lot of multithreaded stuff with notmuch-rs. If you
feel this is too permissive, let me know.

## Lifetime

All structs are strictly linked together with their lifetime. The root of the
tree is ```Database```, which has a lifetime that must outlive any child
objects, for instance ```Query```. The ```Threads``` iterator that you can get
from a ```Query``` is always outlived by the parent query. The ```Threads```
does not own any individual ```Thread```. These are bound to the owner of
the ```Threads``` iterator itself. Each structure keeps a ```PhantomCow```
marker for its owner.

Typically, using a lifetimes structure like this in an application poses
significant difficulties in satisfying these lifetime requirements. While other
libraries force the application developers towards crates like ```owningref```
or ```rental``` to get around this, ```notmuch-rs``` makes use of the
excellent [Supercow](https://crates.io/crates/supercow), to alleviate this.

This way, you get to choose your own container type, and even keep the parent
object alive so you don't have to juggle lifetimes. To use this, most types
are accompagnied with an ```*Ext``` trait, that accepts ```Rc```, ```Arc``` or
comparable.

```rust
use std::sync::Arc;
use notmuch::{DatabaseExt};

let query = {
let dbr = Arc::new(db);

<Database as DatabaseExt>::create_query(dbr.clone(), &"".to_string()).unwrap()
};

```
not thread safe. Hence, all pointers are internally tracked with `Rc`s.

## Acknowledgements

Expand Down
53 changes: 27 additions & 26 deletions src/config_list.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,59 @@
use std::ops::Drop;
use std::rc::Rc;

use ffi;
use Database;
use utils::{ToStr, ScopedPhantomcow};

use ffi;
use utils::ToStr;

#[derive(Debug)]
pub struct ConfigList<'d> {
ptr: *mut ffi::notmuch_config_list_t,
marker: ScopedPhantomcow<'d, Database>,
pub struct ConfigListPtr(*mut ffi::notmuch_config_list_t);

#[derive(Clone, Debug)]
pub struct ConfigList {
ptr: Rc<ConfigListPtr>,
owner: Database,
}

impl<'d> Drop for ConfigList<'d> {
impl Drop for ConfigListPtr {
fn drop(&mut self) {
unsafe { ffi::notmuch_config_list_destroy(self.ptr) };
unsafe { ffi::notmuch_config_list_destroy(self.0) };
}
}

impl<'d> ConfigList<'d> {
pub(crate) fn from_ptr<O>(ptr: *mut ffi::notmuch_config_list_t, owner: O) -> ConfigList<'d>
where
O: Into<ScopedPhantomcow<'d, Database>>,
{
impl ConfigList {
pub(crate) fn from_ptr(
ptr: *mut ffi::notmuch_config_list_t,
owner: Database,
) -> ConfigList {
ConfigList {
ptr,
marker: owner.into(),
ptr: Rc::new(ConfigListPtr(ptr)),
owner,
}
}
}


impl<'d> Iterator for ConfigList<'d>
{
impl Iterator for ConfigList {
type Item = (String, String);

fn next(&mut self) -> Option<Self::Item> {
let valid = unsafe { ffi::notmuch_config_list_valid(self.ptr) };
let valid = unsafe { ffi::notmuch_config_list_valid(self.ptr.0) };

if valid == 0 {
return None;
}

let (k, v) = unsafe {
let key = ffi::notmuch_config_list_key(self.ptr);
let value = ffi::notmuch_config_list_value(self.ptr);
let key = ffi::notmuch_config_list_key(self.ptr.0);
let value = ffi::notmuch_config_list_value(self.ptr.0);

ffi::notmuch_config_list_move_to_next(self.ptr);
ffi::notmuch_config_list_move_to_next(self.ptr.0);

(key, value)
};

Some((k.to_string_lossy().to_string(), v.to_string_lossy().to_string()))
Some((
k.to_string_lossy().to_string(),
v.to_string_lossy().to_string(),
))
}
}

unsafe impl<'d> Send for ConfigList<'d> {}
unsafe impl<'d> Sync for ConfigList<'d> {}
Loading

0 comments on commit 96e31ba

Please sign in to comment.