Lots of changes

This commit is contained in:
Stjepan Glavina 2020-03-31 18:04:16 +02:00
parent 7966853f12
commit db73031707
26 changed files with 430 additions and 263 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
target/
Cargo.lock
socket

View File

@ -11,7 +11,7 @@ async-task = "1.3.1"
crossbeam-deque = "0.7.3"
crossbeam-queue = "0.2.1"
crossbeam-utils = "0.7.2"
futures = "0.3.4"
futures = { version = "0.3.4", default-features = false, features = ["std"] }
libc = "0.2.68"
once_cell = "1.3.1"
parking_lot = "0.10.0"
@ -27,12 +27,19 @@ nix = "0.17.0"
[target.'cfg(windows)'.dependencies]
wepoll-binding = "1.1.0"
[target.'cfg(windows)'.dev-dependencies]
uds_windows = "0.1.4"
[target.'cfg(target_os = "linux")'.dev-dependencies]
timerfd = "1.1.1"
[dev-dependencies]
anyhow = "1.0.27"
async-h1 = "1.0.1"
async-native-tls = "0.3.3"
async-tungstenite = { version = "0.4.2", features = ["async-native-tls"] }
base64 = "0.12.0"
futures = "0.3.4"
http = "0.2.1"
http-types = "1.0.1"
hyper = { version = "0.13.4", default-features = false, features = ["stream"] }

View File

@ -54,7 +54,7 @@ fn main() -> Result<()> {
async_h1::accept(&http_addr, SharedIo::new(stream), serve).await
})
.unwrap()
.forget();
.detach();
}
res = https.accept().fuse() => {
let (stream, _) = res?;
@ -64,7 +64,7 @@ fn main() -> Result<()> {
async_h1::accept(&https_addr, SharedIo::new(stream), serve).await
})
.unwrap()
.forget();
.detach();
}
}
}

View File

@ -1,19 +1,23 @@
use once_cell::sync::Lazy;
use piper::Receiver;
use signal_hook::iterator::Signals;
use smol::Task;
async fn ctrl_c() {
static RECEIVER: Lazy<Receiver<i32>> = Lazy::new(|| {
let signals = Signals::new(&[signal_hook::SIGINT]).unwrap();
smol::iter(100, Box::leak(Box::new(signals)).forever())
});
RECEIVER.recv().await;
}
static CTRL_C: Lazy<Receiver<()>> = Lazy::new(|| {
let (s, r) = piper::chan(100);
Task::blocking(async move {
for _ in Signals::new(&[signal_hook::SIGINT]).unwrap().forever() {
s.send(()).await;
}
})
.detach();
r
});
fn main() {
smol::run(async {
println!("Waiting for Ctrl-C");
ctrl_c().await;
CTRL_C.recv().await;
println!("Done!");
})
}

View File

@ -0,0 +1,20 @@
use futures::prelude::*;
use smol::Task;
fn unordered<T: Send + 'static>(
iter: impl IntoIterator<Item = impl Future<Output = T> + Send + 'static>,
) -> piper::Receiver<T> {
let v = iter.into_iter().collect::<Vec<_>>();
let (s, r) = piper::chan(v.len());
for f in v {
let s = s.clone();
Task::spawn(async move { s.send(f.await).await }).detach();
}
r
}
fn main() {
smol::run(async {
// TODO
})
}

View File

@ -44,7 +44,7 @@ struct SmolExecutor;
impl<F: Future + Send + 'static> hyper::rt::Executor<F> for SmolExecutor {
fn execute(&self, fut: F) {
Task::spawn(async { drop(fut.await) }).forget();
Task::spawn(async { drop(fut.await) }).detach();
}
}
@ -126,11 +126,13 @@ impl tokio::io::AsyncWrite for SmolStream {
}
}
fn poll_shutdown(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match &mut *self {
SmolStream::Plain(s) => s.get_ref().shutdown(std::net::Shutdown::Write)?,
SmolStream::Tls(_) => {}
SmolStream::Plain(s) => {
s.get_ref().shutdown(std::net::Shutdown::Write)?;
Poll::Ready(Ok(()))
}
SmolStream::Tls(s) => Pin::new(s).poll_close(cx),
}
Poll::Ready(Ok(()))
}
}

View File

@ -60,7 +60,7 @@ struct SmolExecutor;
impl<F: Future + Send + 'static> hyper::rt::Executor<F> for SmolExecutor {
fn execute(&self, fut: F) {
Task::spawn(async { drop(fut.await) }).forget();
Task::spawn(async { drop(fut.await) }).detach();
}
}
@ -163,12 +163,14 @@ impl tokio::io::AsyncWrite for SmolStream {
}
}
fn poll_shutdown(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match &mut *self {
SmolStream::Plain(s) => s.get_ref().shutdown(std::net::Shutdown::Write)?,
SmolStream::Tls(_) => {}
SmolStream::Handshake(_) => {}
SmolStream::Plain(s) => {
s.get_ref().shutdown(std::net::Shutdown::Write)?;
Poll::Ready(Ok(()))
}
SmolStream::Tls(s) => Pin::new(s).poll_close(cx),
SmolStream::Handshake(_) => Poll::Ready(Ok(())),
}
Poll::Ready(Ok(()))
}
}

42
examples/linux-timerfd.rs Normal file
View File

@ -0,0 +1,42 @@
#[cfg(target_os = "linux")]
fn main() -> std::io::Result<()> {
use std::io;
use std::os::unix::io::AsRawFd;
use std::time::{Duration, Instant};
use smol::Async;
use timerfd::{SetTimeFlags, TimerFd, TimerState};
/// Converts a `nix::Error` into `std::io::Error`.
fn io_err(err: nix::Error) -> io::Error {
match err {
nix::Error::Sys(code) => code.into(),
err => io::Error::new(io::ErrorKind::Other, Box::new(err)),
}
}
async fn sleep(dur: Duration) -> io::Result<()> {
// Create a timer.
let mut timer = TimerFd::new()?;
timer.set_state(TimerState::Oneshot(dur), SetTimeFlags::Default);
// When the timer fires, a 64-bit integer can be read from it.
Async::new(timer)?
.read_with(|timer| nix::unistd::read(timer.as_raw_fd(), &mut [0u8; 8]).map_err(io_err))
.await?;
Ok(())
}
smol::run(async {
let start = Instant::now();
println!("Sleeping...");
sleep(Duration::from_secs(1)).await?;
println!("Woke up after {:?}", start.elapsed());
Ok(())
})
}
#[cfg(not(target_os = "linux"))]
fn main() {
println!("This example works only on Linux!");
}

View File

@ -1,3 +1,5 @@
//! TODO
fn main() {}
fn main() {
// TODO: lazy reader from a process
}

View File

@ -10,7 +10,7 @@ fn main() -> io::Result<()> {
let path = env::args().nth(1).expect("missing path argument");
smol::run(async move {
let mut dir = smol::iter(10_000, smol::blocking!(fs::read_dir(path))?);
let mut dir = smol::iter(smol::blocking!(fs::read_dir(path))?);
while let Some(res) = dir.next().await {
println!("{}", res?.file_name().to_string_lossy());

View File

@ -1,13 +1,17 @@
//! Prints a file given as an argument to stdout.
use std::{env, fs, io};
use std::fs::File;
use std::io;
use std::env;
fn main() -> io::Result<()> {
let path = env::args().nth(1).expect("missing path argument");
smol::run(async {
let contents = smol::blocking!(fs::read_to_string(path))?;
println!("{}", contents);
let file = smol::reader(File::open(path)?);
let mut stdout = smol::writer(io::stdout());
futures::io::copy(file, &mut stdout).await?;
Ok(())
})
}

View File

@ -9,17 +9,18 @@
// 2. openssl pkcs12 -export -out identity.pfx -inkey localhost/key.pem -in localhost/cert.pem
use std::fs;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::net::{TcpListener, TcpStream};
use std::path::Path;
use anyhow::Result;
use async_native_tls::TlsAcceptor;
use futures::prelude::*;
use smol::{Async, Task, blocking};
use smol::{blocking, Async, Task};
const RESPONSE: &[u8] = br#"
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 47
<!DOCTYPE html><html><body>Hello!</body></html>
"#;
@ -29,7 +30,6 @@ async fn serve(mut stream: Async<TcpStream>, tls: Option<TlsAcceptor>) -> Result
None => {
println!("Serving http://{}", stream.get_ref().local_addr()?);
stream.write_all(RESPONSE).await?;
stream.get_ref().shutdown(Shutdown::Both)?;
}
Some(tls) => {
println!("Serving https://{}", stream.get_ref().local_addr()?);
@ -57,11 +57,11 @@ fn main() -> Result<()> {
futures::select! {
res = http.accept().fuse() => {
let (stream, _) = res?;
Task::spawn(serve(stream, None)).unwrap().forget();
Task::spawn(serve(stream, None)).unwrap().detach();
}
res = https.accept().fuse() => {
let (stream, _) = res?;
Task::spawn(serve(stream, Some(tls.clone()))).unwrap().forget();
Task::spawn(serve(stream, Some(tls.clone()))).unwrap().detach();
}
}
}

View File

@ -0,0 +1,11 @@
use futures::io;
fn main() -> io::Result<()> {
smol::run(async {
let stdin = smol::reader(std::io::stdin());
let mut stdout = smol::writer(std::io::stdout());
io::copy(stdin, &mut stdout).await?;
Ok(())
})
}

View File

@ -1,37 +0,0 @@
use std::process::{Command, Stdio};
use futures::io;
use futures::prelude::*;
use smol::Task;
fn stdin() -> impl AsyncRead + Unpin + 'static {
let mut child = Command::new("cat")
.stdin(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to execute command");
let mut out = child.stdout.take().unwrap();
let (reader, mut writer) = piper::pipe(2); // TODO
Task::blocking(async move {
io::copy(io::AllowStdIo::new(out), &mut writer).await;
})
.forget();
// TODO todo!("return a wrapped Reader that calls child.kill() on drop");
reader
}
// TODO: just pipe stdin to stdout
fn main() -> io::Result<()> {
smol::run(async {
let mut stdin = io::BufReader::new(stdin());
let mut line = String::new();
stdin.read_line(&mut line).await?;
dbg!(line);
Ok(())
})
}

View File

@ -1,19 +0,0 @@
use std::env;
use std::net::TcpStream;
use futures::io;
use smol::Async;
fn main() -> io::Result<()> {
let mut args = env::args().skip(1);
let addr = args.next().expect("missing address argument");
let port = args.next().expect("missing port argument");
smol::run(async {
let mut input = smol::reader(5 * 1024 * 1024, std::io::stdin());
let mut stream = Async::<TcpStream>::connect(format!("{}:{}", addr, port)).await?;
io::copy(&mut input, &mut stream).await?;
Ok(())
})
}

22
examples/tcp-client.rs Normal file
View File

@ -0,0 +1,22 @@
use std::net::TcpStream;
use futures::io;
use futures::prelude::*;
use smol::Async;
fn main() -> io::Result<()> {
smol::run(async {
let stdin = smol::reader(std::io::stdin());
let mut stdout = smol::writer(std::io::stdout());
let stream = Async::<TcpStream>::connect("localhost:7000").await?;
println!("Connected to {}", stream.get_ref().peer_addr()?);
future::try_join(
io::copy(stdin, &mut &stream),
io::copy(&stream, &mut stdout),
)
.await?;
Ok(())
})
}

View File

@ -8,23 +8,23 @@
use std::net::{TcpListener, TcpStream};
use futures::prelude::*;
use futures::io;
use smol::{Async, Task};
async fn echo(stream: Async<TcpStream>) -> io::Result<()> {
println!("Copying");
io::copy(&stream, &mut &stream).await?;
Ok(())
}
fn main() -> io::Result<()> {
smol::run(async {
let listener = Async::<TcpListener>::bind("127.0.0.1:8080")?;
let listener = Async::<TcpListener>::bind("127.0.0.1:7000")?;
println!("Listening on http://{}", listener.get_ref().local_addr()?);
loop {
let (stream, _) = listener.accept().await?;
Task::spawn(echo(stream)).unwrap().forget();
Task::spawn(echo(stream)).unwrap().detach();
}
})
}

View File

@ -9,7 +9,8 @@ async fn sleep(dur: Duration) {
fn main() {
smol::run(async {
let start = Instant::now();
println!("Sleeping...");
sleep(Duration::from_secs(1)).await;
dbg!(start.elapsed());
println!("Woke up after {:?}", start.elapsed());
})
}

View File

@ -14,7 +14,7 @@ async fn timeout<T>(dur: Duration, f: impl Future<Output = T>) -> Result<T> {
fn main() -> Result<()> {
smol::run(async {
let mut stdin = BufReader::new(smol::reader(16 * 1024, std::io::stdin()));
let mut stdin = BufReader::new(smol::reader(std::io::stdin()));
let mut line = String::new();
let dur = Duration::from_secs(5);

View File

@ -45,14 +45,14 @@ fn main() -> Result<()> {
let (stream, _) = res?;
let stream = WsStream::Plain(async_tungstenite::accept_async(stream).await?);
let ws_host = ws_host.clone();
Task::spawn(serve(stream, ws_host)).unwrap().forget();
Task::spawn(serve(stream, ws_host)).unwrap().detach();
}
res = wss.accept().fuse() => {
let (stream, _) = res?;
let stream = tls.accept(stream).await?;
let stream = WsStream::Tls(async_tungstenite::accept_async(stream).await?);
let wss_host = wss_host.clone();
Task::spawn(serve(stream, wss_host)).unwrap().forget();
Task::spawn(serve(stream, wss_host)).unwrap().detach();
}
}
}

46
examples/windows-uds.rs Normal file
View File

@ -0,0 +1,46 @@
#[cfg(windows)]
fn main() -> std::io::Result<()> {
use std::fs;
use std::path::{Path, PathBuf};
use futures::io;
use futures::prelude::*;
use smol::{Async, Task};
use uds_windows::{UnixListener, UnixStream};
async fn client(addr: PathBuf) -> io::Result<()> {
let stream = Async::new(UnixStream::connect(addr)?)?;
println!("Connected to {:?}", stream.get_ref().peer_addr()?);
let mut stdout = smol::writer(std::io::stdout());
io::copy(&stream, &mut stdout).await?;
Ok(())
}
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("socket");
let _ = fs::remove_file(&path);
smol::run(async {
// Create a listener.
let listener = Async::new(UnixListener::bind(&path)?)?;
println!("Listening on {:?}", listener.get_ref().local_addr()?);
// Spawn a client task.
let task = Task::spawn(client(path.clone()));
// Accept the client.
let (stream, _) = listener.read_with(|l| l.accept()).await?;
println!("Accepted a client");
// Send a message, drop the stream, and wait for the client.
Async::new(stream)?.write_all(b"Hello!\n").await?;
task.await?;
Ok(())
})
}
#[cfg(not(windows))]
fn main() {
println!("This example works only on Windows!");
}

View File

@ -8,4 +8,4 @@ license = "MIT OR Apache-2.0"
[dependencies]
crossbeam-utils = "0.7.0"
futures = { version = "0.3.4", features = ["async-await"] }
futures = { version = "0.3.4", default-features = false, features = ["std"] }

View File

@ -1,3 +1,5 @@
//! Asynchronous pipes, channels, and mutexes.
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
mod chan;
@ -8,4 +10,3 @@ mod signal;
pub use chan::{chan, Receiver, Sender};
pub use mutex::{Mutex, MutexGuard};
pub use pipe::{pipe, Reader, Writer};
pub use signal::{Signal, SignalListener};

View File

@ -10,6 +10,7 @@ use std::task::{Context, Poll};
use futures::io::{AsyncRead, AsyncWrite};
use futures::task::AtomicWaker;
/// Creates a bounded single-producer single-consumer pipe.
pub fn pipe(cap: usize) -> (Reader, Writer) {
assert!(cap > 0, "capacity must be positive");
@ -44,12 +45,14 @@ pub fn pipe(cap: usize) -> (Reader, Writer) {
// NOTE: Reader and Writer are !Clone + !Sync
/// The reading side of a pipe.
pub struct Reader {
inner: Arc<Inner>,
head: Cell<usize>,
tail: Cell<usize>,
}
/// The writing side of a pipe.
pub struct Writer {
inner: Arc<Inner>,
head: Cell<usize>,

View File

@ -9,8 +9,6 @@ use std::ptr::NonNull;
use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
use std::task::{Context, Poll, Waker};
use std::thread::{self, Thread};
use std::time::{Duration, Instant};
/// Set when there is at least one watcher that has already been notified.
const NOTIFIED: usize = 1 << 0;
@ -22,7 +20,7 @@ struct Inner {
/// Holds three bits: `LOCKED`, `NOTIFIED`, and `NOTIFIABLE`.
flags: AtomicUsize,
/// A linked list holding blocked tasks or threads.
/// A linked list holding blocked tasks.
list: Mutex<List>,
}
@ -152,71 +150,6 @@ pub struct SignalListener {
unsafe impl Send for SignalListener {}
unsafe impl Sync for SignalListener {}
impl SignalListener {
/// Blocks until a notification is received.
pub fn wait(self) {
self.wait_internal(None);
}
/// Blocks until a notification is received or the timeout is reached.
///
/// Returns `true` if a notification was received.
pub fn wait_timeout(self, timeout: Duration) -> bool {
self.wait_internal(Some(timeout))
}
/// Blocks the current thread with an optional timeout.
///
/// Returns `true` if a notification was received.
fn wait_internal(mut self, mut timeout: Option<Duration>) -> bool {
let entry = match self.entry.take() {
None => unreachable!("cannot wait twice on a `SignalListener`"),
Some(entry) => entry,
};
{
let mut inner = self.inner.lock();
let e = unsafe { entry.as_ref() };
match e.state.replace(State::Notified) {
State::Notified => return inner.remove(entry).is_notified(),
_ => e.state.set(State::Waiting(thread::current())),
}
}
loop {
match &mut timeout {
None => thread::park(),
Some(t) => {
let now = Instant::now();
thread::park_timeout(*t);
let elapsed = now.elapsed();
if elapsed >= *t {
*t = Duration::from_secs(0);
} else {
*t -= elapsed;
}
}
}
let mut inner = self.inner.lock();
let e = unsafe { entry.as_ref() };
if let Some(t) = timeout {
if t == Duration::from_secs(0) {
return inner.remove(entry).is_notified();
}
}
match e.state.replace(State::Notified) {
State::Notified => return inner.remove(entry).is_notified(),
state => e.state.set(state),
}
}
}
}
impl Future for SignalListener {
type Output = ();
@ -239,9 +172,6 @@ impl Future for SignalListener {
}
State::Created => state.set(State::Polling(cx.waker().clone())),
State::Polling(w) => state.set(State::Polling(w)),
State::Waiting(_) => {
unreachable!("cannot poll and wait on `SignalListener` at the same time")
}
}
Poll::Pending
@ -312,16 +242,13 @@ enum State {
/// A task is polling it.
Polling(Waker),
/// A thread is blocked on it.
Waiting(Thread),
}
impl State {
fn is_notified(&self) -> bool {
match self {
State::Notified => true,
State::Created | State::Polling(_) | State::Waiting(_) => false,
State::Created | State::Polling(_) => false,
}
}
}
@ -418,7 +345,6 @@ impl List {
State::Notified => {}
State::Created => {}
State::Polling(w) => w.wake(),
State::Waiting(t) => t.unpark(),
}
if !is_notified {

View File

@ -1,6 +1,5 @@
//! A very smol and fast async runtime.
#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#[cfg(not(any(
@ -33,10 +32,16 @@ use std::time::{Duration, Instant};
#[cfg(unix)]
use std::{
os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream},
os::unix::{
io::{AsRawFd, RawFd},
net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream},
},
path::Path,
};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, RawSocket};
use crossbeam_deque::{Injector, Steal, Stealer, Worker};
use crossbeam_queue::SegQueue;
use crossbeam_utils::sync::{Parker, ShardedLock};
@ -53,7 +58,7 @@ use socket2::{Domain, Protocol, Socket, Type};
type Runnable = async_task::Task<()>;
/// A spawned future.
#[must_use = "tasks are canceled when dropped, use `.forget()` to run in the background"]
#[must_use = "tasks are canceled when dropped, use `.detach()` to run in the background"]
#[derive(Debug)]
pub struct Task<T>(Option<async_task::JoinHandle<T, ()>>);
@ -84,8 +89,8 @@ impl<T: 'static> Task<T> {
}
impl Task<()> {
/// Moves the task into the background.
pub fn forget(mut self) {
/// Detaches the task to keep running in the background.
pub fn detach(mut self) {
self.0.take().unwrap();
}
}
@ -158,7 +163,7 @@ pub fn run<T>(future: impl Future<Output = T>) -> T {
EXECUTOR.processor(|proc| {
let flag = Arc::new(
IoFlag::create()
.and_then(Async::nonblocking)
.and_then(Async::new)
.expect("cannot create waker flag"),
);
@ -440,18 +445,23 @@ static THREAD_POOL: Lazy<ThreadPool> = Lazy::new(|| ThreadPool {
cvar: Condvar::new(),
});
/// A thread pool for blocking tasks.
struct ThreadPool {
state: Mutex<State>,
cvar: Condvar,
}
struct State {
/// Number of sleeping threads in the pool.
idle_count: usize,
/// Total number of thread in the pool.
thread_count: usize,
/// Runnable blocking tasks.
queue: VecDeque<Runnable>,
}
impl ThreadPool {
/// Spawns a blocking task onto the thread pool.
fn spawn<T: Send + 'static>(
&'static self,
future: impl Future<Output = T> + Send + 'static,
@ -461,36 +471,44 @@ impl ThreadPool {
Task(Some(handle))
}
/// Runs the main loop on the current thread.
fn run(&'static self) {
let mut state = self.state.lock();
loop {
state.idle_count -= 1;
// Run tasks in the queue.
while let Some(runnable) = state.queue.pop_front() {
self.spawn_more(state);
let _ = catch_unwind(|| runnable.run());
state = self.state.lock();
}
// Put the thread to sleep until another task is scheduled.
state.idle_count += 1;
let timeout = Duration::from_millis(500);
if self.cvar.wait_for(&mut state, timeout).timed_out() {
state.idle_count -= 1;
state.thread_count -= 1;
self.spawn_more(state);
break;
if state.queue.is_empty() {
// If there are no tasks after a while, stop this thread.
state.idle_count -= 1;
state.thread_count -= 1;
break;
}
}
}
}
/// Schedules a runnable task for execution.
fn schedule(&'static self, runnable: Runnable) {
let mut state = self.state.lock();
state.queue.push_back(runnable);
// Notify a sleeping thread and spawn more threads if needed.
self.cvar.notify_one();
self.spawn_more(state);
}
/// Spawns more blocking threads if the pool is overloaded with work.
fn spawn_more(&'static self, mut state: MutexGuard<'static, State>) {
// If runnable tasks greatly outnumber idle threads and there aren't too many threads
// already, then be aggressive: wake all idle threads and spawn one more thread.
@ -511,42 +529,163 @@ macro_rules! blocking {
};
}
/// Spawns a blocking iterator onto a thread.
pub fn iter<I>(cap: usize, iter: I) -> piper::Receiver<I::Item>
where
I: Iterator + Send + 'static,
I::Item: Send,
{
let (s, r) = piper::chan(cap);
Task::blocking(async move {
for item in iter {
s.send(item).await;
/// Creates an iterator that runs on a thread.
pub fn iter<T: Send + 'static>(
iter: impl Iterator<Item = T> + Send + 'static,
) -> impl Stream<Item = T> + Send + Unpin + 'static {
enum State<T, I> {
Idle(Option<I>),
Busy(piper::Receiver<T>, Task<I>),
}
impl<T, I> Unpin for State<T, I> {}
impl<T: Send + 'static, I: Iterator<Item = T> + Send + 'static> Stream for State<T, I> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
match &mut *self {
State::Idle(iter) => {
let mut iter = iter.take().unwrap();
let (sender, receiver) = piper::chan(8 * 1024);
let task = Task::blocking(async move {
for item in &mut iter {
sender.send(item).await;
}
iter
});
*self = State::Busy(receiver, task);
self.poll_next(cx)
}
State::Busy(receiver, task) => {
let opt = futures::ready!(Pin::new(receiver).poll_next(cx));
if opt.is_none() {
// At the end of stream, retrieve the iterator back.
let iter = futures::ready!(Pin::new(task).poll(cx));
*self = State::Idle(Some(iter));
}
Poll::Ready(opt)
}
}
}
})
.forget();
r
}
State::Idle(Some(iter))
}
/// Spawns a blocking reader onto a thread.
pub fn reader<R>(cap: usize, reader: R) -> piper::Reader
where
R: Read + Send + 'static,
{
let io = AllowStdIo::new(reader);
let (r, mut w) = piper::pipe(cap);
Task::blocking(async move { drop(futures::io::copy(io, &mut w).await) }).forget();
r
/// Creates a reader that runs on a thread.
pub fn reader(reader: impl Read + Send + 'static) -> impl AsyncRead + Send + Unpin + 'static {
enum State<T> {
Idle(Option<T>),
Busy(piper::Reader, Task<(io::Result<()>, T)>),
}
impl<T: AsyncRead + Send + Unpin + 'static> AsyncRead for State<T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match &mut *self {
State::Idle(io) => {
let mut io = io.take().unwrap();
let (reader, mut writer) = piper::pipe(8 * 1024 * 1024); // 8 MB
let task = Task::blocking(async move {
let res = futures::io::copy(&mut io, &mut writer).await;
(res.map(drop), io)
});
*self = State::Busy(reader, task);
self.poll_read(cx, buf)
}
State::Busy(reader, task) => {
let n = futures::ready!(Pin::new(reader).poll_read(cx, buf))?;
if n == 0 {
// At the end of stream, retrieve the reader back.
let (res, io) = futures::ready!(Pin::new(task).poll(cx));
*self = State::Idle(Some(io));
res?;
}
Poll::Ready(Ok(n))
}
}
}
}
let io = Box::pin(AllowStdIo::new(reader));
State::Idle(Some(io))
}
/// Spawns a blocking writer onto a thread.
pub fn writer<W>(cap: usize, writer: W) -> piper::Writer
where
W: Write + Send + 'static,
{
let mut io = AllowStdIo::new(writer);
let (r, w) = piper::pipe(cap);
Task::blocking(async move { drop(futures::io::copy(r, &mut io).await) }).forget();
w
/// Creates a writer that runs on a thread.
pub fn writer(writer: impl Write + Send + 'static) -> impl AsyncWrite + Send + Unpin + 'static {
enum State<T> {
Idle(Option<T>),
Busy(Option<piper::Writer>, Task<(io::Result<()>, T)>),
}
impl<T: AsyncWrite + Send + Unpin + 'static> State<T> {
fn start(&mut self) {
if let State::Idle(io) = self {
if let Some(mut io) = io.take() {
let (reader, writer) = piper::pipe(8 * 1024 * 1024); // 8 MB
let task = Task::blocking(async move {
match futures::io::copy(reader, &mut io).await {
Ok(_) => (io.flush().await, io),
Err(err) => (Err(err), io),
}
});
*self = State::Busy(Some(writer), task);
}
}
}
}
impl<T: AsyncWrite + Send + Unpin + 'static> AsyncWrite for State<T> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
loop {
match &mut *self {
State::Idle(None) => return Poll::Ready(Ok(0)),
State::Idle(Some(_)) => self.start(),
State::Busy(None, task) => {
// The writing end of the pipe is closed, so await the task.
let (res, io) = futures::ready!(Pin::new(task).poll(cx));
*self = State::Idle(Some(io));
res?;
}
State::Busy(Some(writer), _) => return Pin::new(writer).poll_write(cx, buf),
}
}
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
loop {
match &mut *self {
State::Idle(None) => return Poll::Ready(Ok(())),
State::Idle(Some(_)) => self.start(),
State::Busy(writer, task) => {
// Close the writing end of the pipe and await the task.
writer.take();
let (res, io) = futures::ready!(Pin::new(task).poll(cx));
*self = State::Idle(Some(io));
return Poll::Ready(res);
}
}
}
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
// Flush and then drop the I/O handle.
futures::ready!(Pin::new(&mut *self).poll_flush(cx))?;
*self = State::Idle(None);
Poll::Ready(Ok(()))
}
}
let io = AllowStdIo::new(writer);
State::Idle(Some(io))
}
// ----- Reactor -----
@ -555,10 +694,11 @@ static REACTOR: Lazy<Reactor> = Lazy::new(|| Reactor::create().expect("cannot cr
static INTERRUPT: Lazy<Async<IoFlag>> = Lazy::new(|| {
IoFlag::create()
.and_then(Async::nonblocking)
.and_then(Async::new)
.expect("cannot create interrupt flag")
});
/// A source of I/O events.
#[derive(Debug)]
struct Source {
raw: sys::Raw,
@ -567,6 +707,7 @@ struct Source {
writers: Mutex<Vec<Waker>>,
}
/// The async I/O and timers driver.
struct Reactor {
sys: sys::Reactor,
sources: Mutex<Slab<Arc<Source>>>,
@ -589,18 +730,14 @@ impl Reactor {
fn register(&self, raw: sys::Raw) -> io::Result<Arc<Source>> {
let mut sources = self.sources.lock();
let vacant = sources.vacant_entry();
let index = vacant.key();
self.sys.register(raw, index)?;
let source = Arc::new(Source {
raw,
index,
index: vacant.key(),
readers: Mutex::new(Vec::new()),
writers: Mutex::new(Vec::new()),
});
vacant.insert(source.clone());
Ok(source)
self.sys.register(raw, source.index)?;
Ok(vacant.insert(source).clone())
}
/// Deregisters an I/O source from the reactor.
@ -816,9 +953,14 @@ pub struct Async<T> {
}
#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> Async<T> {
impl<T: AsRawFd> Async<T> {
/// Converts a non-blocking I/O handle into an async I/O handle.
pub fn nonblocking(inner: T) -> io::Result<Async<T>> {
pub fn new(inner: T) -> io::Result<Async<T>> {
nix::fcntl::fcntl(
inner.as_raw_fd(),
nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK),
)
.unwrap(); // TODO unwrap and windows
Ok(Async {
source: REACTOR.register(sys::Raw::new(&inner))?,
inner: Some(Box::new(inner)),
@ -827,9 +969,12 @@ impl<T: std::os::unix::io::AsRawFd> Async<T> {
}
#[cfg(windows)]
impl<T: std::os::windows::io::AsRawSocket> Async<T> {
impl<T: AsRawSocket> Async<T> {
/// Converts a non-blocking I/O handle into an async I/O handle.
pub fn nonblocking(inner: T) -> io::Result<Async<T>> {
pub fn new(inner: T) -> io::Result<Async<T>> {
let socket = unsafe { Socket::from_raw_socket(inner.as_raw_socket()) };
mem::ManuallyDrop::new(socket).set_nonblocking(true)?;
Ok(Async {
source: REACTOR.register(sys::Raw::new(&inner))?,
inner: Some(Box::new(inner)),
@ -977,8 +1122,8 @@ impl<T: Write> AsyncWrite for Async<T> {
fut.poll(cx)
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
@ -1002,24 +1147,22 @@ where
fut.poll(cx)
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
impl Async<TcpListener> {
/// Creates a listener bound to the specified address.
pub fn bind<A: ToString>(addr: A) -> io::Result<Async<TcpListener>> {
let listener = TcpListener::bind(addr.to_string().parse::<SocketAddr>().unwrap())?;
listener.set_nonblocking(true)?;
Ok(Async::nonblocking(listener)?)
// TODO: unwrap
TcpListener::bind(addr.to_string().parse::<SocketAddr>().unwrap()).and_then(Async::new)
}
/// Accepts a new incoming connection.
pub async fn accept(&self) -> io::Result<(Async<TcpStream>, SocketAddr)> {
let (stream, addr) = self.read_with(|inner| inner.accept()).await?;
stream.set_nonblocking(true)?;
Ok((Async::nonblocking(stream)?, addr))
Ok((Async::new(stream)?, addr))
}
/// Returns a stream over incoming connections.
@ -1053,7 +1196,7 @@ impl Async<TcpStream> {
// Begin async connect and ignore the inevitable "not yet connected" error.
socket.set_nonblocking(true)?;
let _ = socket.connect(&addr.into());
let stream = Async::nonblocking(socket.into_tcp_stream())?;
let stream = Async::new(socket.into_tcp_stream())?;
// Wait for connect to complete.
let wait_connect = |mut stream: &TcpStream| match stream.write(&[]) {
@ -1077,9 +1220,8 @@ impl Async<TcpStream> {
impl Async<UdpSocket> {
/// Creates a socket bound to the specified address.
pub fn bind<A: ToString>(addr: A) -> io::Result<Async<UdpSocket>> {
let socket = UdpSocket::bind(addr.to_string().parse::<SocketAddr>().unwrap())?;
socket.set_nonblocking(true)?;
Ok(Async::nonblocking(socket)?)
// TODO unwrap()
UdpSocket::bind(addr.to_string().parse::<SocketAddr>().unwrap()).and_then(Async::new)
}
/// Sends data to the specified address.
@ -1118,16 +1260,13 @@ impl Async<UdpSocket> {
impl Async<UnixListener> {
/// Creates a listener bound to the specified path.
pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<Async<UnixListener>> {
let listener = UnixListener::bind(path)?;
listener.set_nonblocking(true)?;
Ok(Async::nonblocking(listener)?)
UnixListener::bind(path).and_then(Async::new)
}
/// Accepts a new incoming connection.
pub async fn accept(&self) -> io::Result<(Async<UnixStream>, UnixSocketAddr)> {
let (stream, addr) = self.read_with(|inner| inner.accept()).await?;
stream.set_nonblocking(true)?;
Ok((Async::nonblocking(stream)?, addr))
Ok((Async::new(stream)?, addr))
}
/// Returns a stream over incoming connections.
@ -1145,17 +1284,13 @@ impl Async<UnixListener> {
impl Async<UnixStream> {
/// Connects to the specified path.
pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<Async<UnixStream>> {
let stream = UnixStream::connect(path)?;
stream.set_nonblocking(true)?;
Ok(Async::nonblocking(stream)?)
UnixStream::connect(path).and_then(Async::new)
}
/// Creates an unnamed pair of connected streams.
pub fn pair() -> io::Result<(Async<UnixStream>, Async<UnixStream>)> {
let (stream1, stream2) = UnixStream::pair()?;
stream1.set_nonblocking(true)?;
stream2.set_nonblocking(true)?;
Ok((Async::nonblocking(stream1)?, Async::nonblocking(stream2)?))
Ok((Async::new(stream1)?, Async::new(stream2)?))
}
}
@ -1163,24 +1298,18 @@ impl Async<UnixStream> {
impl Async<UnixDatagram> {
/// Creates a socket bound to the specified path.
pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<Async<UnixDatagram>> {
let socket = UnixDatagram::bind(path)?;
socket.set_nonblocking(true)?;
Ok(Async::nonblocking(socket)?)
UnixDatagram::bind(path).and_then(Async::new)
}
/// Creates a socket not bound to any address.
pub fn unbound() -> io::Result<Async<UnixDatagram>> {
let socket = UnixDatagram::unbound()?;
socket.set_nonblocking(true)?;
Ok(Async::nonblocking(socket)?)
UnixDatagram::unbound().and_then(Async::new)
}
/// Creates an unnamed pair of connected sockets.
pub fn pair() -> io::Result<(Async<UnixDatagram>, Async<UnixDatagram>)> {
let (socket1, socket2) = UnixDatagram::pair()?;
socket1.set_nonblocking(true)?;
socket2.set_nonblocking(true)?;
Ok((Async::nonblocking(socket1)?, Async::nonblocking(socket2)?))
Ok((Async::new(socket1)?, Async::new(socket2)?))
}
/// Sends data to the specified address.
@ -1300,15 +1429,15 @@ impl IoFlag {
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for IoFlag {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
impl AsRawFd for IoFlag {
fn as_raw_fd(&self) -> RawFd {
self.socket_wakeup.as_raw_fd()
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawSocket for IoFlag {
fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
impl AsRawSocket for IoFlag {
fn as_raw_socket(&self) -> RawSocket {
self.socket_wakeup.as_raw_socket()
}
}