简体   繁体   中英

Acquiring a RwLock for read and keep it beyond the scope

I have a thread that periodically calls a callback function. Depending on the state, the callback function shall acquire an RwLock of a resource shared with other threads and keep the resource locked even beyond the scope of the callback function. It shall then again depending on the state release the resource again in a later callback cycle.

My idea was to put an Option<RwLockReadGuard<T>> into a struct which would be None when the resource is not locked and Some(RwLockReadGuard<T>) when the resource is locked.

Unfortunately, I can't make this work. I have to set up the struct which contains the Option<RwLockReadGuard<T>> outside the thread of the callback function. Even though at the time the struct is moved into the thread the Option is None , the compiler won't let me pass the option because the trait bound ``std::sync::RwLockReadGuard<'_, T>: std::marker::Send`` is not satisfied .

Maybe some code. I hope it's self-explaining enough.

use std::thread;
use std::sync::{Arc, RwLock, RwLockReadGuard};


struct Handler<'a> {
        resource: Arc<RwLock<String>>,
        locked_resource: Option<RwLockReadGuard<'a, String>>,
        counter: usize,
}

impl<'a> Handler<'a> {
        fn callback(&'a mut  self) {
                println!("Callback {}", self.counter);
                if self.counter == 0 {
                        println!("Locking resource");
                        let res = self.resource.read().unwrap();
                        self.locked_resource = Some(res);
                }

                self.counter += 1;

                if self.counter == 100 {
                        println!("Releasing resource");
                        self.locked_resource = None;
                }

                if self.counter == 200 {
                        self.counter = 0;
                }
        }
}


fn main() {
        let resource = Arc::new(RwLock::new("foo".to_string()));

        let handler = Handler {
                resource: resource.clone(),
                locked_resource: None,
                counter: 0
        };

        // This gives E0277
        let thread = thread::spawn( move || {
                loop {
                        handler.callback();
                }
        });
}

The problem is: locking and unlocking need to occur on the same thread. This is, for example, a limitation of pthread .

Fortunately, the Rust type system is expressive enough to model this: by making RwLockReadGuard be !Send , it prevents locks to be accidentally shared! All hail Rust!

So you can lock and unlock in different callback functions... but on the same thread.

In your example, this is as simple as moving the creation of handler inside the thread. In your real application, it might be a bit more complicated, but rest assured: the compiler will hold your hand along the way ;)

fn main() {
    let resource = Arc::new(RwLock::new("foo".to_string()));

    let thread = thread::spawn( move || {
        let handler = Handler {
                resource: resource,
                locked_resource: None,
                counter: 0
        };

        loop {
                handler.callback();
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM