Skip to main content

Scheduler

Struct Scheduler 

Source
pub struct Scheduler {
Show 17 fields pub settings: Arc<Mutex<Settings>>, pub pause_state: Arc<Mutex<PauseState>>, pub camera_active: Arc<AtomicBool>, pub video_active: Arc<AtomicBool>, pub auto_suppress_reason: Arc<AtomicU8>, pub config_path: PathBuf, pub pause_path: PathBuf, pub events_path: PathBuf, pub screen_time_path: PathBuf, pub timers: Arc<Mutex<BreakTimers>>, pub stats: Arc<Mutex<BreakStats>>, pub screen_time: Arc<Mutex<ScreenTimeState>>, pub current_break: Arc<Mutex<Option<BreakEvent>>>, pub logger: Logger, pub profiles: Arc<Mutex<Vec<Profile>>>, pub active_profile_name: Arc<Mutex<String>>, pub hook_dialog_busy: Arc<AtomicBool>,
}
Expand description

Live, mutable state for the break scheduler.

Constructed once in lib::run and shared across the app via tauri::State and Arc-cloning. Every mutable field sits behind a tokio::Mutex (or a std::sync::Mutex for the renderer-bound current_break slot, which only needs short critical sections). Clone is cheap — it bumps the inner Arcs.

The persisted paths (config_path, pause_path, etc.) are captured at construction so the scheduler can write them back without re-resolving Tauri’s app_data_dir each tick.

§Locking convention: no nested async mutexes across .await

Every call site in this module releases a tokio::Mutex guard before acquiring the next one across an .await point. The pattern is “snapshot then act”:

let s = sched.settings.lock().await.clone();      // release before next lock
let name = sched.active_profile_name.lock().await.clone();
let mut profiles = sched.profiles.lock().await;   // safe — others released

Following this rule, deadlock becomes structurally impossible — the classic “thread A holds X waiting for Y, thread B holds Y waiting for X” cycle cannot form if guards never overlap on .await.

What this rules out:

  • let s = sched.settings.lock().await; let p = sched.profiles.lock().await; (holding settings across the profiles acquisition)
  • let g = sched.timers.lock().await; some_async_fn(&sched).await; (holding any guard across a call that may itself lock the same scheduler)

What it allows:

  • Re-acquiring the same lock back-to-back to mutate after an awaited side-effect (write to disk, emit event). Each scope drops first.
  • The std current_break mutex, which is only ever taken inside short non-async blocks (see overlay::fire_break).
  • Short synchronous emits (app.emit("evt", &single_field)) that borrow a guard expression in the argument list and drop it at the end of the statement — the emit itself does not .await and yields no scheduler lock.
  • Reading two unrelated single-field snapshots back-to-back inside one command (see get_postpone_state): clone the first, drop, then acquire the second. Brief observational skew is fine for renderer queries that never make causal decisions across the pair.

If a new code path genuinely needs nested holds — say, an atomic read-modify-write across two pieces of state — consolidate them into one struct under one mutex instead of introducing the nesting.

Fields§

§settings: Arc<Mutex<Settings>>§pause_state: Arc<Mutex<PauseState>>§camera_active: Arc<AtomicBool>§video_active: Arc<AtomicBool>§auto_suppress_reason: Arc<AtomicU8>

0 = not auto-suppressed; otherwise SuppressReason::from_u8 decodes which guard fired. The tray reads this each tick to pick between the Inactive icon + reason tooltip vs the Normal icon. Atomic instead of a mutex so the per-tick read is free.

§config_path: PathBuf§pause_path: PathBuf§events_path: PathBuf§screen_time_path: PathBuf§timers: Arc<Mutex<BreakTimers>>§stats: Arc<Mutex<BreakStats>>§screen_time: Arc<Mutex<ScreenTimeState>>§current_break: Arc<Mutex<Option<BreakEvent>>>§logger: Logger§profiles: Arc<Mutex<Vec<Profile>>>§active_profile_name: Arc<Mutex<String>>§hook_dialog_busy: Arc<AtomicBool>

Implementations§

Source§

impl Scheduler

Source

pub async fn tray_countdown_snapshot(&self) -> (TrayCountdownSnapshot, bool)

Snapshot the per-tick state the tray ticker needs. Polled once per second on macOS/Linux. See TrayCountdownSnapshot for the precedence rules.

Returns (snapshot, text_enabled). text_enabled mirrors the user’s tray_countdown_enabled setting — the ticker uses it to gate the always-visible title text (icon + tooltip aren’t gated, since the icon is the visual signal and the tooltip is hover-only opt-in).

Source§

impl Scheduler

Source

pub fn new( config_path: PathBuf, pause_path: PathBuf, events_path: PathBuf, screen_time_path: PathBuf, ) -> Self

Load persisted state from disk and spawn the camera / video monitor threads. Does not start the main scheduler loop — call spawn for that, after app.manage-ing the result.

Source

pub fn spawn(&self, app: AppHandle)

Launch the 1Hz scheduler loop on the Tauri async runtime. Safe to call exactly once per Scheduler instance.

Source

pub async fn snapshot_profiles_file(&self) -> ProfilesFile

Build the on-disk shape ({ profiles, active }) by snapshotting the in-memory profile list. Used by persist_profiles.

Trait Implementations§

Source§

impl Clone for Scheduler

Source§

fn clone(&self) -> Scheduler

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more