Skip to content

Commit

Permalink
Fixed a bunch of threading issues
Browse files Browse the repository at this point in the history
  • Loading branch information
francesca64 committed Dec 19, 2018
1 parent 4aef63d commit b492564
Show file tree
Hide file tree
Showing 13 changed files with 745 additions and 154 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ log = "0.4"
image = { version = "0.20.1", optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] }

[dev-dependencies]
env_logger = "0.5"

[target.'cfg(target_os = "android")'.dependencies.android_glue]
version = "0.2"

Expand Down
114 changes: 114 additions & 0 deletions examples/multithreaded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
extern crate env_logger;
extern crate winit;

use std::{collections::HashMap, sync::mpsc, thread, time::Duration};

use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop}, window::{MouseCursor, WindowBuilder},
};

const WINDOW_COUNT: usize = 3;
const WINDOW_SIZE: (u32, u32) = (600, 400);

fn main() {
env_logger::init();
let event_loop = EventLoop::new();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT {
let window = WindowBuilder::new()
.with_dimensions(WINDOW_SIZE.into())
.build(&event_loop)
.unwrap();
let (tx, rx) = mpsc::channel();
window_senders.insert(window.id(), tx);
thread::spawn(move || {
while let Ok(event) = rx.recv() {
match event {
WindowEvent::KeyboardInput { input: KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
modifiers,
..
}, .. } => {
window.set_title(&format!("{:?}", key));
let state = !modifiers.shift;
use self::VirtualKeyCode::*;
match key {
A => window.set_always_on_top(state),
C => window.set_cursor(match state {
true => MouseCursor::Progress,
false => MouseCursor::Default,
}),
D => window.set_decorations(!state),
F => window.set_fullscreen(match state {
true => Some(window.get_current_monitor()),
false => None,
}),
G => window.grab_cursor(state).unwrap(),
H => window.hide_cursor(state),
I => {
println!("Info:");
println!("-> position : {:?}", window.get_position());
println!("-> inner_position : {:?}", window.get_inner_position());
println!("-> outer_size : {:?}", window.get_outer_size());
println!("-> inner_size : {:?}", window.get_inner_size());
},
L => window.set_min_dimensions(match state {
true => Some(WINDOW_SIZE.into()),
false => None,
}),
M => window.set_maximized(state),
P => window.set_position({
let mut position = window.get_position().unwrap();
let sign = if state { 1.0 } else { -1.0 };
position.x += 10.0 * sign;
position.y += 10.0 * sign;
position
}),
R => window.set_resizable(state),
S => window.set_inner_size(match state {
true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100),
false => WINDOW_SIZE,
}.into()),
W => window.set_cursor_position((
WINDOW_SIZE.0 as i32 / 2,
WINDOW_SIZE.1 as i32 / 2,
).into()).unwrap(),
Z => {
window.hide();
thread::sleep(Duration::from_secs(1));
window.show();
},
_ => (),
}
},
_ => (),
}
}
});
}
event_loop.run(move |event, _event_loop, control_flow| {
*control_flow = match !window_senders.is_empty() {
true => ControlFlow::Wait,
false => ControlFlow::Exit,
};
match event {
Event::WindowEvent { event, window_id } => {
match event {
WindowEvent::CloseRequested
| WindowEvent::Destroyed
| WindowEvent::KeyboardInput { input: KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
.. }, .. } => {
window_senders.remove(&window_id);
},
_ => if let Some(tx) = window_senders.get(&window_id) {
tx.send(event).unwrap();
},
}
}
_ => (),
}
})
}
10 changes: 4 additions & 6 deletions examples/multiwindow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ fn main() {
}

event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
*control_flow = match !windows.is_empty() {
true => ControlFlow::Wait,
false => ControlFlow::Exit,
};
match event {
Event::WindowEvent { event, window_id } => {
match event {
Expand All @@ -24,11 +27,6 @@ fn main() {
// This drops the window, causing it to close.
windows.remove(&window_id);
},
WindowEvent::Destroyed => {
if windows.is_empty() {
*control_flow = ControlFlow::Exit;
}
},
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode, .. }, .. } => {
if Some(VirtualKeyCode::Escape) == virtual_keycode {
windows.remove(&window_id);
Expand Down
49 changes: 37 additions & 12 deletions src/platform_impl/macos/app_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
self, collections::VecDeque, fmt::{self, Debug, Formatter},
hint::unreachable_unchecked, mem, sync::{Mutex, MutexGuard}, time::Instant,
collections::VecDeque, fmt::{self, Debug, Formatter},
hint::unreachable_unchecked, mem,
sync::{atomic::{AtomicBool, Ordering}, Mutex, MutexGuard}, time::Instant,
};

use cocoa::{appkit::NSApp, base::nil};
Expand Down Expand Up @@ -68,6 +69,7 @@ where

#[derive(Default)]
struct Handler {
ready: AtomicBool,
control_flow: Mutex<ControlFlow>,
control_flow_prev: Mutex<ControlFlow>,
start_time: Mutex<Option<Instant>>,
Expand All @@ -88,6 +90,18 @@ impl Handler {
self.waker.lock().unwrap()
}

fn is_ready(&self) -> bool {
self.ready.load(Ordering::Acquire)
}

fn set_ready(&self) {
self.ready.store(true, Ordering::Release);
}

fn is_control_flow_exit(&self) -> bool {
*self.control_flow.lock().unwrap() == ControlFlow::Exit
}

fn get_control_flow_and_update_prev(&self) -> ControlFlow {
let control_flow = self.control_flow.lock().unwrap();
*self.control_flow_prev.lock().unwrap() = *control_flow;
Expand Down Expand Up @@ -136,17 +150,18 @@ impl AppState {
}));
}

pub fn exit() -> ! {
pub fn exit() {
HANDLER.handle_nonuser_event(Event::LoopDestroyed);
std::process::exit(0)
}

pub fn launched() {
HANDLER.set_ready();
HANDLER.waker().start();
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
}

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

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

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

pub fn cleared() {
HANDLER.handle_nonuser_event(Event::EventsCleared);
if !HANDLER.is_ready() { return }
let mut will_stop = HANDLER.is_control_flow_exit();
for event in HANDLER.take_events() {
HANDLER.handle_nonuser_event(event);
will_stop |= HANDLER.is_control_flow_exit();
}
HANDLER.handle_nonuser_event(Event::EventsCleared);
will_stop |= HANDLER.is_control_flow_exit();
if will_stop {
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
return
}
HANDLER.update_start_time();
match HANDLER.get_old_and_new_control_flow() {
(ControlFlow::Poll, ControlFlow::Poll) => (),
(ControlFlow::Wait, ControlFlow::Wait) => (),
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant)) if old_instant == new_instant => (),
(ControlFlow::Exit, _) | (_, ControlFlow::Exit) => unreachable!(),
(old, new) if old == new => (),
(_, ControlFlow::Wait) => HANDLER.waker().stop(),
(_, ControlFlow::WaitUntil(new_instant)) => HANDLER.waker().start_at(new_instant),
(_, ControlFlow::WaitUntil(instant)) => HANDLER.waker().start_at(instant),
(_, ControlFlow::Poll) => HANDLER.waker().start(),
(_, ControlFlow::Exit) => {
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
},
}
}
}
29 changes: 29 additions & 0 deletions src/platform_impl/macos/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![allow(non_camel_case_types)]

use std::os::raw::c_void;

#[repr(C)]
pub struct dispatch_object_s { _private: [u8; 0] }

pub type dispatch_function_t = extern fn(*mut c_void);
pub type dispatch_queue_t = *mut dispatch_object_s;

pub fn dispatch_get_main_queue() -> dispatch_queue_t {
unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
}

#[link(name = "System", kind = "dylib")]
extern {
static _dispatch_main_q: dispatch_object_s;

pub fn dispatch_async_f(
queue: dispatch_queue_t,
context: *mut c_void,
work: Option<dispatch_function_t>,
);
pub fn dispatch_sync_f(
queue: dispatch_queue_t,
context: *mut c_void,
work: Option<dispatch_function_t>,
);
}
5 changes: 3 additions & 2 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
collections::VecDeque, marker::PhantomData,
collections::VecDeque, marker::PhantomData, process,
};

use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool};
Expand Down Expand Up @@ -78,7 +78,8 @@ impl<T> EventLoop<T> {
assert_ne!(app, nil);
AppState::set_callback(callback, self.window_target);
let _: () = msg_send![app, run];
AppState::exit()
AppState::exit();
process::exit(0)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/macos/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
pub const kCGCursorWindowLevelKey: NSInteger = 19;
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;

#[derive(Debug, Clone, Copy)]
pub enum NSWindowLevel {
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod app;
mod app_delegate;
mod app_state;
mod dispatch;
mod event;
mod event_loop;
mod ffi;
Expand Down Expand Up @@ -39,6 +40,9 @@ pub struct Window {
_delegate: WindowDelegate,
}

unsafe impl Send for Window {}
unsafe impl Sync for Window {}

impl Deref for Window {
type Target = UnownedWindow;
#[inline]
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/macos/observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ impl EventLoopWaker {
unsafe {
let current = CFAbsoluteTimeGetCurrent();
let duration = instant - now;
let fsecs =
duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64;
let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0
+ duration.as_secs() as f64;
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
}
}
Expand Down
Loading

0 comments on commit b492564

Please sign in to comment.