Skip to content

Commit 891a155

Browse files
authored
Merge pull request #185 from jsturtevant/fix-windows-closing
[Windows] Closing connections properly on Windows
2 parents 767565d + c9f67f5 commit 891a155

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

src/sync/sys/windows/net.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const WAIT_FOR_EVENT: i32 = 1;
3737

3838
pub struct PipeListener {
3939
first_instance: AtomicBool,
40+
shutting_down: AtomicBool,
4041
address: String,
4142
connection_event: isize,
4243
}
@@ -65,6 +66,7 @@ impl PipeListener {
6566
let connection_event = create_event()?;
6667
Ok(PipeListener {
6768
first_instance: AtomicBool::new(true),
69+
shutting_down: AtomicBool::new(false),
6870
address: sockaddr.to_string(),
6971
connection_event
7072
})
@@ -98,6 +100,9 @@ impl PipeListener {
98100
trace!("listening for connection");
99101
let result = unsafe { ConnectNamedPipe(np.named_pipe, ol.as_mut_ptr())};
100102
if result != 0 {
103+
if let Some(error) = self.handle_shutdown(&np) {
104+
return Err(error);
105+
}
101106
return Err(io::Error::last_os_error());
102107
}
103108

@@ -110,11 +115,17 @@ impl PipeListener {
110115
return Err(io::Error::last_os_error());
111116
}
112117
_ => {
118+
if let Some(shutdown_signal) = self.handle_shutdown(&np) {
119+
return Err(shutdown_signal);
120+
}
113121
Ok(Some(np))
114122
}
115123
}
116124
}
117125
e if e.raw_os_error() == Some(ERROR_PIPE_CONNECTED as i32) => {
126+
if let Some(error) = self.handle_shutdown(&np) {
127+
return Err(error);
128+
}
118129
Ok(Some(np))
119130
}
120131
e => {
@@ -126,6 +137,17 @@ impl PipeListener {
126137
}
127138
}
128139

140+
fn handle_shutdown(&self, np: &PipeConnection) -> Option<io::Error> {
141+
if self.shutting_down.load(Ordering::SeqCst) {
142+
np.close().unwrap_or_else(|err| trace!("Failed to close the pipe {:?}", err));
143+
return Some(io::Error::new(
144+
io::ErrorKind::Other,
145+
"closing pipe",
146+
));
147+
}
148+
None
149+
}
150+
129151
fn new_instance(&self) -> io::Result<isize> {
130152
let name = OsStr::new(&self.address.as_str())
131153
.encode_wide()
@@ -153,6 +175,7 @@ impl PipeListener {
153175

154176
pub fn close(&self) -> Result<()> {
155177
// release the ConnectNamedPipe thread by signaling the event and clean up event handle
178+
self.shutting_down.store(true, Ordering::SeqCst);
156179
set_event(self.connection_event)?;
157180
close_handle(self.connection_event)
158181
}
@@ -359,4 +382,77 @@ mod test {
359382
}
360383
}
361384
}
385+
386+
#[test]
387+
fn should_accept_new_client() {
388+
let address = r"\\.\pipe\ttrpc-test-accept";
389+
let listener = Arc::new(PipeListener::new(address).unwrap());
390+
391+
let listener_server = listener.clone();
392+
let thread = std::thread::spawn(move || {
393+
let quit_flag = Arc::new(AtomicBool::new(false));
394+
match listener_server.accept(&quit_flag) {
395+
Ok(Some(_)) => {
396+
// pipe is working
397+
}
398+
Ok(None) => {
399+
assert!(false, "should get a working pipe")
400+
}
401+
Err(e) => {
402+
assert!(false, "should not get error {}", e.to_string())
403+
}
404+
}
405+
});
406+
407+
wait_socket_working(address, 10, 5).unwrap();
408+
thread.join().unwrap();
409+
}
410+
411+
#[test]
412+
fn close_should_cancel_accept() {
413+
let listener = Arc::new(PipeListener::new(r"\\.\pipe\ttrpc-test-close").unwrap());
414+
415+
let listener_server = listener.clone();
416+
let thread = std::thread::spawn(move || {
417+
let quit_flag = Arc::new(AtomicBool::new(false));
418+
match listener_server.accept(&quit_flag) {
419+
Ok(_) => {
420+
assert!(false, "should not get pipe on close")
421+
}
422+
Err(e) => {
423+
assert_eq!(e.to_string(), "closing pipe")
424+
}
425+
}
426+
});
427+
428+
// sleep for a moment to allow the pipe to start initialize and be ready to accept new connection.
429+
// this simulates scenario where the thread is asleep and awaiting a connection
430+
std::thread::sleep(std::time::Duration::from_millis(500));
431+
listener.close().unwrap();
432+
thread.join().unwrap();
433+
}
434+
435+
fn wait_socket_working(address: &str, interval_in_ms: u64, count: u32) -> Result<()> {
436+
for _i in 0..count {
437+
let client = match ClientConnection::client_connect(address) {
438+
Ok(c) => {
439+
c
440+
}
441+
Err(_) => {
442+
std::thread::sleep(std::time::Duration::from_millis(interval_in_ms));
443+
continue;
444+
}
445+
};
446+
447+
match client.get_pipe_connection() {
448+
Ok(_) => {
449+
return Ok(());
450+
}
451+
Err(_) => {
452+
std::thread::sleep(std::time::Duration::from_millis(interval_in_ms));
453+
}
454+
}
455+
}
456+
Err(Error::Others("timed out".to_string()))
457+
}
362458
}

0 commit comments

Comments
 (0)