Skip to content

Commit 37eb285

Browse files
committed
Fix a couple of issues in Completion
- librados calls callbacks /after/ setting complete=true, so we have to explicitly wait for callbacks even once the completion is complete. - Could deadlock if completion_callback was called between poll()'s initial completeness check and it setting a Waker
1 parent 6e5875a commit 37eb285

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed

Diff for: src/completion.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ impl Drop for Completion<'_> {
7171

7272
// It is unsound to proceed if the Objecter op is still in flight
7373
assert!(cancel_r == 0 || cancel_r == -libc::ENOENT);
74-
75-
rados_aio_wait_for_complete_and_cb(self.inner);
7674
}
7775
}
7876

77+
unsafe {
78+
// Even if is_complete was true, librados might not be done with
79+
// our callback: wait til it is.
80+
assert_eq!(rados_aio_wait_for_complete_and_cb(self.inner), 0);
81+
}
82+
7983
unsafe {
8084
rados_aio_release(self.inner);
8185
}
@@ -85,16 +89,31 @@ impl Drop for Completion<'_> {
8589
impl std::future::Future for Completion<'_> {
8690
type Output = crate::error::RadosResult<i32>;
8791

88-
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
92+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
93+
// Hold lock across the check of am_complete and subsequent waker registration
94+
// to avoid deadlock if callback is invoked in between.
95+
let mut waker_locked = self.waker.lock().unwrap();
96+
8997
let am_complete = unsafe { rados_aio_is_complete(self.inner) } != 0;
9098

9199
if am_complete {
100+
// Unlock Waker so that completion callback can complete if racing with us.
101+
drop(waker_locked);
102+
103+
// Ensure librados is finished with our callback ('complete' is true
104+
// before it calls that)
105+
unsafe {
106+
let r = rados_aio_wait_for_complete_and_cb(self.inner);
107+
assert_eq!(r, 0);
108+
}
109+
92110
let r = unsafe { rados_aio_get_return_value(self.inner) };
93111
let result = if r < 0 { Err(r.into()) } else { Ok(r) };
112+
94113
std::task::Poll::Ready(result)
95114
} else {
96115
// Register a waker
97-
*self.as_mut().waker.lock().unwrap() = Some(cx.waker().clone());
116+
*waker_locked = Some(cx.waker().clone());
98117

99118
std::task::Poll::Pending
100119
}
@@ -125,6 +144,7 @@ where
125144
};
126145

127146
let ret_code = f(completion);
147+
128148
if ret_code < 0 {
129149
// On error dispatching I/O, drop the unused rados_completion_t
130150
unsafe {

0 commit comments

Comments
 (0)