157: More robust file implementation r=stjepang a=stjepang

This is a reimplementation of the `File`s state machine.

The previous implementation was simple and a bit naive. It was not fundamentally wrong but had surprises in some corner cases. For example, if an async read operation was started but we timed out on it, the file cursor would move even though we didn't complete the operation. The new implementation will move the cursor only when read/write operations complete successfully.

There was also a deadlock hazard in the case where multiple tasks were concurrently reading or writing to the same file, in which case some task wakeups would be lost. This PR fixes the problem.

A nice consequence of this PR: `futures-channel` is now unused, so we can remove it from the dependency list.

Co-authored-by: Stjepan Glavina <stjepang@gmail.com>
This commit is contained in:
bors[bot] 2019-09-08 08:44:17 +00:00 committed by GitHub
commit a8a2ae9e29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 466 additions and 457 deletions

View file

@ -27,10 +27,9 @@ unstable = []
async-task = "1.0.0" async-task = "1.0.0"
cfg-if = "0.1.9" cfg-if = "0.1.9"
crossbeam-channel = "0.3.9" crossbeam-channel = "0.3.9"
futures-channel-preview = "0.3.0-alpha.18"
futures-core-preview = "0.3.0-alpha.18" futures-core-preview = "0.3.0-alpha.18"
futures-io-preview = "0.3.0-alpha.18" futures-io-preview = "0.3.0-alpha.18"
futures-timer = "0.3.0" futures-timer = "0.4.0"
lazy_static = "1.3.0" lazy_static = "1.3.0"
log = { version = "0.4.8", features = ["kv_unstable"] } log = { version = "0.4.8", features = ["kv_unstable"] }
memchr = "2.2.1" memchr = "2.2.1"

View file

@ -7,7 +7,7 @@ use async_std::io;
use async_std::prelude::*; use async_std::prelude::*;
use async_std::task; use async_std::task;
const LEN: usize = 4 * 1024 * 1024; // 4 Mb const LEN: usize = 16 * 1024; // 16 Kb
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
let path = args().nth(1).expect("missing path argument"); let path = args().nth(1).expect("missing path argument");

File diff suppressed because it is too large Load diff

View file

@ -213,7 +213,11 @@ impl<T: Evented> IoHandle<T> {
let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst));
if (readiness & mask).is_empty() { if (readiness & mask).is_empty() {
self.entry.readers.lock().unwrap().push(cx.waker().clone()); let mut list = self.entry.readers.lock().unwrap();
if list.iter().all(|w| !w.will_wake(cx.waker())) {
list.push(cx.waker().clone());
}
readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst));
} }
@ -250,7 +254,11 @@ impl<T: Evented> IoHandle<T> {
let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst));
if (readiness & mask).is_empty() { if (readiness & mask).is_empty() {
self.entry.writers.lock().unwrap().push(cx.waker().clone()); let mut list = self.entry.writers.lock().unwrap();
if list.iter().all(|w| !w.will_wake(cx.waker())) {
list.push(cx.waker().clone());
}
readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst));
} }