4

I'd like to share an evmap, a lock-free, eventually consistent, concurrent multi-value map, across all threads in a Rust program.

Naively, it would look like this:

#[macro_use]
extern crate lazy_static;

extern crate evmap;

use std::collections::hash_map::RandomState;

lazy_static! {
    static ref MAP: (evmap::ReadHandle<u32, u32, (), RandomState>,
                     evmap::WriteHandle<u32, u32, (), RandomState>) = evmap::new();
}

fn main() {
    println!("Hello, world!");
    MAP.1.clear();
}

This gives:

error[E0277]: the trait bound `std::cell::Cell<()>: std::marker::Sync` is not satisfied in `(evmap::ReadHandle<u32, u32>, evmap::WriteHandle<u32, u32>)`
  --> src/main.rs:8:1
   |
8  | / lazy_static! {
9  | |     static ref MAP: (evmap::ReadHandle<u32, u32, (), RandomState>,
10 | |                      evmap::WriteHandle<u32, u32, (), RandomState>) = evmap::new();
11 | | }
   | |_^ `std::cell::Cell<()>` cannot be shared between threads safely
   |
   = help: within `(evmap::ReadHandle<u32, u32>, evmap::WriteHandle<u32, u32>)`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<()>`
   = note: required because it appears within the type `std::marker::PhantomData<std::cell::Cell<()>>`
   = note: required because it appears within the type `evmap::ReadHandle<u32, u32>`
   = note: required because it appears within the type `(evmap::ReadHandle<u32, u32>, evmap::WriteHandle<u32, u32>)`
   = note: required by `lazy_static::lazy::Lazy`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

I think this is complaining about the () returned inside evmap::new():

pub fn new<K, V>(
) -> (ReadHandle<K, V, (), RandomState>, WriteHandle<K, V, (), RandomState>) where
    K: Eq + Hash + Clone,
    V: Eq + ShallowCopy, 

Can it be done?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Edd Barrett
  • 3,425
  • 2
  • 29
  • 48

1 Answers1

3

Can [placing a ReadHandle / WriteHandle directly in a lazy static variable] be done?

No. As the error message states:

std::cell::Cell<()> cannot be shared between threads safely

You are attempting to place a type that will fail when used in a multithreaded context in a static variable, which must be thread-safe.

Can [placing a ReadHandle / WriteHandle in a lazy static variable at all] be done?

Yes, but you have to use something to synchronize access, such as a Mutex or RwLock:

#[macro_use]
extern crate lazy_static;

extern crate evmap;

use std::collections::hash_map::RandomState;
use std::sync::Mutex;

type ReadHandle = evmap::ReadHandle<u32, u32, (), RandomState>;
type WriteHandle = evmap::WriteHandle<u32, u32, (), RandomState>;

lazy_static! {
    static ref MAP: (Mutex<ReadHandle>, Mutex<WriteHandle>) = {
        let (r, w) = evmap::new();
        (Mutex::new(r), Mutex::new(w))
    };
}

fn main() {
    MAP.1.lock().unwrap().clear(1);
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 3
    Hello again Shepmaster! Thanks. Putting a lock around a lock-free data structure kind of defeats the point. I wonder if there is another map I can use... – Edd Barrett Dec 14 '17 at 17:28
  • @EddBarrett that's going to be tricky. I've added another "see also" link on how to "force" it if you *know* it's `Sync`, but lazy_static doesn't actually implement `DerefMut` so you can't then call mutable methods, so you'd need a `RefCell` or something equivalent. It really looks like the library intends to be used as a regular variable. It could be worth asking the maintainer if it makes sense. – Shepmaster Dec 14 '17 at 17:39
  • Dashmap is a good alternative that works inside lazy_static. – Meet Sinojia Jan 28 '21 at 07:55
  • Just want to point out to future readers: The reader is able to be used across threads by cloning (no Mutex). The writer is required to be Mutex'd anyways according to docs, so I don't think the Mutex in the lazy static is a big deal since it's already needed. – Kobato Mar 07 '22 at 23:40