@@ -71,11 +71,15 @@ impl Drop for Completion<'_> {
71
71
72
72
// It is unsound to proceed if the Objecter op is still in flight
73
73
assert ! ( cancel_r == 0 || cancel_r == -libc:: ENOENT ) ;
74
-
75
- rados_aio_wait_for_complete_and_cb ( self . inner ) ;
76
74
}
77
75
}
78
76
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
+
79
83
unsafe {
80
84
rados_aio_release ( self . inner ) ;
81
85
}
@@ -85,16 +89,31 @@ impl Drop for Completion<'_> {
85
89
impl std:: future:: Future for Completion < ' _ > {
86
90
type Output = crate :: error:: RadosResult < i32 > ;
87
91
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
+
89
97
let am_complete = unsafe { rados_aio_is_complete ( self . inner ) } != 0 ;
90
98
91
99
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
+
92
110
let r = unsafe { rados_aio_get_return_value ( self . inner ) } ;
93
111
let result = if r < 0 { Err ( r. into ( ) ) } else { Ok ( r) } ;
112
+
94
113
std:: task:: Poll :: Ready ( result)
95
114
} else {
96
115
// Register a waker
97
- * self . as_mut ( ) . waker . lock ( ) . unwrap ( ) = Some ( cx. waker ( ) . clone ( ) ) ;
116
+ * waker_locked = Some ( cx. waker ( ) . clone ( ) ) ;
98
117
99
118
std:: task:: Poll :: Pending
100
119
}
@@ -125,6 +144,7 @@ where
125
144
} ;
126
145
127
146
let ret_code = f ( completion) ;
147
+
128
148
if ret_code < 0 {
129
149
// On error dispatching I/O, drop the unused rados_completion_t
130
150
unsafe {
0 commit comments