Why always return 1 after handling ctrl-c?

std impl with ctrlc crate:

fn main() {
    let (close_tx, close_rx) = std::sync::mpsc::sync_channel::<()>(0);
    ctrlc::set_handler(move || close_tx.send(()).unwrap()).unwrap();
    for _ in 0..50 {
        if let Ok(()) = close_rx.try_recv() {
            break;
        }
        std::thread::sleep(std::time::Duration::from_millis(100));
    }
    // std::process::exit(0);
}

tokio impl:

#[tokio::main]
async fn main() {
    let (close_tx, mut close_rx) = tokio::sync::oneshot::channel::<()>();
    tokio::spawn(async move {
        tokio::signal::ctrl_c().await.unwrap();
        close_tx.send(()).unwrap();
    });
    for _ in 0..50 {
        if let Ok(()) = close_rx.try_recv() {
            break;
        }
        tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    }
    // std::process::exit(0);
}

Both 2 impls make the process return 1 rather than the normal 0 after handling ctrl-c, whether the std::process::exit(0); is added or not. But running with cargo run will not get the process didn't exit successfully prompt. For comparison, if a std::process::exit(1); is added, it will get the prompt.

When you hit ctrl-C during cargo run, cargo itself also receives the signal and is terminated before it has a chance to print "process didn't exit successfully".

1 Like

The main issue have not solved yet.

Like tornewuff said.
Run the program's binary directly and it will return 0 even when you press ctrl-c.

No, running the program's binary directly also get return 1 after ctrl-c in both ctrlc and tokio cases.
On x86_64-pc-windows-msvc, observing the return value by VSCodium's shell integration.

Perhaps the shell integration is doing something weird?
For me, running your first example in powershell I get a 0 exit code on ctrl+c

PS C:\Users\Tristan\projects\ctrlctest> cargo run
  Downloaded ctrlc v3.4.1
  Downloaded 1 crate (14.2 KB) in 0.68s
   Compiling windows_x86_64_msvc v0.48.5
   Compiling windows-targets v0.48.5
   Compiling windows-sys v0.48.0
   Compiling ctrlc v3.4.1
   Compiling ctrlctest v0.1.0 (C:\Users\Tristan\projects\ctrlctest)
    Finished dev [unoptimized + debuginfo] target(s) in 9.44s
     Running `target\debug\ctrlctest.exe`
PS C:\Users\Tristan\projects\ctrlctest> echo $LASTEXITCODE
0

(included full output so you can compare dependency versions, it's also using rust 1.72.1 running on windows 10)
and if i uncomment the std::process::exit(1)

PS C:\Users\Tristan\projects\ctrlctest> cargo run
   Compiling ctrlctest v0.1.0 (C:\Users\Tristan\projects\ctrlctest)
    Finished dev [unoptimized + debuginfo] target(s) in 2.86s
     Running `target\debug\ctrlctest.exe`
error: process didn't exit successfully: `target\debug\ctrlctest.exe` (exit code: 1)
PS C:\Users\Tristan\projects\ctrlctest> echo $LASTEXITCODE
1

I also tried updating the example to avoid any chance the ctrl+c is not getting caught and the program is just being killed

fn main() {
    let (close_tx, close_rx) = std::sync::mpsc::sync_channel::<()>(0);
    ctrlc::set_handler(move || {
        println!("caught ctrl+c");
        close_tx.send(()).unwrap()
    }).unwrap();
    let _ = close_rx.recv();
    println!("exiting");
}

might be worth checking that to make sure the shell isn't doing something else on ctrl+c?

PS C:\Users\Tristan\projects\ctrlctest> cargo run
   Compiling ctrlctest v0.1.0 (C:\Users\Tristan\projects\ctrlctest)
    Finished dev [unoptimized + debuginfo] target(s) in 1.26s
     Running `target\debug\ctrlctest.exe`
caught ctrl+c
exiting
PS C:\Users\Tristan\projects\ctrlctest> echo $LASTEXITCODE
0

The behavior observed using $LASTEXITCODE is indeed correct. Then the problem does seem to be related to shell integration.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.