waltersmuts / generic_singleton Goto Github PK
View Code? Open in Web Editor NEWA rust library crate for creating generic singletons
License: Apache License 2.0
A rust library crate for creating generic singletons
License: Apache License 2.0
The following code results in a bug from miri:
use std::{cell::RefCell, ops::AddAssign};
use num_traits::{One, Zero};
fn generic_call_counter<T: Zero + One + Copy + AddAssign + 'static>() -> T {
let mut count = generic_singleton::get_or_init!(|| RefCell::new(T::zero())).borrow_mut();
*count += T::one();
*count
}
fn main() {
let mut handles = Vec::new();
for _ in 0..2 {
handles.push(std::thread::spawn(|| {
println!("{}", generic_call_counter::<i32>())
}));
}
for handle in handles {
handle.join().unwrap();
}
}
BUG:
[walter@cuddles generic_singleton_user]$ cargo miri run
Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... done
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `/home/walter/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/generic_singleton_user`
1
error: Undefined Behavior: Data race detected between Read on thread `<unnamed>` and Write on thread `<unnamed>` at alloc3852
--> /home/walter/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:452:18
|
452 | unsafe { *self.value.get() }
| ^^^^^^^^^^^^^^^^^ Data race detected between Read on thread `<unnamed>` and Write on thread `<unnamed>` at alloc3852
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::cell::Cell::<isize>::get` at /home/walter/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:452:18
= note: inside `std::cell::BorrowRefMut::<'_>::new` at /home/walter/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:1682:15
= note: inside `std::cell::RefCell::<i32>::try_borrow_mut` at /home/walter/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:987:15
note: inside `generic_call_counter::<i32>` at src/main.rs:6:21
--> src/main.rs:6:21
|
6 | let mut count = generic_singleton::get_or_init!(|| RefCell::new(T::zero())).borrow_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside closure at src/main.rs:15:28
--> src/main.rs:15:28
|
15 | println!("{}", generic_call_counter::<i32>())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error
The bug makes sense. Not sure why generic_singleton
allows this usage :/
I was experimenting with this crate and came across behavior that I did not expect, but I am not sure, if this is a bug or if my expectation was wrong.
The way I understood it from the documentation, the first time I call the macro get_or_init
with a specific type, a singleton is created. This seems to work fine, when dealing with a single function, like in the example. But consider the following simple code example, where I attempt to have a generic registry singleton (just a hash map):
use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::sync::RwLock;
use generic_singleton::get_or_init;
fn add_to_registry<T>(key: T, value: T) -> bool
where
T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
let registry = get_or_init!(|| RwLock::new(HashMap::<T, T>::new()));
if registry.read().unwrap().contains_key(&key) {
false
} else {
registry.write().unwrap().insert(key, value);
true
}
}
fn get_from_registry<T>(key: T) -> T
where
T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
let registry = get_or_init!(|| RwLock::new(HashMap::<T, T>::new()));
if !registry.read().unwrap().contains_key(&key) {
panic!("No such key registered: {}", key)
}
*registry.read().unwrap().get(&key).unwrap()
}
fn main() {
assert!(add_to_registry::<u32>(1, 10));
assert!(!add_to_registry::<u32>(1, 20));
let val = get_from_registry::<u32>(1);
assert_eq!(val, 10);
}
The get_from_registry
call causes a panic with No such key registered: 1
.
I expected it to return the value 10
since the type involved is the exact same: The underlying data structure is a HashMap<u32, u32>
, so I thought the macro would get me the same singleton that I successfully created in the first add_to_registry
call. Apparently that is not the case.
Am I misunderstanding something? If so, how would I have to approach this, if I wanted such a generic "registry"-like singleton?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.