@@ -720,11 +720,10 @@ En C par exemple, il n'y a pas de moyen simple qui permette de vérifier que le
720
720
destructeur correspondant est appelé. Il est possible d'utiliser des _ callbacks_
721
721
pour assurer que la libération est effectivement faite.
722
722
723
- Le code Rust suivant est un exemple ** _ unsafe_ du point de vue des threads**
724
- d'une API compatible avec le C qui fournit une _ callback_ pour assurer la
725
- libération d'une ressource :
723
+ Le code Rust suivant est un exemple d'une API compatible avec le C qui exploite
724
+ les _ callbacks_ pour assurer la libération d'une ressource :
726
725
727
- ``` rust,noplaypen
726
+ ``` rust
728
727
# use std :: ops :: Drop ;
729
728
#
730
729
pub struct XtraResource { /* champs */ }
@@ -733,8 +732,12 @@ impl XtraResource {
733
732
pub fn new () -> Self {
734
733
XtraResource { /* ... */ }
735
734
}
736
- pub fn dosthg(&mut self) {
737
- /* ... */
735
+
736
+ pub fn dosthg (& mut self , arg : u32 ) {
737
+ /*... des choses qui peuvent paniquer ... */
738
+ # if arg == 0xDEAD_C0DE {
739
+ # panic! (" oups XtraResource.dosthg panique!" );
740
+ # }
738
741
}
739
742
}
740
743
@@ -746,58 +749,55 @@ impl Drop for XtraResource {
746
749
747
750
pub mod c_api {
748
751
use super :: XtraResource ;
749
- use std::panic::catch_unwind;
752
+ use std :: panic :: {catch_unwind, AssertUnwindSafe };
753
+ use std :: sync :: atomic :: {AtomicU32 , Ordering };
750
754
751
755
const INVALID_TAG : u32 = 0 ;
752
756
const VALID_TAG : u32 = 0xDEAD_BEEF ;
753
757
const ERR_TAG : u32 = 0xDEAF_CAFE ;
754
758
755
- static mut COUNTER: u32 = 0;
756
-
757
759
pub struct CXtraResource {
758
- tag: u32, // pour prévenir d'une réutilisation accidentelle
759
- id: u32,
760
+ tag : AtomicU32 , // pour prévenir d'une réutilisation accidentelle
760
761
inner : XtraResource ,
761
762
}
762
763
763
764
#[no_mangle]
764
- pub unsafe extern "C" fn xtra_with(cb: extern "C" fn(*mut CXtraResource) -> ()) {
765
- let inner = if let Ok(res) = catch_unwind(XtraResource::new) {
765
+ pub unsafe extern " C" fn xtra_with (cb : unsafe extern " C" fn (* mut CXtraResource ) -> ()) {
766
+ let inner = if let Ok (res ) = catch_unwind (AssertUnwindSafe ( XtraResource :: new ) ) {
766
767
res
767
768
} else {
768
769
# println! (" impossible d'allouer la ressource" );
769
770
return ;
770
771
};
771
- let id = COUNTER;
772
772
let tag = VALID_TAG ;
773
773
774
- COUNTER = COUNTER.wrapping_add(1);
775
- // Utilisation de la mémoire du tas pour ne pas fournir de pointeur de
776
- // pile au code C!
777
- let mut boxed = Box::new(CXtraResource { tag, id, inner }) ;
774
+ let mut wrapped = CXtraResource {
775
+ tag : AtomicU32 :: new ( tag ),
776
+ inner
777
+ } ;
778
778
779
- # println!("running the callback on {:p}", boxed.as_ref() );
780
- cb(boxed.as_mut() as *mut CXtraResource);
779
+ # println! (" appel du callback sur {:p}" , & wrapped );
780
+ cb (& mut wrapped as * mut CXtraResource );
781
781
782
- if boxed.id == id && (boxed.tag == VALID_TAG || boxed.tag == ERR_TAG) {
783
- # println!("freeing {:p}", boxed.as_ref());
784
- boxed.tag = INVALID_TAG; // prévention d'une réutilisation accidentelle
785
- // drop implicite de la `box`
782
+ // pour éviter de réutilisation accidentelle
783
+ let new_tag = wrapped . tag. swap (INVALID_TAG , Ordering :: SeqCst );
784
+ if new_tag == VALID_TAG || new_tag == ERR_TAG {
785
+ # println! (" libération de {:p}" , & wrapped );
786
+ // drop implicite de la `box`
786
787
} else {
787
- # println!("oubli de {:p}", boxed.as_ref() );
788
+ # println! (" oubli de {:p}" , & wrapped );
788
789
// (...) gestion des erreurs (partie critique)
789
- boxed.tag = INVALID_TAG; // prévention d'une réutilisation
790
- std::mem::forget(boxed); // boxed is corrupted it should not be
790
+ std :: mem :: forget (wrapped ); // la boîte est corrompu, ne pas libérer!
791
791
}
792
792
}
793
793
794
794
#[no_mangle]
795
- pub unsafe extern "C" fn xtra_dosthg(cxtra: *mut CXtraResource) {
796
- let do_it = || {
795
+ pub unsafe extern " C" fn xtra_dosthg (cxtra : * mut CXtraResource , arg : u32 ) {
796
+ let do_it = move || {
797
797
if let Some (cxtra ) = cxtra . as_mut () {
798
- if cxtra.tag == VALID_TAG {
799
- # println!("doing something with {:p}", cxtra);
800
- cxtra.inner.dosthg();
798
+ if cxtra . tag. load ( Ordering :: SeqCst ) == VALID_TAG {
799
+ # println! (" fait quelque chose avec {:p}" , cxtra );
800
+ cxtra . inner. dosthg (arg );
801
801
return ;
802
802
}
803
803
}
@@ -806,25 +806,40 @@ pub mod c_api {
806
806
if catch_unwind (do_it ). is_err () {
807
807
if let Some (cxtra ) = cxtra . as_mut () {
808
808
# println! (" panic avec {:p}" , cxtra );
809
- cxtra.tag = ERR_TAG;
809
+ cxtra . tag. store ( ERR_TAG , Ordering :: SeqCst ) ;
810
810
}
811
811
};
812
812
}
813
813
}
814
814
#
815
- # fn main() {}
815
+ # fn main () {
816
+ # // ne pas utiliser, seulement pour le test
817
+ # use c_api :: * ;
818
+ # unsafe {
819
+ # unsafe extern " C" fn cb_ok (p : * mut CXtraResource ) {
820
+ # xtra_dosthg (p , 0 );
821
+ # }
822
+ # unsafe extern " C" fn cb_panic (p : * mut CXtraResource ) {
823
+ # xtra_dosthg (p , 0xDEADC0DE );
824
+ # }
825
+ # xtra_with (cb_ok );
826
+ # xtra_with (cb_panic );
827
+ # }
828
+ # }
816
829
```
817
830
818
831
Un appel C compatible :
819
832
820
833
``` c
834
+ #include < stdint.h>
835
+
821
836
struct XtraResource;
822
- void xtra_with (void (* cb)(XtraResource* xtra));
837
+ void xtra_with (void (* cb)(XtraResource* xtra), uint32_t arg );
823
838
void xtra_sthg(XtraResource* xtra);
824
839
825
840
void cb(XtraResource* xtra) {
826
841
// ()...) do anything with the proposed C API for XtraResource
827
- xtra_sthg(xtra);
842
+ xtra_sthg(xtra, 0 );
828
843
}
829
844
830
845
int main() {
0 commit comments