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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// win_thread -- Windows threads, semaphores, the wake-up of the UI thread, system information
// [src/plat/win/win_thread.s]
//
// wake(): a flag coalesces bursts. With a window the wake-up is a posted WM_APP_WAKE (the
// window turns it into E_WAKE, also inside modal loops); without one (headless, before the
// window exists) an auto-reset event that wait_events waits for. Everything here is
// thread safe.
use alloc::boxed::Box;
use core::ffi::c_void;
use core::sync::atomic::{AtomicIsize, AtomicU32, Ordering};
use crate::{E_WAKE, Event};
#[link(name = "kernel32", kind = "raw-dylib")]
unsafe extern "system" {
fn CreateThread(sa: usize, stack: usize, start: extern "system" fn(*mut c_void) -> u32, param: *mut c_void,
flags: u32, id: *mut u32) -> isize;
fn WaitForSingleObject(h: isize, ms: u32) -> u32;
fn CloseHandle(h: isize) -> i32;
fn CreateSemaphoreW(sa: usize, initial: i32, max: i32, name: usize) -> isize;
fn ReleaseSemaphore(h: isize, n: i32, prev: usize) -> i32;
fn CreateEventW(sa: usize, manual: i32, initial: i32, name: usize) -> isize;
fn SetEvent(h: isize) -> i32;
fn GetProcessAffinityMask(p: isize, proc_mask: *mut usize, sys_mask: *mut usize) -> i32;
fn GetSystemInfo(si: *mut [u32; 12]);
fn GlobalMemoryStatusEx(ms: *mut [u64; 8]) -> i32;
}
#[link(name = "user32", kind = "raw-dylib")]
unsafe extern "system" {
fn PostMessageW(hwnd: isize, msg: u32, wp: usize, lp: isize) -> i32;
}
const INFINITE: u32 = 0xFFFF_FFFF;
const WAIT_TIMEOUT: u32 = 0x102;
pub(crate) const WM_APP_WAKE: u32 = 0x8001;
const THREAD_STACK_DEF: usize = 0x10_0000; // 1 MB
const THREAD_STACK_MAX: usize = 0x1000_0000; // 256 MB
// ---------------------------------------------------------------- threads
/// a thread started by thread_spawn; join it to wait for its end
pub struct Thread(isize);
// SAFETY: a thread handle may be waited for from any thread.
unsafe impl Send for Thread {}
type Start = Box<dyn FnOnce() + Send + 'static>;
extern "system" fn thread_entry(p: *mut c_void) -> u32 {
// SAFETY: p is the Box<Start> thread_spawn leaked; this thread takes it over once.
let f = unsafe { Box::from_raw(p as *mut Start) };
f();
0
}
/// run f on a new thread with stack bytes of stack (0 = 1 MB), committed up front [plat_thread_create]
pub fn thread_spawn(stack: usize, f: Start) -> Option<Thread> {
let size = if stack == 0 { THREAD_STACK_DEF } else { stack.min(THREAD_STACK_MAX) };
let size = (size + 0xFFFF) & !0xFFFF; // 64 KB granularity
let p = Box::into_raw(Box::new(f)) as *mut c_void;
// SAFETY: thread_entry takes over p; flags 0 = the stack size is committed.
let h = unsafe { CreateThread(0, size, thread_entry, p, 0, core::ptr::null_mut()) };
if h == 0 {
// SAFETY: the thread was not created, p is still ours.
drop(unsafe { Box::from_raw(p as *mut Start) });
return None;
}
Some(Thread(h))
}
impl Thread {
/// wait until the thread has ended [plat_thread_join]
pub fn join(self) {
// SAFETY: the handle is owned by this Thread.
unsafe {
WaitForSingleObject(self.0, INFINITE);
CloseHandle(self.0);
}
}
}
/// give the rest of the time slice away [plat_thread_yield]
pub fn thread_yield() {
rt::os::yield_now()
}
// ---------------------------------------------------------------- semaphores
/// a counting semaphore
pub struct Sem(isize);
// SAFETY: semaphore handles are made for use from several threads.
unsafe impl Send for Sem {}
unsafe impl Sync for Sem {}
impl Sem {
pub fn new(initial: u32) -> Option<Sem> {
// SAFETY: no pointers.
let h = unsafe { CreateSemaphoreW(0, initial as i32, 0x7FFF_FFFF, 0) };
if h == 0 { None } else { Some(Sem(h)) }
}
/// add n units (0: nothing)
pub fn post(&self, n: u32) {
if n > 0 {
// SAFETY: a valid handle.
unsafe { ReleaseSemaphore(self.0, n as i32, 0) };
}
}
/// take one unit: true taken, false timeout (ms -1 = forever, 0 = only try)
pub fn wait(&self, ms: i32) -> bool {
// SAFETY: a valid handle; -1 = INFINITE.
unsafe { WaitForSingleObject(self.0, ms as u32) == 0 }
}
}
impl Drop for Sem {
fn drop(&mut self) {
// SAFETY: the handle is owned by this Sem.
unsafe { CloseHandle(self.0) };
}
}
// ---------------------------------------------------------------- UI wake-up
static WAKE_EVENT: AtomicIsize = AtomicIsize::new(0);
static WAKE_FLAG: AtomicU32 = AtomicU32::new(0);
static WAKE_HWND: AtomicIsize = AtomicIsize::new(0);
/// the auto-reset event of headless wake-ups (process start, before any thread)
pub(crate) fn wake_init() {
// SAFETY: no pointers.
WAKE_EVENT.store(unsafe { CreateEventW(0, 0, 0, 0) }, Ordering::SeqCst);
}
/// the window that receives WM_APP_WAKE from now on (0: none, back to the event)
pub(crate) fn wake_window(hwnd: isize) {
WAKE_HWND.store(hwnd, Ordering::SeqCst);
}
pub(crate) fn wake_event() -> isize {
WAKE_EVENT.load(Ordering::SeqCst)
}
/// UI thread: the wake-up arrived -- clear the flag (the caller delivers E_WAKE)
pub(crate) fn wake_consume() -> Event {
WAKE_FLAG.store(0, Ordering::SeqCst);
Event { typ: E_WAKE, ..Default::default() }
}
/// any thread: make the UI thread's wait return with E_WAKE (coalesced) [plat_wake]
pub fn wake() {
let old = WAKE_FLAG.swap(1, Ordering::SeqCst);
let hwnd = WAKE_HWND.load(Ordering::SeqCst);
if hwnd != 0 {
if old != 0 {
return; // a wake-up is already on its way
}
// SAFETY: posting to a window handle is harmless even if it is gone.
if unsafe { PostMessageW(hwnd, WM_APP_WAKE, 0, 0) } != 0 {
return;
}
}
let ev = WAKE_EVENT.load(Ordering::SeqCst);
if ev != 0 {
// SAFETY: a valid event handle.
unsafe { SetEvent(ev) };
}
}
/// headless wait (no window): until a wake-up (E_WAKE to on_event, returns 1) or the
/// deadline in time_us() (0 = only poll, u64::MAX = forever; returns 0) [plat_wait]
pub fn wait_events(deadline_us: u64, on_event: &mut dyn FnMut(&Event)) -> u32 {
let ev = WAKE_EVENT.load(Ordering::SeqCst);
if ev == 0 {
return 0;
}
// SAFETY: a valid event handle.
if unsafe { WaitForSingleObject(ev, 0) } == 0 {
on_event(&wake_consume());
return 1;
}
if deadline_us == 0 {
return 0;
}
let ms = if deadline_us == u64::MAX {
INFINITE
} else {
let now = super::time_us();
if now >= deadline_us {
return 0;
}
((deadline_us - now + 999) / 1000).min(0xFFFF_FFFE) as u32
};
// SAFETY: a valid event handle.
let r = unsafe { WaitForSingleObject(ev, ms) };
if r == WAIT_TIMEOUT || r != 0 {
return 0;
}
on_event(&wake_consume());
1
}
// ---------------------------------------------------------------- system information
/// logical processors this process may use (>= 1) [plat_cpu_count]
pub fn cpu_count() -> u32 {
let (mut pm, mut sm) = (0usize, 0usize);
// SAFETY: -1 = this process; plain out-parameters.
let mut n = if unsafe { GetProcessAffinityMask(-1, &mut pm, &mut sm) } != 0 { pm.count_ones() } else { 0 };
if n == 0 {
let mut si = [0u32; 12];
// SAFETY: SYSTEM_INFO is 48 bytes.
unsafe { GetSystemInfo(&mut si) };
n = si[8]; // dwNumberOfProcessors
}
n.max(1)
}
/// physical memory in bytes (0 unknown) [plat_mem_total]
pub fn mem_total() -> u64 {
let mut ms = [0u64; 8];
ms[0] = 64; // dwLength
// SAFETY: MEMORYSTATUSEX is 64 bytes.
if unsafe { GlobalMemoryStatusEx(&mut ms) } == 0 { 0 } else { ms[1] }
}