-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[bug] run_iteration
example busy-loops on Windows
#8631
Comments
Okay I guess it's a limitation inherited from |
Will try https://docs.rs/tauri/latest/tauri/plugin/struct.Builder.html#method.on_drop. I haven't used a plugin yet but maybe that's the more idiomatic way to do it. |
iirc on_drop only runs if you use remove_plugin, it's not part of the cleanup that app.exit() runs (it only cleans up the tray and Commands i think) |
Yeah I checked the code and the only cleanup that really happens is something like stopping child processes. For curious readers I ended up finding where tao explains it
I don't fully understand it, but I guess it is some kind of platform limitation. :( I would have thought after more than 20 years of GUI paradigms, the major OSes would have made GUIs as nice to program as CLIs. Oh well. I'll put a mutex or something and a Edit: Just a little more opinion-posting - I can see why webdev took off, if the GUI is just a TCP listener, the OS is much nicer to you than if you actually use most OS' GUI primitives |
I thought so too at one point, until i got into working on (instead of "with") a gui framework. Now i cry myself to sleep every night 🤷 That said, not having an easier way to do custom clean up is on us and our api design. (No idea about the event loop stuff thi issue was about though) |
Tauri never call destructors and never stack unwind because it's calling My current workaround about dropping resources is using A cell can be initialized only once. To set the content of the cell, you use Yes, this require unsafe and a carefull design, but if you use the correct implementation it's safe (the compiler is simply unable to reason about it). Note :
Cell errors can be handled gracefully with the cell API, at the cost of useless check if you know you don't have errors in your code (but who care, it's about initialization and destruction, performance does not really matter here and if you really want you can write unchecked method with unreachable hint for dead code optimization). pub struct ThreadManager {
should_exit: Arc<AtomicBool>,
handle: std::thread::JoinHandle<()>,
}
impl ThreadManager {
fn new(should_exit: Arc<AtomicBool>) -> Self {
// Start thread and return a new ThreadManager with the associated signal and thread handle.
// Move "should_exit" (Arc<AtomicBool>) into the thread.
// ThreadManager::new is private, can not be constructed outside of the start() method.
}
/// Start the ThreadManager.
///
/// Calling start multiple time has no effect, unless you empty the cell with stop(). There's only a single ThreadManager.
pub fn start(should_exit: Arc<AtomicBool>) {
unsafe {
let _ = THREAD_MANAGER.set(Self::new(should_exit));
}
}
/// Stop the ThreadManager. Usefull for exiting gracefully.
/// Calling stop multiple time has no effect. There's only a single ThreadManager.
/// Note that you should signal the thread to stop since it's running an infinite loop.
/// If you don't set "should_exit = false" (from the Arc<AtomicBool> you passed-in originally), we gonna wait the join forever.
pub fn stop() {
unsafe {
// Just in case someone call stop() multiple time, we want to be sure the Cell is not empty.
if let Some(thread_manager) = THREAD_MANAGER.take() {
thread_manager
.handle
.join()
.expect("thread should join");
}
};
}
}
/// ThreadManager is thread safe. We use a OnceLock to ensure it's initialized only once.
/// Note : it's not public, the only way to interact with it is with start() and stop().
/// Since the ThreadManager is a static global, it's not possible to drop it without making it a static mutable.
static mut THREAD_MANAGER: OnceLock<ThreadManager> = OnceLock::new(); In You clone and move the The single storage that will leak in this process is the The same principle can be applied if you want to share data globally in Tauri and need to destroy it properly. You can pass Outside of ugly static storage duration, you could also use End result is it will remain 1 strong reference at the end and things will never get dropped - Pick your poison :
|
@arialpew I have a similar case, I have a worker thread for COM that I'd like to join gracefully. I didn't end up needing Edit: I dug this up - It's a limitation of tao, not Tauri itself, and tao has some justification for it that I don't fully understand :/ https://docs.rs/tao/latest/x86_64-pc-windows-msvc/tao/event_loop/struct.EventLoop.html#method.run |
Having just spent a couple days trying to gracefully clean up a background future, this whole situation needs to be better documented if nothing else. Exiting with |
Agreed on docs. I always look at examples first, so if it was me I'd put an tauri/examples/helloworld/main.rs Line 13 in f5e4b67
Also... The docs say But you could have a |
Describe the bug
I'm getting this output indicating a busy loop:
Reproduction
https://github.com/ReactorScram/tauri_repro
Run
cargo tauri dev
orcd src-tauri; cargo run
. stdout will be filled with constant print statements.Expected behavior
I expected from #5532 that
run_iteration
would block until the main event loop receives an event, then process it, then return.Instead it doesn't seem to block, at least on Windows.
This is part of a larger yak shave for me. I'm trying to join a worker thread after Tauri exits, but all these methods have failed:
app.exit
doesn't triggerRunEvent::Exit
(intentional behavior per Does `app.exit()` not trigger `RunEvent::Exit` event? #4662), so I can't clean up in thererun_iteration
busy loopsRunEvent
into the main loop to wake uprun
and check whether we need to clean upDrop
naturally, per [bug] Some resources are not dropped when tauri app is closed #7358on_exit
withBuilder
https://docs.rs/tauri/latest/tauri/struct.Builder.htmlMy fallback plan is to write my own cleanup function and call that, and have it call
app.exit
at the end. It'll work, but it'll require some comments explaining it.Full
tauri info
outputStack trace
Additional context
If this is a platform limitation, like maybe it's just impossible to gracefully stop a GUI event loop on Windows, I would understand.But if it's not... can I just have anapp.stop
that stops the event loop, so everything canDrop
?Seems to be a
tao
thing, see later commentThe text was updated successfully, but these errors were encountered: