1use std::io;
2
3#[cfg(windows)]
4async fn windows_main() -> io::Result<()> {
5 use tokio::io::Interest;
6 use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
7
8 const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
9
10 let server = ServerOptions::new().create(PIPE_NAME)?;
11
12 let server = tokio::spawn(async move {
13 // Note: we wait for a client to connect.
14 server.connect().await?;
15
16 let buf = {
17 let mut read_buf = [0u8; 5];
18 let mut read_buf_cursor = 0;
19
20 loop {
21 server.readable().await?;
22
23 let buf = &mut read_buf[read_buf_cursor..];
24
25 match server.try_read(buf) {
26 Ok(n) => {
27 read_buf_cursor += n;
28
29 if read_buf_cursor == read_buf.len() {
30 break;
31 }
32 }
33 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
34 continue;
35 }
36 Err(e) => {
37 return Err(e);
38 }
39 }
40 }
41
42 read_buf
43 };
44
45 {
46 let write_buf = b"pong\n";
47 let mut write_buf_cursor = 0;
48
49 loop {
50 let buf = &write_buf[write_buf_cursor..];
51
52 if buf.is_empty() {
53 break;
54 }
55
56 server.writable().await?;
57
58 match server.try_write(buf) {
59 Ok(n) => {
60 write_buf_cursor += n;
61 }
62 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
63 continue;
64 }
65 Err(e) => {
66 return Err(e);
67 }
68 }
69 }
70 }
71
72 Ok::<_, io::Error>(buf)
73 });
74
75 let client = tokio::spawn(async move {
76 // There's no need to use a connect loop here, since we know that the
77 // server is already up - `open` was called before spawning any of the
78 // tasks.
79 let client = ClientOptions::new().open(PIPE_NAME)?;
80
81 let mut read_buf = [0u8; 5];
82 let mut read_buf_cursor = 0;
83 let write_buf = b"ping\n";
84 let mut write_buf_cursor = 0;
85
86 loop {
87 let mut interest = Interest::READABLE;
88 if write_buf_cursor < write_buf.len() {
89 interest |= Interest::WRITABLE;
90 }
91
92 let ready = client.ready(interest).await?;
93
94 if ready.is_readable() {
95 let buf = &mut read_buf[read_buf_cursor..];
96
97 match client.try_read(buf) {
98 Ok(n) => {
99 read_buf_cursor += n;
100
101 if read_buf_cursor == read_buf.len() {
102 break;
103 }
104 }
105 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
106 continue;
107 }
108 Err(e) => {
109 return Err(e);
110 }
111 }
112 }
113
114 if ready.is_writable() {
115 let buf = &write_buf[write_buf_cursor..];
116
117 if buf.is_empty() {
118 continue;
119 }
120
121 match client.try_write(buf) {
122 Ok(n) => {
123 write_buf_cursor += n;
124 }
125 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
126 continue;
127 }
128 Err(e) => {
129 return Err(e);
130 }
131 }
132 }
133 }
134
135 let buf = String::from_utf8_lossy(&read_buf).into_owned();
136
137 Ok::<_, io::Error>(buf)
138 });
139
140 let (server, client) = tokio::try_join!(server, client)?;
141
142 assert_eq!(server?, *b"ping\n");
143 assert_eq!(client?, "pong\n");
144
145 Ok(())
146}
147
148#[tokio::main]
149async fn main() -> io::Result<()> {
150 #[cfg(windows)]
151 {
152 windows_main().await?;
153 }
154
155 #[cfg(not(windows))]
156 {
157 println!("Named pipes are only supported on Windows!");
158 }
159
160 Ok(())
161}
162