Skip to content

Commit 25e30f7

Browse files
committed
Major redesign. Remove InterruptibleCommand trait. Change to ctrl-break on Windows. Stop add creation flags on Windows.
1 parent b4f2413 commit 25e30f7

4 files changed

Lines changed: 24 additions & 107 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ cargo add send_ctrlc -F tokio
2828
## Examples
2929

3030
```rust
31-
use send_ctrlc::{Interruptible as _, InterruptibleCommand as _};
31+
use send_ctrlc::{Interruptible as _};
3232

3333
// Synchronous...
3434

@@ -41,7 +41,7 @@ fn main() {
4141
command.arg("127.0.0.1");
4242

4343
// Spawn the ping, interrupt it, and wait for it to complete
44-
let mut child = command.spawn_interruptible().unwrap();
44+
let mut child = command.spawn().unwrap();
4545
child.interrupt().unwrap();
4646
child.wait().unwrap();
4747
}
@@ -58,7 +58,7 @@ async fn main() {
5858
command.arg("127.0.0.1");
5959

6060
// Spawn the ping, interrupt it, and wait for it to complete
61-
let mut child = command.spawn_interruptible().unwrap();
61+
let mut child = command.spawn().unwrap();
6262
child.interrupt().unwrap();
6363
child.wait().await.unwrap();
6464
}

src/lib.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,6 @@ mod readme_tests {}
1111

1212
use std::io;
1313

14-
pub use stdlib::InterruptibleChild;
15-
16-
#[cfg(windows)]
17-
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
18-
19-
/// A trait for spawning interruptible child processes
20-
pub trait InterruptibleCommand {
21-
type Child: Interruptible;
22-
23-
/// Spawn a new interruptible child process that has been specifically configured
24-
/// to ensure it is interruptible. An error is returned if one occurs while attempting
25-
/// to spawn the child process.
26-
fn spawn_interruptible(&mut self) -> io::Result<Self::Child>;
27-
}
28-
2914
/// A trait for sending interrupts/ctrl-c to child processes.
3015
///
3116
/// NOTE: By implementing this trait, you are stating that the correct steps have been taken to
@@ -67,12 +52,16 @@ mod inner {
6752

6853
#[cfg(windows)]
6954
pub fn interrupt(pid: u32) -> io::Result<()> {
70-
use windows_sys::Win32::System::Console::{CTRL_C_EVENT, GenerateConsoleCtrlEvent};
55+
use windows_sys::Win32::System::Console::{CTRL_BREAK_EVENT, GenerateConsoleCtrlEvent};
7156

72-
// NOTE: This only works if the process is in a new process group
57+
// NOTE: After testing, it seems CTRL_BREAK_EVENT works in all cases (or all the ones I tried).
58+
// CTRL_C_EVENT didn't work for the `ctrlc` crate, for example, which is my primary use case.
59+
// NOTE 2: Despite what the docs say, and I found them confusing, it seems that no special
60+
// creation flags are actually needed anymore on Windows 10+. According to ChatGPT, this changed
61+
// around Windows 10+. I don't have anything older to test on to confirm or deny this.
7362
// SAFETY: This is a standard Windows console function. Any number passed in is memory safe,
7463
// even if it impacts a process the user hadn't intended.
75-
if unsafe { GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid) } != 0 {
64+
if unsafe { GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) } != 0 {
7665
Ok(())
7766
} else {
7867
Err(io::Error::last_os_error())

src/stdlib.rs

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,17 @@
1-
use std::{
2-
io,
3-
ops::{Deref, DerefMut},
4-
process::{Child, Command},
5-
};
1+
use std::{io, process::Child};
62

7-
use crate::{Interruptible, InterruptibleCommand};
3+
use crate::Interruptible;
84

9-
#[cfg(windows)]
10-
fn set_creation_flags(command: &mut Command) {
11-
use std::os::windows::process::CommandExt as _;
12-
command.creation_flags(crate::CREATE_NEW_PROCESS_GROUP);
13-
}
14-
15-
impl InterruptibleCommand for Command {
16-
type Child = InterruptibleChild;
17-
18-
fn spawn_interruptible(&mut self) -> io::Result<InterruptibleChild> {
19-
#[cfg(windows)]
20-
set_creation_flags(self);
21-
self.spawn().map(InterruptibleChild)
22-
}
23-
}
24-
25-
/// A child process that can be interrupted
26-
pub struct InterruptibleChild(Child);
27-
28-
impl Interruptible for InterruptibleChild {
5+
impl Interruptible for Child {
296
fn pid(&mut self) -> io::Result<Option<u32>> {
30-
match self.0.try_wait() {
7+
match self.try_wait() {
318
Ok(Some(_)) => Ok(None),
32-
Ok(None) => Ok(Some(self.0.id())),
9+
Ok(None) => Ok(Some(self.id())),
3310
Err(e) => Err(e),
3411
}
3512
}
3613
}
3714

38-
impl Deref for InterruptibleChild {
39-
type Target = Child;
40-
41-
fn deref(&self) -> &Self::Target {
42-
&self.0
43-
}
44-
}
45-
46-
impl DerefMut for InterruptibleChild {
47-
fn deref_mut(&mut self) -> &mut Self::Target {
48-
&mut self.0
49-
}
50-
}
51-
5215
#[cfg(test)]
5316
mod tests {
5417
use super::*;
@@ -60,7 +23,7 @@ mod tests {
6023
command.arg("-t");
6124
command.arg("127.0.0.1");
6225

63-
let mut child = command.spawn_interruptible().unwrap();
26+
let mut child = command.spawn().unwrap();
6427
child.interrupt().unwrap();
6528
child.wait().unwrap();
6629
}
@@ -69,7 +32,7 @@ mod tests {
6932
fn test_completed_interruptible_command() {
7033
let mut command = std::process::Command::new("ping");
7134

72-
let mut child = command.spawn_interruptible().unwrap();
35+
let mut child = command.spawn().unwrap();
7336
child.wait().unwrap();
7437
assert!(child.interrupt().is_err());
7538
}

src/tokio.rs

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,12 @@
1-
use std::{
2-
io,
3-
ops::{Deref, DerefMut},
4-
};
1+
use std::io;
52

6-
use crate::{Interruptible, InterruptibleCommand};
3+
use crate::Interruptible;
74

8-
use tokio::process::{Child, Command};
5+
use tokio::process::Child;
96

10-
#[cfg(windows)]
11-
fn set_creation_flags(command: &mut Command) {
12-
command.creation_flags(crate::CREATE_NEW_PROCESS_GROUP);
13-
}
14-
15-
impl InterruptibleCommand for Command {
16-
type Child = InterruptibleChild;
17-
18-
fn spawn_interruptible(&mut self) -> io::Result<InterruptibleChild> {
19-
#[cfg(windows)]
20-
set_creation_flags(self);
21-
self.spawn().map(InterruptibleChild)
22-
}
23-
}
24-
25-
/// A child process that can be interrupted
26-
pub struct InterruptibleChild(Child);
27-
28-
impl Interruptible for InterruptibleChild {
7+
impl Interruptible for Child {
298
fn pid(&mut self) -> io::Result<Option<u32>> {
30-
Ok(self.0.id())
31-
}
32-
}
33-
34-
impl Deref for InterruptibleChild {
35-
type Target = Child;
36-
37-
fn deref(&self) -> &Self::Target {
38-
&self.0
39-
}
40-
}
41-
42-
impl DerefMut for InterruptibleChild {
43-
fn deref_mut(&mut self) -> &mut Self::Target {
44-
&mut self.0
9+
Ok(self.id())
4510
}
4611
}
4712

@@ -56,7 +21,7 @@ mod tests {
5621
command.arg("-t");
5722
command.arg("127.0.0.1");
5823

59-
let mut child = command.spawn_interruptible().unwrap();
24+
let mut child = command.spawn().unwrap();
6025
child.interrupt().unwrap();
6126
child.wait().await.unwrap();
6227
}
@@ -65,7 +30,7 @@ mod tests {
6530
async fn test_completed_interruptible_command() {
6631
let mut command = tokio::process::Command::new("ping");
6732

68-
let mut child = command.spawn_interruptible().unwrap();
33+
let mut child = command.spawn().unwrap();
6934
child.wait().await.unwrap();
7035
assert!(child.interrupt().is_err());
7136
}

0 commit comments

Comments
 (0)