Skip to content

Commit 32b27f5

Browse files
committed
work
1 parent 4a1fc03 commit 32b27f5

File tree

12 files changed

+390
-22
lines changed

12 files changed

+390
-22
lines changed

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,9 @@ nix = {git = "https://github.com/nix-rust/nix"}
2020
libc = "0.2.68"
2121
errno = "0.2.5"
2222

23+
[target.'cfg(target_os="windows")'.dependencies]
24+
winapi = { version = "0.3.9", features = ["std", "processthreadsapi", "jobapi2", "errhandlingapi",
25+
"namedpipeapi", "fileapi", "synchapi", "winnt"] }
26+
2327
[workspace]
2428
members = ["minion-ffi", ".", "minion-tests", "minion-cli"]

minion-tests/Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ version = "0.1.0"
44
authors = ["Mikail Bagishov <bagishov.mikail@yandex.ru>"]
55
edition = "2018"
66

7-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8-
97
[dependencies]
108
once_cell = "1.4.0"
119
minion = {path = ".."}
1210
clap = "2.33.1"
13-
nix = "0.17.0"
1411
tempfile = "3.1.0"
12+
13+
[target.'cfg(target_os="linux")'.dependencies]
14+
nix = "0.17.0"
15+
16+
[target.'cfg(target_os="windows")'.dependencies]
17+
winapi = { version = "0.3.9", features = ["processthreadsapi"] }

minion-tests/src/tests/simple.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ impl crate::TestCase for TTlFork {
5050
(TODO: verify this is not wall-clock time limit)"
5151
}
5252
fn test(&self) -> ! {
53+
#[cfg(target_os = "linux")]
5354
nix::unistd::fork().unwrap();
55+
#[cfg(target_os = "winfows")]
56+
winapi::um::
5457
exceed_time_limit()
5558
}
5659
fn check(&self, cp: &mut dyn ChildProcess, _: &dyn Sandbox) {

src/erased.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub trait ChildProcess {
5959
timeout: Option<std::time::Duration>,
6060
) -> anyhow::Result<crate::WaitOutcome>;
6161
fn poll(&self) -> anyhow::Result<()>;
62-
fn is_finished(&self) -> anyhow::Result<bool>;
62+
fn is_finished(&self) -> bool;
6363
}
6464

6565
impl<C: crate::ChildProcess> ChildProcess for C {
@@ -93,8 +93,8 @@ impl<C: crate::ChildProcess> ChildProcess for C {
9393
fn poll(&self) -> anyhow::Result<()> {
9494
self.poll().map_err(Into::into)
9595
}
96-
fn is_finished(&self) -> anyhow::Result<bool> {
97-
self.is_finished().map_err(Into::into)
96+
fn is_finished(&self) -> bool {
97+
self.is_finished()
9898
}
9999
}
100100

@@ -133,7 +133,11 @@ pub type ChildProcessOptions = crate::ChildProcessOptions<Box<dyn Sandbox>>;
133133

134134
/// Returns backend instance
135135
pub fn setup() -> anyhow::Result<Box<dyn Backend>> {
136-
Ok(Box::new(crate::linux::LinuxBackend::new(
136+
#[cfg(target_os = "linux")]
137+
return Ok(Box::new(crate::linux::LinuxBackend::new(
137138
crate::linux::Settings::new(),
138-
)?))
139+
)?));
140+
141+
#[cfg(target_os = "windows")]
142+
return Ok(Box::new(crate::windows::WindowsBackend::new()));
139143
}

src/lib.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ mod command;
1212
#[cfg(target_os = "linux")]
1313
pub mod linux;
1414

15+
#[cfg(target_os = "windows")]
16+
pub mod windows;
17+
1518
pub mod erased;
1619

1720
use serde::{Deserialize, Serialize};
@@ -172,9 +175,17 @@ impl InputSpecification {
172175

173176
/// # Safety
174177
/// See requirements of `handle`
178+
#[cfg(target_os = "linux")]
175179
pub unsafe fn handle_of<T: std::os::unix::io::IntoRawFd>(obj: T) -> Self {
176180
unsafe { Self::handle(obj.into_raw_fd() as u64) }
177181
}
182+
183+
/// # Safety
184+
/// See requirements of `handle`
185+
#[cfg(target_os = "windows")]
186+
pub unsafe fn handle_of<T: std::os::windows::io::IntoRawHandle>(obj: T) -> Self {
187+
unsafe { Self::handle(obj.into_raw_handle() as u64) }
188+
}
178189
}
179190

180191
/// Configures stdout and stderr for child
@@ -217,9 +228,17 @@ impl OutputSpecification {
217228

218229
/// # Safety
219230
/// See requirements of `handle`
231+
#[cfg(target_os = "linux")]
220232
pub unsafe fn handle_of<T: std::os::unix::io::IntoRawFd>(obj: T) -> Self {
221233
unsafe { Self::handle(obj.into_raw_fd() as u64) }
222234
}
235+
236+
/// # Safety
237+
/// See requirements of `handle`
238+
#[cfg(target_os = "windows")]
239+
pub unsafe fn handle_of<T: std::os::windows::io::IntoRawHandle>(obj: T) -> Self {
240+
unsafe { Self::handle(obj.into_raw_handle() as u64) }
241+
}
223242
}
224243

225244
#[derive(Debug, Clone)]
@@ -306,10 +325,8 @@ pub trait ChildProcess: Debug + 'static {
306325
/// If timeout is None, `wait_for_exit` will block until child has exited
307326
fn wait_for_exit(&self, timeout: Option<Duration>) -> Result<WaitOutcome, Self::Error>;
308327

309-
/// Refreshes information about process
310-
fn poll(&self) -> Result<(), Self::Error>;
311-
312-
/// Returns whether child process has exited by the moment of call
313-
/// This function doesn't blocks on waiting (see `wait_for_exit`).
314-
fn is_finished(&self) -> Result<bool, Self::Error>;
328+
/// Returns whether child process is known to be finished
329+
/// (I.e. whether last `wait_for_exit` has not timed out)
330+
/// This function doesn't wait child (see `wait_for_exit`).
331+
fn is_finished(&self) -> bool;
315332
}

src/linux.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,7 @@ impl ChildProcess for LinuxChildProcess {
9393
}
9494
}
9595

96-
fn poll(&self) -> Result<(), Error> {
97-
self.wait_for_exit(Some(Duration::from_nanos(1)))
98-
.map(|_w| ())
99-
}
100-
10196
fn is_finished(&self) -> Result<bool, Error> {
102-
self.poll()?;
10397
Ok(self.exit_code.load(Ordering::SeqCst) != EXIT_CODE_STILL_RUNNING)
10498
}
10599
}

src/windows.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//! Minion on windows
2+
3+
pub mod error;
4+
mod pipe;
5+
mod sandbox;
6+
mod child;
7+
8+
pub use error::Error;
9+
pub use pipe::{ReadPipe, WritePipe};
10+
pub use sandbox::WindowsSandbox;
11+
12+
use error::Cvt;
13+
14+
/// Minion backend, supporting Windows.
15+
#[derive(Debug)]
16+
pub struct WindowsBackend {}
17+
18+
impl WindowsBackend {
19+
pub fn new() -> WindowsBackend {
20+
WindowsBackend {}
21+
}
22+
}
23+
24+
impl crate::Backend for WindowsBackend {
25+
type Error = Error;
26+
type Sandbox = WindowsSandbox;
27+
type ChildProcess = child::WindowsChildProcess;
28+
29+
fn new_sandbox(&self, options: crate::SandboxOptions) -> Result<Self::Sandbox, Self::Error> {
30+
todo!()
31+
}
32+
33+
fn spawn(
34+
&self,
35+
options: crate::ChildProcessOptions<Self::Sandbox>,
36+
) -> Result<Self::ChildProcess, Self::Error> {
37+
todo!()
38+
}
39+
}

src/windows/child.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use crate::{
2+
windows::{Cvt, Error, ReadPipe, WritePipe, },
3+
WaitOutcome,
4+
};
5+
use std::{
6+
convert::TryInto,
7+
sync::atomic::{AtomicU32, Ordering::Relaxed},
8+
time::Duration,
9+
};
10+
use winapi::um::{minwinbase::STILL_ACTIVE, winnt::HANDLE};
11+
12+
#[derive(Debug)]
13+
pub struct WindowsChildProcess {
14+
/// Handle to winapi Process object
15+
child: HANDLE,
16+
17+
/// Contains exit code or STILL_ACTIVE otherwise
18+
exit_code: AtomicU32,
19+
20+
/// Stdin
21+
stdin: Option<WritePipe>,
22+
/// Stdout
23+
stdout: Option<ReadPipe>,
24+
/// Stderr
25+
stderr: Option<ReadPipe>,
26+
}
27+
28+
impl WindowsChildProcess {}
29+
30+
impl crate::ChildProcess for WindowsChildProcess {
31+
type Error = Error;
32+
type PipeIn = WritePipe;
33+
type PipeOut = ReadPipe;
34+
35+
fn get_exit_code(&self) -> Result<Option<i64>, Self::Error> {
36+
let ec = self.exit_code.load(Relaxed);
37+
if ec != STILL_ACTIVE {
38+
return Ok(Some(ec.into()));
39+
}
40+
let mut exit_code = 0;
41+
unsafe {
42+
Cvt::nonzero(winapi::um::processthreadsapi::GetExitCodeProcess(
43+
self.child,
44+
&mut exit_code,
45+
))?;
46+
}
47+
if exit_code == STILL_ACTIVE {
48+
return Ok(None);
49+
}
50+
self.exit_code.store(exit_code, Relaxed);
51+
Ok(Some(exit_code.into()))
52+
}
53+
54+
fn stdin(&mut self) -> Option<Self::PipeIn> {
55+
self.stdin.take()
56+
}
57+
fn stdout(&mut self) -> Option<Self::PipeOut> {
58+
self.stdout.take()
59+
}
60+
fn stderr(&mut self) -> Option<Self::PipeOut> {
61+
self.stderr.take()
62+
}
63+
fn wait_for_exit(&self, timeout: Option<Duration>) -> Result<WaitOutcome, Self::Error> {
64+
if self.exit_code.load(Relaxed) != STILL_ACTIVE {
65+
return Ok(WaitOutcome::AlreadyFinished);
66+
}
67+
let timeout = match timeout {
68+
Some(dur) => dur
69+
.as_millis()
70+
.max(1)
71+
.try_into()
72+
.unwrap_or(u32::max_value()),
73+
None => winapi::um::winbase::INFINITE,
74+
};
75+
let res = unsafe { winapi::um::synchapi::WaitForSingleObject(self.child, timeout) };
76+
if res == winapi::um::winbase::WAIT_FAILED {
77+
return Err(Error::last());
78+
}
79+
match self.get_exit_code()? {
80+
Some(_) => Ok(WaitOutcome::Exited),
81+
None => Ok(WaitOutcome::Timeout),
82+
}
83+
}
84+
fn is_finished(&self) -> bool {
85+
self.exit_code.load(Relaxed) != STILL_ACTIVE
86+
}
87+
}

src/windows/error.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#[derive(Debug, thiserror::Error)]
2+
pub enum Error {
3+
#[error("winapi call failed")]
4+
Syscall { errno: u32 },
5+
}
6+
7+
impl From<u32> for Error {
8+
fn from(errno: u32) -> Self {
9+
Error::Syscall { errno }
10+
}
11+
}
12+
13+
impl Error {
14+
pub(crate) fn last() -> Self {
15+
Error::Syscall {
16+
errno: unsafe { winapi::um::errhandlingapi::GetLastError() },
17+
}
18+
}
19+
}
20+
21+
/// Helper for checking return values
22+
pub(crate) struct Cvt {
23+
_priv: (),
24+
}
25+
26+
impl Cvt {
27+
/// checks that operation returned non-zero
28+
pub fn nonzero(ret: i32) -> Result<i32, Error> {
29+
if ret != 0 {
30+
Ok(ret)
31+
} else {
32+
Err(Error::last())
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)