1use super::{jobs, long, report};
12use crate::Session;
13use sioul_learn::Dirs;
14use sioul_learn::auto::{self, Decision, Outcome, Run};
15use std::sync::Arc;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::time::Duration;
18
19pub(crate) fn start(s: &Session, fetch: bool, threads: Option<u32>) -> Result<jobs::Job, String> {
23 let threads = threads.unwrap_or_else(auto::threads);
24 let mut args = vec!["--by-itself".to_string(), "--threads".to_string(), threads.to_string()];
25 if !fetch {
26 args.push("--no-fetch".to_string());
27 }
28 let job = jobs::create(s, "train", args)?;
29 Run { started: job.created, job: job.id.clone(), fetched: fetch, ended: None, outcome: None }.save(&Dirs::standard())?;
30 jobs::spawn(s, job)
31}
32
33pub(crate) fn run(s: &Session, dirs: &Dirs, id: &str, ask: &report::TrainAsk, json: bool) -> Result<(), String> {
38 lowest_priority();
39 let done = Arc::new(AtomicBool::new(false));
40 let watcher = {
41 let (done, id) = (Arc::clone(&done), id.to_string());
42 std::thread::Builder::new().name("spam-supply".into()).spawn(move || {
43 auto::watch_supply(
44 sioul_sync::power::supply,
45 || {
46 let _ = jobs::stop(&id);
47 },
48 || done.load(Ordering::Relaxed),
49 auto::SUPPLY_EVERY,
50 );
51 })
52 };
53 let mut replaced = None;
54 let result = long(s, json, Some(id), |progress, cancel| {
55 let report = report::train(s, dirs, ask, progress, cancel)?;
56 replaced = report.data["summary"]["replaced"].as_bool();
57 Ok(report)
58 });
59 done.store(true, Ordering::Relaxed);
60 if let Ok(watcher) = watcher {
61 let _ = watcher.join();
62 }
63 let stopped = jobs::look(id).is_ok_and(|job| job.state == jobs::State::Stopped);
64 let _ = Run::finish(dirs, id, outcome(&result, replaced, stopped), jiff::Timestamp::now().as_second());
65 result
66}
67
68pub(crate) fn outcome(result: &Result<(), String>, replaced: Option<bool>, stopped: bool) -> Outcome {
71 match (result, replaced) {
72 (Ok(()), Some(true)) => Outcome::Replaced,
73 (Ok(()), _) => Outcome::Kept,
74 (Err(_), _) if stopped => Outcome::Stopped,
75 (Err(_), _) => Outcome::Failed,
76 }
77}
78
79pub(crate) fn every_minute(s: &Session) {
83 let (path, language) = (s.config_path.clone(), s.tr.language().to_string());
84 let _ = std::thread::Builder::new().name("spam-by-itself".into()).spawn(move || {
85 loop {
86 std::thread::sleep(Duration::from_secs(60));
87 let config = sioul_core::config::Config::load(&path).unwrap_or_default();
88 sioul_core::words::set_current(sioul_core::words::Words::of(&config));
89 let session = Session { config, config_path: path.clone(), tr: sioul_core::i18n::Translator::new(&language) };
90 let (dirs, now) = (Dirs::standard(), jiff::Timestamp::now().as_second());
91 let focus = sioul_core::timelog::running().is_some();
92 let facts = auto::facts(&dirs, session.config.spam.trains_by_itself(), &sioul_sync::lease::host_name(), false, focus, now, sioul_sync::power::read);
93 if let Decision::Train { fetch } = auto::decide(&facts, now) {
94 let _ = start(&session, fetch, None);
95 }
96 }
97 });
98}
99
100fn lowest_priority() {
105 #[cfg(unix)]
106 unsafe {
109 libc::setpriority(libc::PRIO_PROCESS, 0, 19);
110 }
111 #[cfg(target_os = "linux")]
112 unsafe {
115 libc::syscall(libc::SYS_ioprio_set, 1, 0, 3 << 13);
116 }
117 #[cfg(windows)]
118 {
119 #[link(name = "kernel32")]
120 unsafe extern "system" {
121 fn GetCurrentProcess() -> isize;
122 fn SetPriorityClass(process: isize, class: u32) -> i32;
123 }
124 unsafe {
126 SetPriorityClass(GetCurrentProcess(), 0x0010_0000);
127 }
128 }
129 #[cfg(target_os = "macos")]
130 {
131 unsafe extern "C" {
132 fn setiopolicy_np(iotype: i32, scope: i32, policy: i32) -> i32;
133 }
134 unsafe {
137 setiopolicy_np(0, 0, 3);
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
148 fn how_a_run_by_itself_ended() {
149 let (ok, err) = (Ok(()), Err("x".to_string()));
150 assert_eq!(outcome(&ok, Some(true), false), Outcome::Replaced);
151 assert_eq!(outcome(&ok, Some(false), false), Outcome::Kept);
152 assert_eq!(outcome(&ok, None, false), Outcome::Kept);
153 assert_eq!(outcome(&err, None, true), Outcome::Stopped);
154 assert_eq!(outcome(&err, None, false), Outcome::Failed);
155 }
156}