@@ -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-
8761impl 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 >
126101where
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