0

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");
                },
            );
        }
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
chris
  • 4,840
  • 5
  • 35
  • 66
  • As a sidenote: In rust 1.34.0 you can use `ATOMIC_{U,I}64` https://github.com/rust-lang/rust/pull/57425. If you are on nightly you can use them right now :) – hellow Jan 23 '19 at 08:00
  • Also I'm not sure, what your concern is. Your code works fine. Can you please either expand your question or code to clarify what is not working and what you want to achieve? – hellow Jan 23 '19 at 08:04
  • it doesn't work because I don't know how to use the guard as a struct member. without the guard the timer callback is never called – chris Jan 23 '19 at 08:07
  • 3
    Possible duplicate of [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct) – Jmb Jan 23 '19 at 08:26
  • As @Jmb has pointed out, the problem you are facing is that you can't store an object and a reference to that object in the same structure. It's difficult to make suggestions to fix your code though, because it's really not clear what you are trying to do (except this one impossible thing). Do you need to construct the guard inside `new`? That would imply that you want to start the timer running - if the timer is not always running, what value do you expect the guard to have when it isn't? – Peter Hall Jan 23 '19 at 16:12
  • Is `tick` the callback for the timer? – Peter Hall Jan 23 '19 at 16:17
  • @PeterHall tick starts the timer, the guard is basically just the reference to the object, and when its dropped the callback doesn't run either. I see the problem though, I guess this doesn't work in rust – chris Jan 24 '19 at 07:02
  • In that case, just for starters `guard` needs to be `Option`, so it can be `None` before you call `tick`. – Peter Hall Jan 24 '19 at 09:16

1 Answers1

1

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;
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204