Maintaining a mutable reference to struct in HashMap - rust

Is it possible to borrow a mutable reference to the contents of a HashMap and use it for an extended period of time without impeding read-only access?
This is for trying to maintain a window into the state of various components in a system that are running independently (via Tokio) and need to be monitored.
As an example:
use std::sync::Arc;
use std::collections::HashMap;
struct Container {
running : bool,
count : u8
}
impl Container {
fn run(&mut self) {
for i in 1..100 {
self.count = i;
}
self.running = false;
}
}
fn main() {
let mut map = HashMap::new();
let mut container = Arc::new(
Box::new(
Container {
running: true,
count: 0
}
)
);
map.insert(0, container.clone());
container.run();
map.remove(&0);
}
This is for a Tokio-driven program where multiple operations will be happening asynchronously and visibility into the overall state of them is required.
There's this question where a temporary mutable reference can be borrowed, but that won't work as the run() function needs time to complete.

Based on suggestions from Jmb and Stargateur reworked this to use a RwLock internally. These internals could be reworked by having methods that manipulate them, but the basics are here:
use std::sync::Arc;
use std::sync::RwLock;
use std::collections::HashMap;
#[derive(Debug)]
struct ContainerState {
running : bool,
count : u8
}
struct Container {
state : Arc<RwLock<ContainerState>>
}
impl Container {
fn run(&self) {
for i in 1..100 {
let mut state = self.state.write().unwrap();
state.count = i;
}
{
let mut state = self.state.write().unwrap();
state.running = false;
}
}
}
fn main() {
let mut map = HashMap::new();
let state = Arc::new(
RwLock::new(
ContainerState {
running: true,
count: 0
}
)
);
map.insert(0, state);
let container = Container {
state: map[&0].clone()
};
container.run();
println!("Final state: {:?}", map[&0]);
map.remove(&0);
}
Where the key thing I was missing is you can have a mutable reference or multiple immutable references, and they're mutually exclusive. My initial understanding was that these two limits were independent.

Related

Separate thread for loop in struct implementation

I'm working with a struct where I need to read the GPIO pin of a Raspberry Pi, and increment a 'register' within the struct every time the pin goes high. Concurrently with this, I would like to be able to sample the register every now and then to see what the current value is.
When implementing this, my thought was to spawn a thread that continuously loops checking if the pin has gone from Low to High, and increment the register from within the thread. Then, from the parent thread, I can read the value of the register and report it.
After doing some research, it seems that a scoped thread would not be the correct implementation of this, because the child thread would never hand over ownership of the register to the parent thread.
Rather, I believe I should use an Arc/Mutex combination guarding the register and only momentarily take control over the lock to increment the register. Is this the correct interpretation of multithreading in Rust?
Assuming the above is correct, I'm unsure of how to implement this in Rust.
struct GpioReader {
register: Arc<Mutex<i64>>,
input_pin: Arc<Mutex<InputPin>>,
}
impl GpioReader {
pub fn new(input_pin: InputPin) -> Self {
Self {
register: Arc::New(Mutex::from(0)),
input_pin: Arc::new(Mutex::from(input_pin))
}
}
pub fn start(&self) {
let pin = self.input_pin.lock().unwrap(); // ???
let register = self.register.lock().unwrap(); // ???
let handle = spawn(move || loop {
match pin.read() { // ???
High => register += 1, // ???
Low => (),
}
sleep(Duration::from_millis(SLEEP_TIME));
});
handle.join().expect("Failed to join thread.");
}
pub fn get_register(&self) -> i64 {
let reg_val = self.register.lock().unwrap();
return reg_val;
}
}
Given the above, how do I declare the pin and register variables in such a way that I can read off the pin and increment the register within the loop? My best guess is I'll have to instantiate some kind of reference to these members of the struct outside of the loop, and then pass the reference into the loop at which point I can use the lock() method of the Arc.
Edit: Using RaspberryPi 3A+ running Raspbian. The InputPin in question is from the rppal crate.
Mutex<i64> is an anti-pattern. Replace it with AtomicI64.
Arc is meant to be cloned with Arc::clone() to create new references to the same object.
Don't use shared ownership if not necessary. InputPin is only used from within the thread, so move it in instead.
I'm unsure why you do handle.join(). If you want it to continue in the background, don't wait for it with .join().
use std::{
sync::{
atomic::{AtomicI64, Ordering},
Arc,
},
thread::{self, sleep},
time::Duration,
};
use rppal::gpio::InputPin;
struct GpioReader {
register: Arc<AtomicI64>,
input_pin: Option<InputPin>,
}
const SLEEP_TIME: Duration = Duration::from_millis(1000);
impl GpioReader {
pub fn new(input_pin: InputPin) -> Self {
Self {
register: Arc::new(AtomicI64::new(0)),
input_pin: Some(input_pin),
}
}
pub fn start(&mut self) {
let register = Arc::clone(&self.register);
let pin = self.input_pin.take().expect("Thread already running!");
let handle = thread::spawn(move || loop {
match pin.read() {
High => {
register.fetch_add(1, Ordering::Relaxed);
}
Low => (),
}
sleep(SLEEP_TIME);
});
}
pub fn get_register(&self) -> i64 {
self.register.load(Ordering::Relaxed)
}
}
If you want to stop the thread automatically when the GpioReader object is dropped, you can use Weak to signal it to the thread:
use std::{
sync::{
atomic::{AtomicI64, Ordering},
Arc,
},
thread::{self, sleep},
time::Duration,
};
use rppal::gpio::InputPin;
struct GpioReader {
register: Arc<AtomicI64>,
input_pin: Option<InputPin>,
}
const SLEEP_TIME: Duration = Duration::from_millis(1000);
impl GpioReader {
pub fn new(input_pin: InputPin) -> Self {
Self {
register: Arc::new(AtomicI64::new(0)),
input_pin: Some(input_pin),
}
}
pub fn start(&mut self) {
let register = Arc::downgrade(&self.register);
let pin = self.input_pin.take().expect("Thread already running!");
let handle = thread::spawn(move || loop {
if let Some(register) = register.upgrade() {
match pin.read() {
High => {
register.fetch_add(1, Ordering::Relaxed);
}
Low => (),
}
sleep(SLEEP_TIME);
} else {
// Original `register` got dropped, cancel the thread
break;
}
});
}
pub fn get_register(&self) -> i64 {
self.register.load(Ordering::Relaxed)
}
}

Unexpected Issue when Using defer-lite in combination with a Mutable Struct

I'm quite new to Rust but I've run into a strange issue that is likely me misunderstanding how the defer-lite crate works.
If I have the below code then everything is working as expected
use defer_lite::defer;
fn main() {
println!("Start");
defer! {
println!("Stop");
}
println!("Interval");
}
I get the output that I want as
Start
Interval
Stop
Then when I try to include a mutable struct
use defer_lite::defer;
struct Timer {
start:i32, interval:i32, stop:i32
}
fn timer_initialize() -> Timer {
let timer = Timer{start:0, interval:0, stop:0 };
return timer;
}
impl Timer {
fn timer_start(&mut self) { self.start = 1; }
fn timer_interval(&mut self) { self.interval = 2; }
fn timer_stop(&mut self) { self.stop = 3; }
}
fn main() {
let mut timer = timer_initialize();
println!("Start");
timer.timer_start();
defer! {
println!("Stop");
timer.timer_stop();
}
println!("Interval");
// uncommenting this line will cause a compiler error
//timer.timer_interval();
}
Everything still works fine until I try to use the struct methods below the defer! at which point I will get a compiler error
timer.timer_interval();
^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
I'm not sure why this is and if I am using defer-lite wrong
This is a limitation of this crate. This cannot be implemented in Rust. The problem is that in order to do its work, defer-lite creates a RAII guard that holds the cleanup code as a closure. But the closure holds a mutable reference to the value, so you cannot use it again until it is dropped - at the end of the scope.
If you need that I'd recommend the scopeguard crate which is also much more popular. It provides a guard() function for this case. It works like:
fn main() {
let mut timer = timer_initialize();
println!("Start");
timer.timer_start();
let mut timer = scopeguard::guard(timer, |mut timer| {
println!("Stop");
timer.timer_stop();
});
println!("Interval");
timer.timer_interval();
}

How to idiomatically share data between closures with wasm-bindgen?

In my browser application, two closures access data stored in a Rc<RefCell<T>>. One closure mutably borrows the data, while the other immutably borrows it. The two closures are invoked independently of one another, and this will occasionally result in a BorrowError or BorrowMutError.
Here is my attempt at an MWE, though it uses a future to artificially inflate the likelihood of the error occurring:
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
#[wasm_bindgen(js_name = setTimeout)]
fn set_timeout(closure: &Closure<dyn FnMut()>, millis: u32) -> i32;
#[wasm_bindgen(js_name = setInterval)]
fn set_interval(closure: &Closure<dyn FnMut()>, millis: u32) -> i32;
}
pub struct Counter(u32);
#[wasm_bindgen(start)]
pub async fn main() -> Result<(), JsValue> {
console_error_panic_hook::set_once();
let counter = Rc::new(RefCell::new(Counter(0)));
let counter_clone = counter.clone();
let log_closure = Closure::wrap(Box::new(move || {
let c = counter_clone.borrow();
log(&c.0.to_string());
}) as Box<dyn FnMut()>);
set_interval(&log_closure, 1000);
log_closure.forget();
let counter_clone = counter.clone();
let increment_closure = Closure::wrap(Box::new(move || {
let counter_clone = counter_clone.clone();
wasm_bindgen_futures::spawn_local(async move {
let mut c = counter_clone.borrow_mut();
// In reality this future would be replaced by some other
// time-consuming operation manipulating the borrowed data
SleepFuture::new(5000).await;
c.0 += 1;
});
}) as Box<dyn FnMut()>);
set_timeout(&increment_closure, 3000);
increment_closure.forget();
Ok(())
}
struct SleepSharedState {
waker: Option<Waker>,
completed: bool,
closure: Option<Closure<dyn FnMut()>>,
}
struct SleepFuture {
shared_state: Rc<RefCell<SleepSharedState>>,
}
impl Future for SleepFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.borrow_mut();
if shared_state.completed {
Poll::Ready(())
} else {
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
impl SleepFuture {
fn new(duration: u32) -> Self {
let shared_state = Rc::new(RefCell::new(SleepSharedState {
waker: None,
completed: false,
closure: None,
}));
let state_clone = shared_state.clone();
let closure = Closure::wrap(Box::new(move || {
let mut state = state_clone.borrow_mut();
state.completed = true;
if let Some(waker) = state.waker.take() {
waker.wake();
}
}) as Box<dyn FnMut()>);
set_timeout(&closure, duration);
shared_state.borrow_mut().closure = Some(closure);
SleepFuture { shared_state }
}
}
panicked at 'already mutably borrowed: BorrowError'
The error makes sense, but how should I go about resolving it?
My current solution is to have the closures use try_borrow or try_borrow_mut, and if unsuccessful, use setTimeout for an arbitrary amount of time before attempting to borrow again.
Think about this problem independently of Rust's borrow semantics. You have a long-running operation that's updating some shared state.
How would you do it if you were using threads? You would put the shared state behind a lock. RefCell is like a lock except that you can't block on unlocking it — but you can emulate blocking by using some kind of message-passing to wake up the reader.
How would you do it if you were using pure JavaScript? You don't automatically have anything like RefCell, so either:
The state can be safely read while the operation is still ongoing (in a concurrency-not-parallelism sense): in this case, emulate that by not holding a single RefMut (result of borrow_mut()) alive across an await boundary.
The state is not safe to be read: you'd either write something lock-like as described above, or perhaps arrange so that it's only written once when the operation is done, and until then, the long-running operation has its own private state not shared with the rest of the application (so there can be no BorrowError conflicts).
Think about what your application actually needs and pick a suitable solution. Implementing any of these solutions will most likely involve having additional interior-mutable objects used for communication.

How to update in one thread and read from many?

I've failed to get this code past the borrow-checker:
use std::sync::Arc;
use std::thread::{sleep, spawn};
use std::time::Duration;
#[derive(Debug, Clone)]
struct State {
count: u64,
not_copyable: Vec<u8>,
}
fn bar(thread_num: u8, arc_state: Arc<State>) {
let state = arc_state.clone();
loop {
sleep(Duration::from_millis(1000));
println!("thread_num: {}, state.count: {}", thread_num, state.count);
}
}
fn main() -> std::io::Result<()> {
let mut state = State {
count: 0,
not_copyable: vec![],
};
let arc_state = Arc::new(state);
for i in 0..2 {
spawn(move || {
bar(i, arc_state.clone());
});
}
loop {
sleep(Duration::from_millis(300));
state.count += 1;
}
}
I'm probably trying the wrong thing.
I want one (main) thread which can update state and many threads which can read state.
How should I do this in Rust?
I have read the Rust book on shared state, but that uses mutexes which seem overly complex for a single writer / multiple reader situation.
In C I would achieve this with a generous sprinkling of _Atomic.
Atomics are indeed a proper way, there are plenty of those in std (link. Your example needs 2 fixes.
Arc must be cloned before moving into the closure, so your loop becomes:
for i in 0..2 {
let arc_state = arc_state.clone();
spawn(move || { bar(i, arc_state); });
}
Using AtomicU64 is fairly straight forward, though you need explicitly use newtype methods with specified Ordering (Playground):
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread::{sleep, spawn};
use std::time::Duration;
#[derive(Debug)]
struct State {
count: AtomicU64,
not_copyable: Vec<u8>,
}
fn bar(thread_num: u8, arc_state: Arc<State>) {
let state = arc_state.clone();
loop {
sleep(Duration::from_millis(1000));
println!(
"thread_num: {}, state.count: {}",
thread_num,
state.count.load(Ordering::Relaxed)
);
}
}
fn main() -> std::io::Result<()> {
let state = State {
count: AtomicU64::new(0),
not_copyable: vec![],
};
let arc_state = Arc::new(state);
for i in 0..2 {
let arc_state = arc_state.clone();
spawn(move || {
bar(i, arc_state);
});
}
loop {
sleep(Duration::from_millis(300));
// you can't use `state` here, because it moved
arc_state.count.fetch_add(1, Ordering::Relaxed);
}
}

Rust: concurrency error, program hangs after first thread

I have created a simplified version of my problem below, I have a Bag struct and Item struct. I want to spawn 10 threads that execute item_action method from Bag on each item in an item_list, and print a statement if both item's attributes are in the bag's attributes.
use std::sync::{Mutex,Arc};
use std::thread;
#[derive(Clone, Debug)]
struct Bag{
attributes: Arc<Mutex<Vec<usize>>>
}
impl Bag {
fn new(n: usize) -> Self {
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(0);
}
Bag{
attributes:Arc::new(Mutex::new(v)),
}
}
fn item_action(&self, item_attr1: usize, item_attr2: usize) -> Result<(),()> {
if self.attributes.lock().unwrap().contains(&item_attr1) ||
self.attributes.lock().unwrap().contains(&item_attr2) {
println!("Item attributes {} and {} are in Bag attribute list!", item_attr1, item_attr2);
Ok(())
} else {
Err(())
}
}
}
#[derive(Clone, Debug)]
struct Item{
item_attr1: usize,
item_attr2: usize,
}
impl Item{
pub fn new(item_attr1: usize, item_attr2: usize) -> Self {
Item{
item_attr1: item_attr1,
item_attr2: item_attr2
}
}
}
fn main() {
let mut item_list: Vec<Item> = Vec::new();
for i in 0..10 {
item_list.push(Item::new(i, (i+1)%10));
}
let bag: Bag= Bag::new(10); //create 10 attributes
let mut handles = Vec::with_capacity(10);
for x in 0..10 {
let bag2 = bag.clone();
let item_list2= item_list.clone();
handles.push(
thread::spawn(move || {
bag2.item_action(item_list2[x].item_attr1, item_list2[x].item_attr2);
})
)
}
for h in handles {
println!("Here");
h.join().unwrap();
}
}
When running, I only got one line, and the program just stops there without returning.
Item attributes 0 and 1 are in Bag attribute list!
May I know what went wrong? Please see code in Playground
Updated:
With suggestion from #loganfsmyth, the program can return now... but still only prints 1 line as above. I expect it to print 10 because my item_list has 10 items. Not sure if my thread logic is correct.
I have added println!("Here"); when calling join all threads. And I can see Here is printed 10 times, just not the actual log from item_action
I believe this is because Rust is not running your
if self.attributes.lock().unwrap().contains(&item_attr1) ||
self.attributes.lock().unwrap().contains(&item_attr2) {
expression in the order you expect. The evaluation order of subexpressions in Rust is currently undefined. What appears to be happening is that you essentially end up with
const condition = {
let lock1 = self.attributes.lock().unwrap();
let lock2 = self.attributes.lock().unwrap();
lock1.contains(&item_attr1) || lock2.contains(&item_attr2)
};
if condition {
which is causing your code to deadlock.
You should instead write:
let attributes = self.attributes.lock().unwrap();
if attributes.contains(&item_attr1) ||
attributes.contains(&item_attr2) {
so that there is only one lock.
Your code would also work as-is if you used an RwLock or ReentrantMutex instead of a Mutex since those allow the same thread to have multiple immutable references to the data.

Resources