1
+ #![ allow( mutable_transmutes) ]
1
2
2
- #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
3
- pub fn compare_and_swap ( destination : & u64 , expected : u64 , new_value : u64 ) -> bool {
4
- let value_at_dest : u64 ;
5
- unsafe {
6
- asm ! ( "LOCK CMPXCHG qword ptr [RCX], RBX"
7
- : "={rax}" ( value_at_dest) // output
8
-
9
- : "{rbx}" ( new_value) ,
10
- "{rcx}" ( destination) , // input
11
- "{rax}" ( expected)
12
-
13
- : "rax" , "memory" // clobbers
14
-
15
- : "intel" // options
16
- ) ;
17
- }
3
+ use std:: mem;
4
+ use std:: intrinsics:: { atomic_cxchg, atomic_xadd} ;
18
5
19
- // this information is also available through the zero flag, but it's
20
- // impossible (?) to use that information without doing some sort of
21
- // secondary compare outside of the asm! block
22
- value_at_dest == expected
23
- }
6
+ pub fn compare_and_swap ( destination : & u64 , expected : u64 , new_value : u64 ) -> bool {
7
+ let destination : & mut u64 = unsafe { mem:: transmute ( destination) } ;
8
+ let ( value_at_dest, success) = unsafe { atomic_cxchg ( destination, expected, new_value) } ;
9
+ assert ! ( ( value_at_dest == expected && success) || ( value_at_dest != expected && !success) ) ;
24
10
25
- #[ repr( align( 16 ) ) ]
26
- #[ derive( Debug ) ]
27
- pub struct DoubleU64 {
28
- high : u64 ,
29
- low : u64 ,
11
+ success
30
12
}
31
13
32
- #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
33
- pub fn compare_and_swap_2 ( destination : & DoubleU64 , expected : & DoubleU64 , new_value : & DoubleU64 ) -> bool { // TODO: return Result to pass back values?
34
- let value_at_dest_high : u64 ;
35
- let value_at_dest_low : u64 ;
36
-
37
- unsafe {
38
- asm ! ( "LOCK CMPXCHG16B [R8]"
39
- : "={rax}" ( value_at_dest_high) , // output
40
- "={rdx}" ( value_at_dest_low)
41
-
42
- : "{rbx}" ( new_value. high) , // input
43
- "{rcx}" ( new_value. low) ,
44
- "{r8}" ( destination) ,
45
- "{rax}" ( expected. high)
46
- "{rdx}" ( expected. low)
14
+ pub fn compare_and_swap_u128 ( destination : & u128 , expected : u128 , new_value : u128 ) -> bool { // TODO: return Result to pass back values?
15
+ let destination : & mut u128 = unsafe { mem:: transmute ( destination) } ;
16
+ let ( value_at_dest, success) = unsafe { atomic_cxchg ( destination, expected, new_value) } ;
17
+ assert ! ( ( value_at_dest == expected && success) || ( value_at_dest != expected && !success) ) ;
47
18
48
- : "rax" , "rdx" , "memory" // clobbers
49
-
50
- : "intel" // options
51
- ) ;
52
- }
53
-
54
- // this information is also available through the zero flag, but it's
55
- // impossible (?) to use that information without doing some sort of
56
- // secondary compare outside of the asm! block
57
- value_at_dest_high == expected. high && value_at_dest_low == expected. low
19
+ success
58
20
}
59
21
60
- #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
61
22
pub fn fetch_and_add ( destination : & u64 , addend : u64 ) -> u64 {
62
- let value_at_dest : u64 ;
63
- unsafe {
64
- asm ! ( "LOCK XADD qword ptr [RCX], RBX"
65
- : "={rbx}" ( value_at_dest) // output
66
-
67
- : "{rbx}" ( addend) , // input
68
- "{rcx}" ( destination)
69
-
70
- : "rbx" , "memory" // clobbers
71
-
72
- : "intel" // options
73
- ) ;
74
- }
23
+ let destination : & mut u64 = unsafe { mem:: transmute ( destination) } ;
24
+ let value_at_dest = unsafe { atomic_xadd ( destination, addend) } ;
75
25
76
26
value_at_dest
77
27
}
78
28
79
- #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
80
29
pub fn test_and_set ( destination : & u64 ) {
81
- unsafe {
82
- asm ! ( "LOCK BTS qword ptr [RCX], 63"
83
- : // output
84
- : "{rcx}" ( destination) // input
85
- : "rbx" , "memory" // clobbers
86
- : "intel" // options
87
- ) ;
88
- }
30
+ //let destination : &mut u64 = unsafe { mem::transmute(destination) };
31
+ //*destination = *destination | (1 << 63);
89
32
}
90
33
91
34
#[ cfg( test) ]
@@ -112,15 +55,17 @@ mod test {
112
55
}
113
56
114
57
#[ test]
115
- fn test_compare_and_swap_2_single_thread ( ) {
116
- let mut x = DoubleU64 { high : 1 , low : 2 } ;
117
- assert ! ( compare_and_swap_2( & mut x, & DoubleU64 { high: 1 , low: 2 } , & DoubleU64 { high: 2 , low: 3 } ) ) ;
118
- assert_eq ! ( x. high, 2 ) ;
119
- assert_eq ! ( x. low , 3 ) ;
120
-
121
- assert ! ( !compare_and_swap_2( & mut x, & DoubleU64 { high: 1 , low: 2 } , & DoubleU64 { high: 3 , low: 2 } ) ) ;
122
- assert_eq ! ( x. high, 2 ) ;
123
- assert_eq ! ( x. low , 3 ) ;
58
+ fn test_compare_and_swap_u128_single_thread ( ) {
59
+ let mut x = 0x00000000000000010000000000000002u128 ;
60
+ let expect = 0x00000000000000010000000000000002u128 ;
61
+ let new = 0x00000000000000020000000000000003u128 ;
62
+ assert ! ( compare_and_swap_u128( & mut x, expect, new) ) ;
63
+ assert_eq ! ( x, new) ;
64
+
65
+ // won't swap new for newer
66
+ let newer = 0x00000000000000030000000000000002u128 ;
67
+ assert ! ( !compare_and_swap_u128( & mut x, expect, newer) ) ;
68
+ assert_eq ! ( x, new) ;
124
69
}
125
70
126
71
#[ test]
0 commit comments