Skip to content

Commit 94a1dd0

Browse files
committed
Completion: only construct via with_completion
Removes need for 'armed' flag - Completion instances always contain a rados_completion_t that has been used to start an operation
1 parent 6cdb0e4 commit 94a1dd0

1 file changed

Lines changed: 49 additions & 38 deletions

File tree

src/completion.rs

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ use crate::rados::{
2525
rados_completion_t,
2626
};
2727

28-
pub struct Completion {
28+
struct Completion {
2929
inner: rados_completion_t,
3030

31-
armed: bool,
32-
3331
// Box to provide a stable address for completion_complete callback
3432
// Mutex to make Sync-safe for write from poll() vs read from completion_complete
3533
waker: Box<std::sync::Mutex<Option<std::task::Waker>>>,
@@ -39,6 +37,8 @@ pub struct Completion {
3937
// to be an Arc rather than a raw rados_ioctx_t because otherwise
4038
// there would be nothing to stop the rados_ioctx_t being invalidated
4139
// during the lifetime of this Completion.
40+
// (AioCompletionImpl does hold a reference to IoCtxImpl for writes, but
41+
// not for reads.)
4242
ioctx: Arc<IoCtx>,
4343
}
4444

@@ -58,39 +58,14 @@ pub extern "C" fn completion_complete(_cb: rados_completion_t, arg: *mut c_void)
5858
}
5959
}
6060

61-
impl Completion {
62-
fn new(ioctx: Arc<IoCtx>) -> Self {
63-
let mut waker = Box::new(Mutex::new(None));
64-
65-
let completion = unsafe {
66-
let mut completion: rados_completion_t = std::ptr::null_mut();
67-
let p: *mut Mutex<Option<Waker>> = &mut *waker;
68-
let p = p as *mut c_void;
69-
70-
let r = rados_aio_create_completion2(p, Some(completion_complete), &mut completion);
71-
if r != 0 {
72-
panic!("Error {} allocating RADOS completion: out of memory?", r);
73-
}
74-
75-
completion
76-
};
77-
78-
Self {
79-
inner: completion,
80-
waker,
81-
ioctx,
82-
armed: false,
83-
}
84-
}
85-
}
86-
8761
impl Drop for Completion {
8862
fn drop(&mut self) {
89-
let am_complete = unsafe { rados_aio_is_complete(self.inner) } != 0;
90-
9163
// Ensure that after dropping the Completion, the AIO callback
92-
// will not be called on our dropped waker Box
93-
if self.armed && !am_complete {
64+
// will not be called on our dropped waker Box. Only necessary
65+
// if we got as far as successfully starting an operation using
66+
// the completion.
67+
let am_complete = unsafe { rados_aio_is_complete(self.inner) } != 0;
68+
if !am_complete {
9469
unsafe {
9570
rados_aio_cancel(self.ioctx.ioctx, self.inner);
9671
rados_aio_wait_for_complete_and_cb(self.inner);
@@ -122,16 +97,52 @@ impl std::future::Future for Completion {
12297
}
12398
}
12499

125-
pub async fn with_completion<F>(ioctx: Arc<IoCtx>, f: F) -> RadosResult<i32>
100+
fn with_completion_impl<F>(ioctx: Arc<IoCtx>, f: F) -> RadosResult<Completion>
126101
where
127102
F: FnOnce(rados_completion_t) -> libc::c_int,
128103
{
129-
let mut completion = Completion::new(ioctx);
130-
let ret_code = f(completion.inner);
104+
let mut waker = Box::new(Mutex::new(None));
105+
106+
let completion = unsafe {
107+
let mut completion: rados_completion_t = std::ptr::null_mut();
108+
let p: *mut Mutex<Option<Waker>> = &mut *waker;
109+
let p = p as *mut c_void;
110+
111+
let r = rados_aio_create_completion2(p, Some(completion_complete), &mut completion);
112+
if r != 0 {
113+
panic!("Error {} allocating RADOS completion: out of memory?", r);
114+
}
115+
116+
completion
117+
};
118+
119+
let ret_code = f(completion);
131120
if ret_code < 0 {
121+
// On error dispatching I/O, drop the unused rados_completion_t
122+
unsafe {
123+
rados_aio_release(completion);
124+
drop(completion)
125+
}
132126
Err(ret_code.into())
133127
} else {
134-
completion.armed = true;
135-
completion.await
128+
// Pass the rados_completion_t into a Future-implementing wrapper and await it.
129+
Ok(Completion {
130+
ioctx,
131+
inner: completion,
132+
waker,
133+
})
136134
}
137135
}
136+
/// Completions are only created via this wrapper, in order to ensure
137+
/// that the Completion struct is only constructed around 'armed' rados_completion_t
138+
/// instances (i.e. those that have been used to start an I/O).
139+
pub async fn with_completion<F>(ioctx: Arc<IoCtx>, f: F) -> RadosResult<i32>
140+
where
141+
F: FnOnce(rados_completion_t) -> libc::c_int,
142+
{
143+
// Hide c_void* temporaries in a non-async function so that the future generated
144+
// by this function isn't encumbered by their non-Send-ness.
145+
let completion = with_completion_impl(ioctx, f)?;
146+
147+
completion.await
148+
}

0 commit comments

Comments
 (0)