-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guarantee that File::write_all
writes all data (or at least tries)
#4316
Changes from all commits
bbe3fb6
1ab9f3a
fc71d43
fc51289
6ff802c
14c3840
137bad0
7ae245d
11b0be4
6555247
52e11f5
ae607e3
0bb4085
34f7c7d
a333992
a31b367
75f487f
f96fa96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,56 @@ fn blocking_shutdown() { | |
}); | ||
} | ||
|
||
#[test] | ||
fn spawn_mandatory_blocking_should_always_run() { | ||
use crate::runtime::tests::loom_oneshot; | ||
loom::model(|| { | ||
let rt = runtime::Builder::new_current_thread().build().unwrap(); | ||
|
||
let (tx, rx) = loom_oneshot::channel(); | ||
let _enter = rt.enter(); | ||
runtime::spawn_blocking(|| {}); | ||
runtime::spawn_mandatory_blocking(move || { | ||
let _ = tx.send(()); | ||
}) | ||
.unwrap(); | ||
|
||
drop(rt); | ||
|
||
// This call will deadlock if `spawn_mandatory_blocking` doesn't run. | ||
let () = rx.recv(); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn spawn_mandatory_blocking_should_run_even_when_shutting_down_from_other_thread() { | ||
use crate::runtime::tests::loom_oneshot; | ||
loom::model(|| { | ||
let rt = runtime::Builder::new_current_thread().build().unwrap(); | ||
let handle = rt.handle().clone(); | ||
|
||
// Drop the runtime in a different thread | ||
{ | ||
loom::thread::spawn(move || { | ||
drop(rt); | ||
}); | ||
} | ||
|
||
let _enter = handle.enter(); | ||
let (tx, rx) = loom_oneshot::channel(); | ||
let handle = runtime::spawn_mandatory_blocking(move || { | ||
let _ = tx.send(()); | ||
}); | ||
Comment on lines
+62
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this test not do an extra There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each test is covering a different race related to shutting down:
I've checked that both tests are useful by changing the implementations slightly and seeing them fail. I could also write a test that covers the combination of both races if you want, but it wouldn't add a lot of value IIUC because both races are independent |
||
|
||
// handle.is_some() means that `spawn_mandatory_blocking` | ||
// promised us to run the blocking task | ||
if handle.is_some() { | ||
// This call will deadlock if `spawn_mandatory_blocking` doesn't run. | ||
let () = rx.recv(); | ||
} | ||
}); | ||
} | ||
|
||
fn mk_runtime(num_threads: usize) -> Runtime { | ||
runtime::Builder::new_multi_thread() | ||
.worker_threads(num_threads) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps include a comment here that it is ok to shut down a mandatory task here without running it because this only happens if the runtime has already started shutdown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think about it, we could still have a race where one worker thread successfully executes:
but the runtime shutdown is scheduled from another thread, and the payload is never written to the file. The return value of
blocking::Spawner::spawn
(which conveys whether the task was actually scheduled) gets ignored in https://github.com/tokio-rs/tokio/pull/4316/files#diff-f1d42d2af51248e5dacb1cc3dd6bba12a956abba0d25567ee67bfd68d27e27d5R266Our test doesn't show this error scenario because the
write
call and the runtime shutdown happen in the same thread, one after the other.Perhaps
spawn_mandatory_blocking
should return an error when it tried to spawn a mandatory task but the runtime was already shutting down. That way, a call towrite
would only succeed if the write were to be actually attempted at shutdown. What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, the current version of this PR (+ the comment you suggested) would already fix the original issue (#4296), so I leave it up to you whether you want to pursue the fix to #4316 (comment) in this PR or in a follow-up PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's simple to fix here, then go ahead and do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've taken a stab at fixing the problem.