Skip to content

Commit b492564

Browse files
committed
Fixed a bunch of threading issues
1 parent 4aef63d commit b492564

13 files changed

Lines changed: 745 additions & 154 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ log = "0.4"
2323
image = { version = "0.20.1", optional = true }
2424
serde = { version = "1", optional = true, features = ["serde_derive"] }
2525

26+
[dev-dependencies]
27+
env_logger = "0.5"
28+
2629
[target.'cfg(target_os = "android")'.dependencies.android_glue]
2730
version = "0.2"
2831

examples/multithreaded.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
extern crate env_logger;
2+
extern crate winit;
3+
4+
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
5+
6+
use winit::{
7+
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
8+
event_loop::{ControlFlow, EventLoop}, window::{MouseCursor, WindowBuilder},
9+
};
10+
11+
const WINDOW_COUNT: usize = 3;
12+
const WINDOW_SIZE: (u32, u32) = (600, 400);
13+
14+
fn main() {
15+
env_logger::init();
16+
let event_loop = EventLoop::new();
17+
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
18+
for _ in 0..WINDOW_COUNT {
19+
let window = WindowBuilder::new()
20+
.with_dimensions(WINDOW_SIZE.into())
21+
.build(&event_loop)
22+
.unwrap();
23+
let (tx, rx) = mpsc::channel();
24+
window_senders.insert(window.id(), tx);
25+
thread::spawn(move || {
26+
while let Ok(event) = rx.recv() {
27+
match event {
28+
WindowEvent::KeyboardInput { input: KeyboardInput {
29+
state: ElementState::Released,
30+
virtual_keycode: Some(key),
31+
modifiers,
32+
..
33+
}, .. } => {
34+
window.set_title(&format!("{:?}", key));
35+
let state = !modifiers.shift;
36+
use self::VirtualKeyCode::*;
37+
match key {
38+
A => window.set_always_on_top(state),
39+
C => window.set_cursor(match state {
40+
true => MouseCursor::Progress,
41+
false => MouseCursor::Default,
42+
}),
43+
D => window.set_decorations(!state),
44+
F => window.set_fullscreen(match state {
45+
true => Some(window.get_current_monitor()),
46+
false => None,
47+
}),
48+
G => window.grab_cursor(state).unwrap(),
49+
H => window.hide_cursor(state),
50+
I => {
51+
println!("Info:");
52+
println!("-> position : {:?}", window.get_position());
53+
println!("-> inner_position : {:?}", window.get_inner_position());
54+
println!("-> outer_size : {:?}", window.get_outer_size());
55+
println!("-> inner_size : {:?}", window.get_inner_size());
56+
},
57+
L => window.set_min_dimensions(match state {
58+
true => Some(WINDOW_SIZE.into()),
59+
false => None,
60+
}),
61+
M => window.set_maximized(state),
62+
P => window.set_position({
63+
let mut position = window.get_position().unwrap();
64+
let sign = if state { 1.0 } else { -1.0 };
65+
position.x += 10.0 * sign;
66+
position.y += 10.0 * sign;
67+
position
68+
}),
69+
R => window.set_resizable(state),
70+
S => window.set_inner_size(match state {
71+
true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100),
72+
false => WINDOW_SIZE,
73+
}.into()),
74+
W => window.set_cursor_position((
75+
WINDOW_SIZE.0 as i32 / 2,
76+
WINDOW_SIZE.1 as i32 / 2,
77+
).into()).unwrap(),
78+
Z => {
79+
window.hide();
80+
thread::sleep(Duration::from_secs(1));
81+
window.show();
82+
},
83+
_ => (),
84+
}
85+
},
86+
_ => (),
87+
}
88+
}
89+
});
90+
}
91+
event_loop.run(move |event, _event_loop, control_flow| {
92+
*control_flow = match !window_senders.is_empty() {
93+
true => ControlFlow::Wait,
94+
false => ControlFlow::Exit,
95+
};
96+
match event {
97+
Event::WindowEvent { event, window_id } => {
98+
match event {
99+
WindowEvent::CloseRequested
100+
| WindowEvent::Destroyed
101+
| WindowEvent::KeyboardInput { input: KeyboardInput {
102+
virtual_keycode: Some(VirtualKeyCode::Escape),
103+
.. }, .. } => {
104+
window_senders.remove(&window_id);
105+
},
106+
_ => if let Some(tx) = window_senders.get(&window_id) {
107+
tx.send(event).unwrap();
108+
},
109+
}
110+
}
111+
_ => (),
112+
}
113+
})
114+
}

examples/multiwindow.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ fn main() {
1515
}
1616

1717
event_loop.run(move |event, event_loop, control_flow| {
18-
*control_flow = ControlFlow::Wait;
18+
*control_flow = match !windows.is_empty() {
19+
true => ControlFlow::Wait,
20+
false => ControlFlow::Exit,
21+
};
1922
match event {
2023
Event::WindowEvent { event, window_id } => {
2124
match event {
@@ -24,11 +27,6 @@ fn main() {
2427
// This drops the window, causing it to close.
2528
windows.remove(&window_id);
2629
},
27-
WindowEvent::Destroyed => {
28-
if windows.is_empty() {
29-
*control_flow = ControlFlow::Exit;
30-
}
31-
},
3230
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode, .. }, .. } => {
3331
if Some(VirtualKeyCode::Escape) == virtual_keycode {
3432
windows.remove(&window_id);

src/platform_impl/macos/app_state.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{
2-
self, collections::VecDeque, fmt::{self, Debug, Formatter},
3-
hint::unreachable_unchecked, mem, sync::{Mutex, MutexGuard}, time::Instant,
2+
collections::VecDeque, fmt::{self, Debug, Formatter},
3+
hint::unreachable_unchecked, mem,
4+
sync::{atomic::{AtomicBool, Ordering}, Mutex, MutexGuard}, time::Instant,
45
};
56

67
use cocoa::{appkit::NSApp, base::nil};
@@ -68,6 +69,7 @@ where
6869

6970
#[derive(Default)]
7071
struct Handler {
72+
ready: AtomicBool,
7173
control_flow: Mutex<ControlFlow>,
7274
control_flow_prev: Mutex<ControlFlow>,
7375
start_time: Mutex<Option<Instant>>,
@@ -88,6 +90,18 @@ impl Handler {
8890
self.waker.lock().unwrap()
8991
}
9092

93+
fn is_ready(&self) -> bool {
94+
self.ready.load(Ordering::Acquire)
95+
}
96+
97+
fn set_ready(&self) {
98+
self.ready.store(true, Ordering::Release);
99+
}
100+
101+
fn is_control_flow_exit(&self) -> bool {
102+
*self.control_flow.lock().unwrap() == ControlFlow::Exit
103+
}
104+
91105
fn get_control_flow_and_update_prev(&self) -> ControlFlow {
92106
let control_flow = self.control_flow.lock().unwrap();
93107
*self.control_flow_prev.lock().unwrap() = *control_flow;
@@ -136,17 +150,18 @@ impl AppState {
136150
}));
137151
}
138152

139-
pub fn exit() -> ! {
153+
pub fn exit() {
140154
HANDLER.handle_nonuser_event(Event::LoopDestroyed);
141-
std::process::exit(0)
142155
}
143156

144157
pub fn launched() {
158+
HANDLER.set_ready();
145159
HANDLER.waker().start();
146160
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
147161
}
148162

149163
pub fn wakeup() {
164+
if !HANDLER.is_ready() { return }
150165
let start = HANDLER.get_start_time().unwrap();
151166
let cause = match HANDLER.get_control_flow_and_update_prev() {
152167
ControlFlow::Poll => StartCause::Poll,
@@ -173,29 +188,39 @@ impl AppState {
173188
}
174189

175190
pub fn queue_event(event: Event<Never>) {
191+
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
192+
panic!("uh-oh");
193+
}
176194
HANDLER.events().push_back(event);
177195
}
178196

179197
pub fn queue_events(mut events: VecDeque<Event<Never>>) {
198+
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
199+
panic!("uh-ohs");
200+
}
180201
HANDLER.events().append(&mut events);
181202
}
182203

183204
pub fn cleared() {
184-
HANDLER.handle_nonuser_event(Event::EventsCleared);
205+
if !HANDLER.is_ready() { return }
206+
let mut will_stop = HANDLER.is_control_flow_exit();
185207
for event in HANDLER.take_events() {
186208
HANDLER.handle_nonuser_event(event);
209+
will_stop |= HANDLER.is_control_flow_exit();
210+
}
211+
HANDLER.handle_nonuser_event(Event::EventsCleared);
212+
will_stop |= HANDLER.is_control_flow_exit();
213+
if will_stop {
214+
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
215+
return
187216
}
188217
HANDLER.update_start_time();
189218
match HANDLER.get_old_and_new_control_flow() {
190-
(ControlFlow::Poll, ControlFlow::Poll) => (),
191-
(ControlFlow::Wait, ControlFlow::Wait) => (),
192-
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant)) if old_instant == new_instant => (),
219+
(ControlFlow::Exit, _) | (_, ControlFlow::Exit) => unreachable!(),
220+
(old, new) if old == new => (),
193221
(_, ControlFlow::Wait) => HANDLER.waker().stop(),
194-
(_, ControlFlow::WaitUntil(new_instant)) => HANDLER.waker().start_at(new_instant),
222+
(_, ControlFlow::WaitUntil(instant)) => HANDLER.waker().start_at(instant),
195223
(_, ControlFlow::Poll) => HANDLER.waker().start(),
196-
(_, ControlFlow::Exit) => {
197-
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
198-
},
199224
}
200225
}
201226
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![allow(non_camel_case_types)]
2+
3+
use std::os::raw::c_void;
4+
5+
#[repr(C)]
6+
pub struct dispatch_object_s { _private: [u8; 0] }
7+
8+
pub type dispatch_function_t = extern fn(*mut c_void);
9+
pub type dispatch_queue_t = *mut dispatch_object_s;
10+
11+
pub fn dispatch_get_main_queue() -> dispatch_queue_t {
12+
unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
13+
}
14+
15+
#[link(name = "System", kind = "dylib")]
16+
extern {
17+
static _dispatch_main_q: dispatch_object_s;
18+
19+
pub fn dispatch_async_f(
20+
queue: dispatch_queue_t,
21+
context: *mut c_void,
22+
work: Option<dispatch_function_t>,
23+
);
24+
pub fn dispatch_sync_f(
25+
queue: dispatch_queue_t,
26+
context: *mut c_void,
27+
work: Option<dispatch_function_t>,
28+
);
29+
}

src/platform_impl/macos/event_loop.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
collections::VecDeque, marker::PhantomData,
2+
collections::VecDeque, marker::PhantomData, process,
33
};
44

55
use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool};
@@ -78,7 +78,8 @@ impl<T> EventLoop<T> {
7878
assert_ne!(app, nil);
7979
AppState::set_callback(callback, self.window_target);
8080
let _: () = msg_send![app, run];
81-
AppState::exit()
81+
AppState::exit();
82+
process::exit(0)
8283
}
8384
}
8485

src/platform_impl/macos/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
9595
pub const kCGCursorWindowLevelKey: NSInteger = 19;
9696
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
9797

98+
#[derive(Debug, Clone, Copy)]
9899
pub enum NSWindowLevel {
99100
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
100101
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,

src/platform_impl/macos/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod app;
44
mod app_delegate;
55
mod app_state;
6+
mod dispatch;
67
mod event;
78
mod event_loop;
89
mod ffi;
@@ -39,6 +40,9 @@ pub struct Window {
3940
_delegate: WindowDelegate,
4041
}
4142

43+
unsafe impl Send for Window {}
44+
unsafe impl Sync for Window {}
45+
4246
impl Deref for Window {
4347
type Target = UnownedWindow;
4448
#[inline]

src/platform_impl/macos/observer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ impl EventLoopWaker {
251251
unsafe {
252252
let current = CFAbsoluteTimeGetCurrent();
253253
let duration = instant - now;
254-
let fsecs =
255-
duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64;
254+
let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0
255+
+ duration.as_secs() as f64;
256256
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
257257
}
258258
}

0 commit comments

Comments
 (0)