Skip to content
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

Follow-up changes for blocking unamed_socket #4099

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions tests/fail-dep/libc/socketpair_closed_while_blocking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@ignore-target: windows # No libc socketpair on Windows

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / coverage report

Unmatched diagnostics outside the testfile

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / coverage report

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / coverage report

expexted error patterns

no message

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

Unmatched diagnostics outside the testfile

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

expexted error patterns

no message

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

Unmatched diagnostics outside the testfile

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

expexted error patterns

no message

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (windows-latest, i686-pc-windows-msvc)

Unmatched diagnostics outside the testfile

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (windows-latest, i686-pc-windows-msvc)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 1 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (windows-latest, i686-pc-windows-msvc)

expexted error patterns

no message
//@compile-flags: -Zmiri-preemption-rate=0

use std::thread;

fn main() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
let arr1: [u8; 212992] = [1; 212992];
// Exhaust the space in the buffer so the subsequent write will block.
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
assert_eq!(res, 212992);
let thread1 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked because the buffer is already full.
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };

Check failure on line 17 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / coverage report

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 17 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 17 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked

Check failure on line 17 in tests/fail-dep/libc/socketpair_closed_while_blocking.rs

View workflow job for this annotation

GitHub Actions / build (windows-latest, i686-pc-windows-msvc)

Unmatched diagnostics

Error: deadlock: the evaluated program deadlocked
assert_eq!(res, 3);
});
let thread2 = thread::spawn(move || {
// Close the socketpair fd while thread1 is blocking on it.
assert_eq!(unsafe { libc::close(fds[0]) }, 0);
// Unblock thread1 by freeing up some space.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(buf, [1, 1, 1]);
});
thread1.join().unwrap();
thread2.join().unwrap();
}
35 changes: 35 additions & 0 deletions tests/fail-dep/libc/socketpair_closed_while_blocking.stderr
Copy link
Contributor Author

@tiif tiif Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the diagnostic already shows deadlock on write because we wake up the blocked write thread only if the fd blocked on write is not closed:

Relevant code snippet:

if let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() {
ecx.check_and_update_readiness(&peer_fd)?;
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
// Unblock all threads that are currently blocked on peer_fd's write.
let waiting_threads =
std::mem::take(&mut *peer_anonsocket.blocked_write_tid.borrow_mut());
// FIXME: We can randomize the order of unblocking.
for thread_id in waiting_threads {
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
}
};

This is acceptable, but I wanted to try if it is possible to let the diagnostic point to close and tell the user that "this thread never wakes up because of the close here".

EDIT: On other note, this also means that throw_unsupported in anonsocket_write is unreachable.

Copy link
Member

@RalfJung RalfJung Dec 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the diagnostic already shows deadlock on write because we wake up the blocked write thread only if the fd blocked on write is not closed:

There's still an odd throw_unsupported in that logic that we should get rid of. Either it is unreachable and should become an assert, or else you can adjust the test to reach it. (I think it is reachable.)

Improving the diagnostics is nice, but getting rid of that throw_unsupported is the more important part IMO.

Copy link
Contributor Author

@tiif tiif Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking a little bit more, I am inclined to think that the unsupported for unnamed_socket read/write might be both unreachable, so I might remove both. But I will add test(s) to reconfirm it.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error: deadlock: the evaluated program deadlocked
|
= note: the evaluated program deadlocked
= note: (no span available)
= note: BACKTRACE on thread `unnamed-ID`:

error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> tests/fail-dep/libc/socketpair_closed_while_blocking.rs:LL:CC
|
LL | thread1.join().unwrap();
| ^^^^^^^^^^^^^^

error: deadlock: the evaluated program deadlocked
--> tests/fail-dep/libc/socketpair_closed_while_blocking.rs:LL:CC
|
LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at tests/fail-dep/libc/socketpair_closed_while_blocking.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 3 previous errors

Loading