singletonThread/
lib.rs

1#![allow(non_snake_case)]
2#![allow(unused_parens)]
3
4mod thread;
5mod r#loop;
6
7#[cfg(feature = "thread-priority")]
8extern crate thread_priority;
9
10use std::sync::Arc;
11use std::thread::{sleep, JoinHandle};
12use std::time::{Duration, Instant};
13use parking_lot::Mutex;
14#[cfg(feature = "thread-priority")]
15use thread_priority::{ThreadPriority, ThreadPriorityValue};
16use crate::r#loop::Loop;
17use crate::thread::Thread;
18
19pub struct SingletonThread
20{
21	_thread: Option<JoinHandle<()>>,
22	_threadName: Option<String>,
23	#[cfg(feature = "thread-priority")]
24	_threadPriority: ThreadPriority,
25	_threadFunc: Arc<Mutex<Box<dyn FnMut() + Send + Sync + 'static>>>,
26	_filterPrefunc: Box<dyn FnMut() -> bool + Send + Sync + 'static>,
27	_durationmin: Arc<Mutex<Duration>>,
28	_loop: Arc<Mutex<Loop>>,
29}
30
31impl SingletonThread
32{
33	/// create a new singletonThread
34	pub fn new(mainFn: impl Fn() + Send + Sync + 'static) -> Self
35	{
36		return Self::newFiltered(mainFn,||{true});
37	}
38	
39	/// create a new singletonThread with a filter.
40	/// filter is run each time, after testing if the thread is running or not,
41	/// filter must return true if the thread can be launched, or false if not
42	pub fn newFiltered(mainFn: impl Fn() + Send + Sync + 'static, filterFn: impl Fn() -> bool + Send + Sync + 'static) -> Self
43	{
44		return SingletonThread {
45			_thread: None,
46			_threadName: None,
47			#[cfg(feature = "thread-priority")]
48			_threadPriority: ThreadPriority::Crossplatform(ThreadPriorityValue::default()),
49			_threadFunc: Arc::new(Mutex::new(Box::new(mainFn))),
50			_filterPrefunc: Box::new(filterFn),
51			_durationmin: Arc::new(Mutex::new(Duration::from_nanos(1))),
52			_loop: Arc::new(Mutex::new(Loop::NO)),
53		};
54	}
55	
56	/// try to launch a new run, check if not running AND the filter function return true
57	/// return if the thread have been launched
58	pub fn thread_launch(&mut self) -> bool
59	{
60		return self.internal_thread_launch(false);
61	}
62	
63	/// try to launch a new run, check if not running, AND the filter function returns true
64	/// if it cannot be launched immediately, mark the class to run another time after the current one. can only mark one time.
65	pub fn thread_launch_delayabe(&mut self)
66	{
67		self.internal_thread_launch(true);
68	}
69	
70	/// minimum duration before another run can occur, default 1ns
71	pub fn duration_set(&mut self, duration: Duration)
72	{
73		*self._durationmin.lock() = duration;
74	}
75	
76	/// minimum duration before another run can occur from frame per second (60 fps = 16.666 ms), minimum 1 fps (1 ms)
77	pub fn duration_setFPS(&mut self, fps: u8)
78	{
79		let durationmillis = 1000/fps.max(1) as u64;
80		*self._durationmin.lock() = Duration::from_millis(durationmillis);
81	}
82	
83	/// define if the thread auto relaunch himself (technically it just use the same thread)
84	/// set to false if you want to stop a looped thread that running.
85	pub fn loop_set(&mut self, canLoop: bool)
86	{
87		*self._loop.lock() = canLoop.into();
88	}
89
90	/// set the name of the thread.
91	/// the name is updated only if the thread launch is completely relaunched (no change when a loop ends)
92	pub fn thread_setName(&mut self, name: impl Into<String>)
93	{
94		self._threadName = Some(name.into());
95	}
96
97	/// get the name of the thread
98	pub fn thread_getName(&mut self) -> &Option<String>
99	{
100		return &self._threadName;
101	}
102
103	/// set the priority of the thread
104	#[cfg(feature = "thread-priority")]
105	pub fn thread_setPriority(&mut self, priority: ThreadPriority)
106	{
107		self._threadPriority = priority;
108	}
109
110	/// set the thread to stop looping
111	/// wait the thread is stopped
112	pub fn wait(&mut self) -> std::thread::Result<()> {
113		self.loop_set(false);
114
115		if let Some(threadinfo) = self._thread.take() {
116			return threadinfo.join().map(|_| ());
117		}
118
119		return Ok(());
120	}
121	
122	////// PRIVATE
123	
124	
125	fn internal_thread_launch(&mut self,delayabe: bool) -> bool
126	{
127		let mut canLaunch = match &self._thread {
128			None => true,
129			Some(threadinfo) => {
130				threadinfo.is_finished()
131			}
132		};
133		
134		if (canLaunch)
135		{
136			let tmp = &mut self._filterPrefunc;
137			canLaunch = tmp();
138		}
139		
140		if (!canLaunch)
141		{
142			if(delayabe)
143			{
144				let mut tmp = self._loop.lock();
145				match *tmp
146				{
147					Loop::NO => {*tmp = Loop::ONE}
148					_ => {}
149				}
150			}
151			return false;
152		}
153		
154		let arced = self._threadFunc.clone();
155		let arcloop = self._loop.clone();
156		let durationtowait = self._durationmin.clone();
157		let spawnfunc = move ||
158			{
159				Self::InternalThreadFunc(arced, arcloop, durationtowait);
160			};
161
162		let mut innerthread = Thread::default();
163		innerthread.name_set(self._threadName.clone());
164		#[cfg(feature = "thread-priority")]
165		{
166			innerthread.priority_set(self._threadPriority);
167		}
168		if let Ok(spawned) = innerthread.build(spawnfunc)
169		{
170			self._thread = Some(spawned);
171			return true;
172		}
173		
174		return false;
175	}
176	
177	fn InternalThreadFunc(arced: Arc<Mutex<Box<dyn FnMut() + Send + Sync>>>, arcloop: Arc<Mutex<Loop>>, durationtowait: Arc<Mutex<Duration>>)
178	{
179		loop
180		{
181			let instant = Instant::now();
182			{
183				let tmp = &mut arced.lock();
184				tmp();
185			}
186			
187			let timeelapsed = instant.elapsed();
188			let durationtowait = durationtowait.lock().clone();
189			if(timeelapsed<durationtowait)
190			{
191				sleep(durationtowait - timeelapsed);
192			}
193			
194			let mut tmp = arcloop.lock();
195			match *tmp
196			{
197				Loop::NO => {return;}
198				Loop::ONE => {
199					*tmp=Loop::NO;
200				}
201				Loop::YES => {}
202			}
203		}
204	}
205}
206
207#[cfg(feature = "drop")]
208/// when dropped, the thread is stopped and waited for completion
209/// only if the feature "drop" is enabled
210impl Drop for SingletonThread
211{
212	fn drop(&mut self) {
213		let _ = self.wait();
214	}
215}