1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#![allow(non_snake_case)]
#![allow(unused_parens)]

use std::sync::Arc;
use std::thread::{JoinHandle, sleep};
use std::time::{Duration, Instant};
use parking_lot::RwLock;
use thread_priority::ThreadBuilder;

pub type ThreadPriority = thread_priority::ThreadPriority;
pub type ThreadPriorityValue = thread_priority::ThreadPriorityValue;

pub struct SingletonThread
{
	_thread: Option<JoinHandle<()>>,
	_threadName: Option<String>,
	_threadPriority: ThreadPriority,
	_threadFunc: Arc<RwLock<Box<dyn FnMut() + Send + Sync + 'static>>>,
	_filterPrefunc: Box<dyn FnMut() -> bool + Send + Sync + 'static>,
	_durationmin: Duration,
	_loop: Arc<RwLock<bool>>,
}

impl SingletonThread
{
	/// create a new singletonThread
	pub fn new(mainFn: impl Fn() + Send + Sync + 'static) -> Self
	{
		return SingletonThread {
			_thread: None,
			_threadName: None,
			_threadPriority: ThreadPriority::Crossplatform(ThreadPriorityValue::default()),
			_threadFunc: Arc::new(RwLock::new(Box::new(mainFn))),
			_filterPrefunc: Box::new(||{true}),
			_durationmin: Duration::from_millis(17), // default 60 fps
			_loop: Arc::new(RwLock::new(false)),
		};
	}
	
	/// create a new singletonThread with a filter
	/// filter is run each time, after testing if thread is running or not
	/// filter must return true if the thread can be launched, or false if not
	pub fn newFiltered(mainFn: impl Fn() + Send + Sync + 'static, filterFn: impl Fn() -> bool + Send + Sync + 'static) -> Self
	{
		return SingletonThread {
			_thread: None,
			_threadName: None,
			_threadPriority: ThreadPriority::Crossplatform(ThreadPriorityValue::default()),
			_threadFunc: Arc::new(RwLock::new(Box::new(mainFn))),
			_filterPrefunc: Box::new(filterFn),
			_durationmin: Duration::from_millis(17), // default 60 fps
			_loop: Arc::new(RwLock::new(false)),
		};
	}
	
	/// try to launch a new run, check if not running AND the filter function return true
	/// return if the thread have been launched
	pub fn thread_launch(&mut self) -> bool
	{
		let mut canLaunch = match &self._thread {
			None => true,
			Some(threadinfo) => {
				threadinfo.is_finished()
			}
		};
		
		if (canLaunch)
		{
			let tmp = &mut self._filterPrefunc;
			canLaunch = tmp();
		}
		
		if (!canLaunch)
		{
			return false;
		}
		
		let arced = self._threadFunc.clone();
		let arcloop = self._loop.clone();
		let durationtowait = self._durationmin.clone();
		let spawnfunc = move |_|
			{
				Self::ThreadFunc(arced,arcloop,durationtowait);
			};
		let mut thread = ThreadBuilder::default();
		if let Some(name) = &self._threadName
		{
			thread = thread.name(name.to_string());
		}
		thread = thread.priority(self._threadPriority);
		self._thread = None;
		if let Ok(spawned) = thread.spawn(spawnfunc)
		{
			self._thread = Some(spawned);
		}
		
		return true;
	}
	
	/// minimum duration before another run can occur, default 166ms (60fps)
	pub fn setDuration(&mut self, duration: Duration)
	{
		self._durationmin = duration;
	}
	
	/// minimum duration before another run can occur, default 166ms (60fps)
	pub fn setDuration_FPS(&mut self,fps: u8)
	{
		let durationmillis = 1000/fps as u64;
		self._durationmin = Duration::from_millis(durationmillis);
	}
	
	/// define if the thread auto relaunch himself (technically it just use the same thread)
	/// set to false if you want to stop a looped thread that running.
	pub fn setLoop(&mut self, canLoop: bool)
	{
		*self._loop.write() = canLoop;
	}
	
	pub fn setThreadName(&mut self, name: impl Into<String>)
	{
		let name = name.into();
		self._threadName = Some(name);
	}
	
	pub fn setThreadPriority(&mut self, priority: ThreadPriority)
	{
		self._threadPriority = priority;
	}
	
	////// PRIVATE
	
	fn ThreadFunc(arced: Arc<RwLock<Box<dyn FnMut() + Send + Sync>>>, arcloop: Arc<RwLock<bool>>, durationtowait: Duration)
	{
		loop
		{
			let instant = Instant::now();
			{
				let tmp = &mut arced.write();
				tmp();
			}
			
			let timeelapsed = instant.elapsed();
			if(timeelapsed<durationtowait)
			{
				sleep(durationtowait - timeelapsed);
			}
			
			if !*arcloop.read()
			{
				break;
			}
		}
	}
}

impl Drop for SingletonThread
{
	fn drop(&mut self) {
		self.setLoop(false);
	}
}