@@ -12,26 +12,28 @@ use crate::{
1212 error:: { Error , Result } ,
1313 ffi:: c_void,
1414 prelude:: * ,
15- revocable:: Revocable ,
16- sync:: Arc ,
15+ revocable:: { Revocable , RevocableGuard } ,
16+ sync:: { rcu , Arc , Completion } ,
1717 types:: ARef ,
1818} ;
1919
20- use core:: ops:: Deref ;
21-
2220#[ pin_data]
2321struct DevresInner < T > {
2422 dev : ARef < Device > ,
2523 callback : unsafe extern "C" fn ( * mut c_void ) ,
2624 #[ pin]
2725 data : Revocable < T > ,
26+ #[ pin]
27+ revoke : Completion ,
2828}
2929
3030/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
3131/// manage their lifetime.
3232///
3333/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
34- /// [`Device`] is unbound respectively, depending on what happens first.
34+ /// [`Device`] is unbound respectively, depending on what happens first. In any case, it is always
35+ /// guaranteed that revoking the device resource is completed before the corresponding [`Device`]
36+ /// is unbound.
3537///
3638/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
3739/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
@@ -102,6 +104,7 @@ impl<T> DevresInner<T> {
102104 dev: dev. into( ) ,
103105 callback: Self :: devres_callback,
104106 data <- Revocable :: new( data) ,
107+ revoke <- Completion :: new( ) ,
105108 } ) ,
106109 flags,
107110 ) ?;
@@ -130,26 +133,28 @@ impl<T> DevresInner<T> {
130133 self as _
131134 }
132135
133- fn remove_action ( this : & Arc < Self > ) {
136+ fn remove_action ( this : & Arc < Self > ) -> bool {
134137 // SAFETY:
135138 // - `self.inner.dev` is a valid `Device`,
136139 // - the `action` and `data` pointers are the exact same ones as given to devm_add_action()
137140 // previously,
138141 // - `self` is always valid, even if the action has been released already.
139- let ret = unsafe {
142+ let success = unsafe {
140143 bindings:: devm_remove_action_nowarn (
141144 this. dev . as_raw ( ) ,
142145 Some ( this. callback ) ,
143146 this. as_ptr ( ) as _ ,
144147 )
145- } ;
148+ } == 0 ;
146149
147- if ret == 0 {
150+ if success {
148151 // SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if
149152 // devm_remove_action_nowarn() was successful we can (and have to) claim back ownership
150153 // of this reference.
151154 let _ = unsafe { Arc :: from_raw ( this. as_ptr ( ) ) } ;
152155 }
156+
157+ success
153158 }
154159
155160 #[ allow( clippy:: missing_safety_doc) ]
@@ -161,7 +166,12 @@ impl<T> DevresInner<T> {
161166 // `DevresInner::new`.
162167 let inner = unsafe { Arc :: from_raw ( ptr) } ;
163168
164- inner. data . revoke ( ) ;
169+ if !inner. data . revoke ( ) {
170+ // If `revoke()` returns false, it means that `Devres::drop` already started revoking
171+ // `inner.data` for us. Hence we have to wait until `Devres::drop()` signals that it
172+ // completed revoking `inner.data`.
173+ inner. revoke . wait_for_completion ( ) ;
174+ }
165175 }
166176}
167177
@@ -218,20 +228,36 @@ impl<T> Devres<T> {
218228 // SAFETY: `dev` being the same device as the device this `Devres` has been created for
219229 // proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as
220230 // long as `dev` lives; `dev` lives at least as long as `self`.
221- Ok ( unsafe { self . deref ( ) . access ( ) } )
231+ Ok ( unsafe { self . 0 . data . access ( ) } )
222232 }
223- }
224233
225- impl < T > Deref for Devres < T > {
226- type Target = Revocable < T > ;
234+ /// [`Devres`] accessor for [`Revocable::try_access`].
235+ pub fn try_access ( & self ) -> Option < RevocableGuard < ' _ , T > > {
236+ self . 0 . data . try_access ( )
237+ }
238+
239+ /// [`Devres`] accessor for [`Revocable::try_access_with`].
240+ pub fn try_access_with < R , F : FnOnce ( & T ) -> R > ( & self , f : F ) -> Option < R > {
241+ self . 0 . data . try_access_with ( f)
242+ }
227243
228- fn deref ( & self ) -> & Self :: Target {
229- & self . 0 . data
244+ /// [`Devres`] accessor for [`Revocable::try_access_with_guard`].
245+ pub fn try_access_with_guard < ' a > ( & ' a self , guard : & ' a rcu:: Guard ) -> Option < & ' a T > {
246+ self . 0 . data . try_access_with_guard ( guard)
230247 }
231248}
232249
233250impl < T > Drop for Devres < T > {
234251 fn drop ( & mut self ) {
235- DevresInner :: remove_action ( & self . 0 ) ;
252+ // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data
253+ // anymore, hence it is safe not to wait for the grace period to finish.
254+ if unsafe { self . 0 . data . revoke_nosync ( ) } {
255+ // We revoked `self.0.data` before the devres action did, hence try to remove it.
256+ if !DevresInner :: remove_action ( & self . 0 ) {
257+ // We could not remove the devres action, which means that it now runs concurrently,
258+ // hence signal that `self.0.data` has been revoked successfully.
259+ self . 0 . revoke . complete_all ( ) ;
260+ }
261+ }
236262 }
237263}
0 commit comments