Native Windows GUI: Multithreading

Win32 is a single threaded API. That means a window and all it's children control will be contained on a single thread (the GUI thread). This is a problem is an application has a method that blocks the GUI thread (ex: reading a large file) because doing so will make the UI unusable for the duration of the method. This section will explain how NWG fix this problem.

The notice object

Assuming there is a task that will block the UI thread. It's obvious the first step would be to just move this task into another thread. But then, how does the main thread will know when the task is completed? The answer is the Notice object.

(Using notice objects require the notice feature.)

The Notice object is an invisible component that can send a message to their window from another thread. Here's a very simple example on how to use it:
pub struct HeavyCompute {
    window: nwg::Window,
    notice: nwg::Notice,
    compute: RefCell<Option<thread::JoinHandle<u64>>>,
}

// ... NativeUi implementation ... 

/// `Event::OnWindowInit` callback
fn on_init(app: &HeavyCompute) {
    let sender = app.notice.sender();
    *self.compute.borrow_mut() = Some(thread::spawn(move || {
        thread::sleep(Duration::new(5, 0));
        sender.notice();
        1+1
    }));
}

/// `Event::OnNotice` callback
fn on_notice(app: &HeavyCompute) {
    let mut data = app.compute.borrow_mut();
    match data.take() {
        Some(data) => {
            println!("THE ANSWER IS {:?}!", data);
        },
        None => {}
    }
}

In this example, a heavy compute operation is started off the main thread after window initialization.
  1. on_init generates a NoticeSender from the application notice
  2. The NoticeSender is sent to another thread
  3. The thread handle is saved in the application to read from it later
  4. Once the off-thread computation are done, sender.notice() is called to wake up the notice on the main GUI thread
  5. Finally, the OnNotice event is raised and the compute thread is joined to get the compute value

Running windows on multiple threads

NWG does not have any global state. As such it's possible to create as many GUI thread as you might want. Each GUI thread can have its own windows. Blocking one of the GUI thread won't block the second. This is useful when implementing dialogs.

Example