Skip to content
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

Windows CTRL_CLOSE not working? #122

Open
Astlaan opened this issue Dec 16, 2024 · 4 comments
Open

Windows CTRL_CLOSE not working? #122

Astlaan opened this issue Dec 16, 2024 · 4 comments

Comments

@Astlaan
Copy link

Astlaan commented Dec 16, 2024

Adding the ctrlc crate to Cargo.toml with the termination feature, I tested the code below.

I compiled with cargo build and then run it by double clicking the executable. Then, when the console spawns, click the X Close button.

With the ctrlc version, the code didn't work as expected. I expected, after closing the console, for both the X.txt and m_X.txt to keep being saved every second, for 5 seconds. However, when I clicked close, they both stop printing right away. The ctrl handler only prints a single file, m_1.txt.

I managed to get it work properly with the winapi version, commented in the code below. To test that, just comment the ctrlc code and uncommented that one.

What am I doing wrong?

main.rs

use ctrlc;
use std::fs::File;
use std::io::Write;
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};
use std::{thread, time::Duration};
use winapi::shared::minwindef::{BOOL, TRUE};
use winapi::um::consoleapi::SetConsoleCtrlHandler; // SetConsoleCtrlHandler is in consoleapi module



//============================ rust-ctrlc version =======================================


fn register_ctrl_handler(exit_flag: Arc<AtomicBool>) {
    ctrlc::set_handler(move || {
        let handler_start_time = std::time::Instant::now();

        for _ in 0..30 {
            let elapsed_secs = handler_start_time.elapsed().as_secs();
            let file_name = format!("m_{}.txt", elapsed_secs);
            File::create(&file_name);
            thread::sleep(Duration::from_secs(1));
        }

        exit_flag.store(true, Ordering::SeqCst);
        println!("Handler finished execution.");
    })
    .expect("Error setting up CTRL+C handler");
}

fn main() {
    let start_time = std::time::Instant::now();
    let exit_flag = Arc::new(AtomicBool::new(false));
    register_ctrl_handler(Arc::clone(&exit_flag));

    while !exit_flag.load(Ordering::SeqCst) {
        let elapsed_secs = start_time.elapsed().as_secs();
        let file_name = format!("{}.txt", elapsed_secs);
        let mut file = File::create(&file_name).expect("Unable to create file");
        writeln!(file, "Elapsed seconds: {}", elapsed_secs).expect("Unable to write to file");
        println!("Saved file: {}", file_name);
        thread::sleep(Duration::from_secs(1));
    }

    println!("Program exited gracefully.");
}

// ==================================== WINAPI version =======================================

// unsafe extern "system" fn ctrl_handler(_ctrl_type: u32) -> BOOL {
//     println!("CTRL+C detected. Handler started...");
//     let handler_start_time = std::time::Instant::now();

//     for i in 0..30 {
//         let elapsed_secs = handler_start_time.elapsed().as_secs();
//         let file_name = format!("m_{}.txt", elapsed_secs);
//         File::create(&file_name);
//         thread::sleep(Duration::from_secs(1));
//     }

//     println!("Handler finished execution.");
//     TRUE
// }

// fn register_ctrl_handler(_exit_flag: Arc<AtomicBool>) {
//     unsafe {
//         if SetConsoleCtrlHandler(Some(ctrl_handler), TRUE) == 0 {
//             panic!("Error setting up CTRL+C handler");
//         }
//     }
// }

// fn main() {
//     let start_time = std::time::Instant::now();
//     let exit_flag = Arc::new(AtomicBool::new(false));
//     register_ctrl_handler(Arc::clone(&exit_flag));

//     while !exit_flag.load(Ordering::SeqCst) {
//         let elapsed_secs = start_time.elapsed().as_secs();
//         let file_name = format!("{}.txt", elapsed_secs);
//         let mut file = File::create(&file_name).expect("Unable to create file");
//         writeln!(file, "Elapsed seconds: {}", elapsed_secs).expect("Unable to write to file");
//         println!("Saved file: {}", file_name);
//         thread::sleep(Duration::from_secs(1));
//     }

//     println!("Program exited gracefully.");
// }

Cargo.toml

[package]
name = "rust-thread"
version = "0.1.0"
edition = "2021"

[dependencies]
winapi ={version = "*", features = ["wincon", "minwindef", "consoleapi"]}
ctrlc = { version = "*", features = ["termination"] }
@Detegr
Copy link
Owner

Detegr commented Jan 2, 2025

CtrlC runs the signal handler in a separate thread, so it's trying to block that thread. The main thread exists and forcefully exists the signal handling thread as well. I did an implementation few years back that would've solved this issue #60, but as the project was widely adopted already and no one took the time to review the code for bugs, I wasn't brave enough to merge it in. Since then I've left this crate more or less in a maintenance mode.

@MikiGrit
Copy link

MikiGrit commented Jan 7, 2025

@Detegr Could you please share some alternatives that one can use? I was not able to find any suitable. Thanks.

@Astlaan
Copy link
Author

Astlaan commented Jan 16, 2025

@MikiGrit

You can try something inspired on one of the solutions I mention in tokio-rs/tokio#7039

You can also see the concrete workaround I used for my specific case:
https://github.com/Astlaan/upnp-engage
But it is a windows-specific implementation (this issue doesn't happen on linux anyway). I defined the cleanup code inside the control handler, and it gets called and makes whatever changes are necessary.

Another alternative, if you can forego using a Windows Console App, is to use a Windows GUI app. I can't remember whether Ctrl-c implements the Windows GUI signals though.

@MikiGrit
Copy link

@Astlaan Ok, great! Thanks for the response. I'll look into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants