I am trying to implement a struct that keeps track of a global tick. In an effort to refactor I moved the timer into the struct but now I am facing the issue of the timer guard losing reference and thus the timer being dropped. My thought was to add the guard as struct member but I am not sure how to do this.
use timer;
use chrono;
use futures::Future;
use std::{process, thread};
use std::sync::{Arc, Mutex};
struct GlobalTime {
tick_count: Arc<Mutex<u64>>,
millis: Arc<Mutex<i64>>,
timer: timer::Timer,
guard: timer::Guard,
}
impl GlobalTime {
fn new() -> GlobalTime {
GlobalTime {
tick_count: Arc::new(Mutex::new(0)),
millis: Arc::new(Mutex::new(200)),
timer: timer::Timer::new(),
guard: ???, // what do I do here to init the guard??
}
}
fn tick(&self) {
*self.guard = {
let global_tick = self.tick_count.clone();
self.timer.schedule_repeating(
chrono::Duration::milliseconds(*self.millis.lock().unwrap()),
move || {
*global_tick.lock().unwrap() += 1;
println!("timer callback");
},
);
}
}
}
Given that the timer is not always running for the lifetime of GlobalTime, there isn't always a valid value for guard. We usually model that idea with an Option:
struct GlobalTime {
tick_count: Arc<Mutex<u64>>,
millis: Arc<Mutex<i64>>,
timer: timer::Timer,
guard: Option<timer::Guard>,
}
Which also solves your problem of what the initial value is, because it's Option::None:
impl GlobalTime {
fn new() -> GlobalTime {
GlobalTime {
tick_count: Arc::new(Mutex::new(0)),
millis: Arc::new(Mutex::new(200)),
timer: timer::Timer::new(),
guard: None,
}
}
}
The tick method becomes:
fn tick(&mut self) {
let global_tick = self.tick_count.clone();
let guard = self.timer.schedule_repeating(
chrono::Duration::milliseconds(*self.millis.lock().unwrap()),
move || {
*global_tick.lock().unwrap() += 1;
println!("timer callback");
},
);
self.guard = Some(guard);
}
To stop the timer you can just set the guard value to Option::None:
fn stop(&mut self) {
self.guard = None;
}
Related
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)
}
}
I am attempting to create a simple event loop with Mio. Every event invokes an associated callback depending on the Token of the event. The event loop runs on a separate thread to the rest of the code.
Events can be registered via the register function, however in order to register the event it must add it to the same HashMap of callbacks (and access the same Mio Poll struct) that the event loop is iterating.
The issue is that callbacks themselves may register events, in this case it is impossible to take the mutex as the event loop has the mutex. Is it possible to somehow drop the Mutex in the start() function whilst the callback is being invoked? Although, this does not seem performant.
Is there a better way to handle this in Rust?
use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use mio::{event, Events, Interest, Poll, Token};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use std::{io, thread};
pub trait HookCb: Send {
fn call(&self, event: &Event);
}
impl<F: Send> HookCb for F
where
F: Fn(&Event),
{
fn call(&self, event: &Event) {
self(event)
}
}
struct EventLoopInner {
handlers: HashMap<Token, Box<dyn HookCb>>,
poll: Poll,
}
pub struct EventLoop {
inner: Mutex<EventLoopInner>,
}
impl EventLoop {
pub fn new() -> io::Result<Self> {
Ok(Self {
inner: Mutex::new(EventLoopInner {
handlers: HashMap::new(),
poll: Poll::new()?,
}),
})
}
pub fn start(&self) -> io::Result<()> {
let mut events = Events::with_capacity(1024);
let inner = &mut *self.inner.lock().unwrap(); // Inner mutex taken
loop {
inner.poll.poll(&mut events, None)?;
for event in events.iter() {
match event.token() {
Token(v) => {
if let Some(cb) = inner.handlers.get(&Token(v)) {
// TODO release the inner mutex before here so that the callback can invoke register
cb.call(event)
}
}
}
}
}
}
pub fn register<S>(
&self,
source: &mut S,
interest: Interest,
cb: impl HookCb + 'static,
) -> io::Result<Token>
where
S: event::Source + std::marker::Send,
{
let mut inner = self.inner.lock().unwrap(); // Cannot get this lock after start() has been invoked
let token = Token(inner.handlers.len());
inner.poll.registry().register(source, token, interest)?;
inner.handlers.insert(token, Box::new(cb));
Ok(token)
}
}
struct ServerConn {
listener: Option<TcpListener>,
connections: HashMap<Token, TcpStream>,
}
struct Server {
eventloop: Arc<EventLoop>,
thread: Option<JoinHandle<()>>,
conn: Arc<Mutex<ServerConn>>,
}
impl Server {
pub fn new() -> Self {
Self {
eventloop: Arc::new(EventLoop::new().unwrap()),
thread: None,
conn: Arc::new(Mutex::new(ServerConn {
listener: None,
connections: HashMap::new(),
})),
}
}
pub fn listen(&mut self, addr: &str) {
{
let mut conn = self.conn.lock().unwrap();
conn.listener = Some(TcpListener::bind(addr.parse().unwrap()).unwrap());
let cb_conn = Arc::clone(&self.conn);
let cb_eventloop = Arc::clone(&self.eventloop);
self.eventloop
.register(
conn.listener.as_mut().unwrap(),
Interest::READABLE,
move |e: &Event| {
Self::accept_cb(e, &cb_conn, &cb_eventloop);
},
)
.unwrap();
} // Unlock conn
let t_eventloop = Arc::clone(&self.eventloop);
self.thread = Some(thread::spawn(move || {
t_eventloop.start().unwrap();
}));
self.thread.take().unwrap().join().unwrap(); // Temp fix to block main thread so application does not exit
}
fn accept_cb(_e: &Event, conn: &Arc<Mutex<ServerConn>>, evloop: &Arc<EventLoop>) {
let mut conn_lock = conn.lock().unwrap();
loop {
let (mut stream, addr) = match conn_lock.listener.as_ref().unwrap().accept() {
Ok((stream, addr)) => (stream, addr),
Err(_) => return,
};
println!("Accepted connection from: {}", addr);
// TODO can this clone be avoided?
let cb_conn = Arc::clone(conn);
let cb_evloop = Arc::clone(evloop);
let token = evloop
.register(
&mut stream,
Interest::READABLE.add(Interest::WRITABLE),
move |e: &Event| {
Self::conn_cb(e, &cb_conn, &cb_evloop);
},
)
.unwrap();
conn_lock.connections.insert(token, stream);
}
}
pub fn conn_cb(e: &Event, conn: &Arc<Mutex<ServerConn>>, _evloop: &Arc<EventLoop>) {
let conn_lock = conn.lock().unwrap();
let mut _connection = conn_lock.connections.get(&e.token()).unwrap();
if e.is_writable() {
// TODO write logic -- connection.write(b"Hello World!\n").unwrap();
// TODO evloop.reregister(&mut connection, event.token(), Interest::READABLE)?
}
if e.is_readable() {
// TODO read logic -- connection.read(&mut received_data);
}
}
}
fn main() {
let mut s = Server::new();
s.listen("127.0.0.1:8000");
}
I am teaching myself Rust by creating a toy SDL2 lib for myself.
I created something similar in Go and am trying to port my code across. So far, this is the problem I cannot overcome. I want my library to have callback to a function on the program state so I can have keyboard events sent from my library code to my client code.
The aim is for the keydown events from the SDL keyboard event pump should trigger the on_keydown function on the state object. If I remove the State struct and just use static functions then it works. Of course this prevents me from changing the state of the program based on keyboard actions.
I am trying to use external crates as little as possible.
The relevant parts of the library.
pub enum GameCommand {
Quit,
Continue,
}
pub struct Window {
keydown_event: fn(Event) -> GameCommand,
}
impl Window {
pub fn set_keydown_event(&mut self, f: fn(e: Event) -> GameCommand) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
let mut event_pump = self.game.context.event_pump()?;
'running: loop {
// Handle events
for event in event_pump.poll_iter() {
let mut gc = GameCommand::Continue;
match event {
Event::Quit { .. } => break 'running,
Event::KeyDown { repeat: false, .. } => {
gc = (self.keydown_event)(event);
}
_ => {}
}
if let GameCommand::Quit = gc {
break 'running
}
}
}
Ok(())
}
}
Now the relevant part of the client bin.
struct State {
bgcolor: Color,
}
impl State {
fn on_keydown(&mut self, event: Event) -> GameCommand {
match event {
Event::KeyDown { keycode: Some(Keycode::R), .. } => {
self.bgcolor.r += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::G), .. } => {
self.bgcolor.g += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::B), .. } => {
self.bgcolor.b += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::Escape), ..} => {
GameCommand::Quit
},
_ => GameCommand::Continue,
}
}
}
Now the main function.
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
window.set_keydown_event(state.on_keydown);
Ok(())
}
There is a far bit of code skipped to keep it shortish. The error I get with this code is.
{
"code": "E0615",
"message": "attempted to take value of method `on_keydown` on type `State`\n\nmethod, not a field\n\nhelp: use parentheses to call the method: `(_)`",
}
If I window.set_keydown_event(state.on_keydown); I get this error.
{
"code": "E0308",
"message": "mismatched types\n\nexpected fn pointer, found enum `sdlgame::GameCommand`\n\nnote: expected fn pointer `fn(sdl2::event::Event) -> sdlgame::GameCommand`\n found enum `sdlgame::GameCommand`",
}
I assume the problem is the difference in function signatures. In the set_keydown_event function it expects.
fn(Event) -> GameCommand
Which is why a plain function not associated with a struct works. For the instance method to mutate state it requires the signature.
fn on_keydown(&mut self, event: Event) -> GameCommand
Initially, I am trying to achieve this is a single threaded manner as I am trying to keep things simple for me to reason out. Multi-threading will come later.
Is this possible in Rust and what is the correct way of achieving this result?
Thanks in advance.
Basically, you need to use function traits as well as an explicit closure so the call is bound to the variable. So, you'd change your Window to use a function trait:
// F is now the function type
pub struct Window<F: FnMut(Event) -> GameCommand> {
keydown_event: F,
}
Then you'd change your impl to support that function trait:
// put generic in impl
impl<F: FnMut(Event) -> GameCommand> Window<F> {
// take F as the parameter type now
pub fn set_keydown_event(&mut self, f: F) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
// this function should stay the same
}
}
Then, you'd pass an explicit closure to it:
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
// binds the on_keydown function to the state variable
window.set_keydown_event(|x| state.on_keydown(x));
Ok(())
}
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);
}
}
struct ThreadHolder{
state: ???
thread: ???
}
impl ThreadHolder {
fn launch(&mut self) {
self.thread = ???
// in thread change self.state
}
}
#[test]
fn test() {
let mut th = ThreadHolder{...};
th.launch();
// thread will be destroy as soon as th go out of scope
}
I think there is something to deal with lifetime, but I don't know how to write it.
What you want is so simple that you don't even need it to be mutable in any way, and then it becomes trivial to share it across threads, unless you want to reset it. You said you need to leave a thread, for one reason or another, therefore I'll assume that you don't care about this.
You instead can poll it every tick (most games run in ticks so I don't think there will be any issue implementing that).
I will provide example that uses sleep, so it's not most accurate thing, it is painfully obvious on the last subsecond duration, but I am not trying to do your work for you anyway, there's enough resources on internet that can help you deal with it.
Here it goes:
use std::{
sync::Arc,
thread::{self, Result},
time::{Duration, Instant},
};
struct Timer {
end: Instant,
}
impl Timer {
fn new(duration: Duration) -> Self {
// this code is valid for now, but might break in the future
// future so distant, that you really don't need to care unless
// you let your players draw for eternity
let end = Instant::now().checked_add(duration).unwrap();
Timer { end }
}
fn left(&self) -> Duration {
self.end.saturating_duration_since(Instant::now())
}
// more usable than above with fractional value being accounted for
fn secs_left(&self) -> u64 {
let span = self.left();
span.as_secs() + if span.subsec_millis() > 0 { 1 } else { 0 }
}
}
fn main() -> Result<()> {
let timer = Timer::new(Duration::from_secs(10));
let timer_main = Arc::new(timer);
let timer = timer_main.clone();
let t = thread::spawn(move || loop {
let seconds_left = timer.secs_left();
println!("[Worker] Seconds left: {}", seconds_left);
if seconds_left == 0 {
break;
}
thread::sleep(Duration::from_secs(1));
});
loop {
let seconds_left = timer_main.secs_left();
println!("[Main] Seconds left: {}", seconds_left);
if seconds_left == 5 {
println!("[Main] 5 seconds left, waiting for worker thread to finish work.");
break;
}
thread::sleep(Duration::from_secs(1));
}
t.join()?;
println!("[Main] worker thread finished work, shutting down!");
Ok(())
}
By the way, this kind of implementation wouldn't be any different in any other language, so please don't blame Rust for it. It's not the easiest language, but it provides more than enough tools to build anything you want from scratch as long as you put effort into it.
Goodluck :)
I think I got it work
use std::sync::{Arc, Mutex};
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;
struct Timer {
pub(crate) time: Arc<Mutex<u32>>,
jh_ticker: Option<JoinHandle<()>>,
}
impl Timer {
fn new<T>(i: T, duration: Duration) -> Self
where
T: Iterator<Item = u32> + Send + 'static,
{
let time = Arc::new(Mutex::new(0));
let arc_time = time.clone();
let jh_ticker = Some(spawn(move || {
for item in i {
let mut mg = arc_time.lock().unwrap();
*mg = item;
drop(mg); // needed, otherwise this thread will always hold lock
sleep(duration);
}
}));
Timer { time, jh_ticker }
}
}
impl Drop for Timer {
fn drop(&mut self) {
self.jh_ticker.take().unwrap().join();
}
}
#[test]
fn test_timer() {
let t = Timer::new(0..=10, Duration::from_secs(1));
let a = t.time.clone();
for _ in 0..100 {
let b = *a.lock().unwrap();
println!("{}", b);
sleep(Duration::from_millis(100));
}
}