├── .gitignore ├── Cargo.toml ├── Cargo.lock ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lock-bench" 3 | version = "0.1.0" 4 | authors = ["Aleksey Kladov "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | crossbeam-utils = "0.7" 11 | parking_lot = "0.10" 12 | spin = "0.5" 13 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "0.1.7" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "bitflags" 10 | version = "1.2.1" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "cfg-if" 15 | version = "0.1.10" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | 18 | [[package]] 19 | name = "cloudabi" 20 | version = "0.0.3" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "crossbeam-utils" 28 | version = "0.7.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 34 | ] 35 | 36 | [[package]] 37 | name = "lazy_static" 38 | version = "1.4.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | 41 | [[package]] 42 | name = "libc" 43 | version = "0.2.66" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | 46 | [[package]] 47 | name = "lock-bench" 48 | version = "0.1.0" 49 | dependencies = [ 50 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 53 | ] 54 | 55 | [[package]] 56 | name = "lock_api" 57 | version = "0.3.2" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | dependencies = [ 60 | "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 61 | ] 62 | 63 | [[package]] 64 | name = "parking_lot" 65 | version = "0.10.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | dependencies = [ 68 | "lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 70 | ] 71 | 72 | [[package]] 73 | name = "parking_lot_core" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | dependencies = [ 77 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "redox_syscall" 87 | version = "0.1.56" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | 90 | [[package]] 91 | name = "scopeguard" 92 | version = "1.0.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | 95 | [[package]] 96 | name = "smallvec" 97 | version = "1.1.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | 100 | [[package]] 101 | name = "spin" 102 | version = "0.5.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | 105 | [[package]] 106 | name = "winapi" 107 | version = "0.3.8" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | dependencies = [ 110 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 112 | ] 113 | 114 | [[package]] 115 | name = "winapi-i686-pc-windows-gnu" 116 | version = "0.4.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | 119 | [[package]] 120 | name = "winapi-x86_64-pc-windows-gnu" 121 | version = "0.4.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | 124 | [metadata] 125 | "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 126 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 127 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 128 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 129 | "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" 130 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 131 | "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" 132 | "checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" 133 | "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" 134 | "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" 135 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 136 | "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" 137 | "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" 138 | "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 139 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 140 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 141 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A benchmark of mutex vs spinlock throughput for an extremely short critical section under varying levels of contention on "average" desktop. 2 | 3 | Inspiration and code for `AmdSpinlock` are from https://probablydance.com/2019/12/30/measuring-mutexes-spinlocks-and-how-bad-the-linux-scheduler-really-is/. 4 | 5 | Summary of results: 6 | 7 | * Spinlocks are almost always significantly worse than a good mutex, and never significantly better, 8 | * Contention makes spinlocks relatively slower. 9 | 10 | Biggest known caveat (apart from this being a single benchmark run on a single machine): 11 | 12 | The best mutex implementation seems to be relatively more optimized than the best spinlock implementation. 13 | 14 | ## Results 15 | 16 | **extreme contention:** 17 | ``` 18 | 12:31:05|~/projects/lock-bench|master⚡* 19 | λ cargo run --release 32 2 10000 100 20 | Finished release [optimized] target(s) in 0.01s 21 | Running `target/release/lock-bench 32 2 10000 100` 22 | Options { 23 | n_threads: 32, 24 | n_locks: 2, 25 | n_ops: 10000, 26 | n_rounds: 100, 27 | } 28 | 29 | std::sync::Mutex avg 97.770106ms min 38.799445ms max 103.42306ms 30 | parking_lot::Mutex avg 68.350542ms min 32.139233ms max 72.404877ms 31 | spin::Mutex avg 142.257494ms min 69.860396ms max 217.587871ms 32 | AmdSpinlock avg 127.612286ms min 50.407761ms max 219.429909ms 33 | 34 | std::sync::Mutex avg 98.838394ms min 68.180052ms max 125.635571ms 35 | parking_lot::Mutex avg 68.51149ms min 58.805279ms max 71.512899ms 36 | spin::Mutex avg 139.751964ms min 54.499263ms max 193.70374ms 37 | AmdSpinlock avg 127.757924ms min 50.249234ms max 210.452623ms 38 | ``` 39 | 40 | **heavy contention:** 41 | ``` 42 | 12:34:39|~/projects/lock-bench|master⚡* 43 | λ cargo run --release 32 64 10000 100 44 | Finished release [optimized] target(s) in 0.01s 45 | Running `target/release/lock-bench 32 64 10000 100` 46 | Options { 47 | n_threads: 32, 48 | n_locks: 64, 49 | n_ops: 10000, 50 | n_rounds: 100, 51 | } 52 | 53 | std::sync::Mutex avg 21.538657ms min 11.704293ms max 23.688275ms 54 | parking_lot::Mutex avg 10.016941ms min 6.787887ms max 11.7508ms 55 | spin::Mutex avg 55.555043ms min 7.639845ms max 161.030869ms 56 | AmdSpinlock avg 40.82985ms min 6.174719ms max 123.545934ms 57 | 58 | std::sync::Mutex avg 21.489658ms min 20.344423ms max 24.05294ms 59 | parking_lot::Mutex avg 9.640073ms min 6.782365ms max 12.600402ms 60 | spin::Mutex avg 48.74331ms min 7.601425ms max 138.172171ms 61 | AmdSpinlock avg 40.993328ms min 8.365127ms max 110.106416ms 62 | ``` 63 | 64 | **light contention:** 65 | ``` 66 | 12:29:01|~/projects/lock-bench|master⚡* 67 | λ cargo run --release 32 1000 10000 100 68 | Finished release [optimized] target(s) in 0.01s 69 | Running `target/release/lock-bench 32 1000 10000 100` 70 | Options { 71 | n_threads: 32, 72 | n_locks: 1000, 73 | n_ops: 10000, 74 | n_rounds: 100, 75 | } 76 | 77 | std::sync::Mutex avg 13.897816ms min 8.176447ms max 15.680984ms 78 | parking_lot::Mutex avg 6.553058ms min 3.284944ms max 8.230407ms 79 | spin::Mutex avg 37.946399ms min 4.668167ms max 115.748116ms 80 | AmdSpinlock avg 39.530919ms min 2.049988ms max 127.139724ms 81 | 82 | std::sync::Mutex avg 13.922504ms min 12.885598ms max 15.28518ms 83 | parking_lot::Mutex avg 6.80546ms min 5.621588ms max 8.932723ms 84 | spin::Mutex avg 39.411306ms min 4.752935ms max 102.888667ms 85 | AmdSpinlock avg 37.423773ms min 5.087086ms max 103.319751ms 86 | ``` 87 | 88 | **no contention:** 89 | ``` 90 | 12:26:25|~/projects/lock-bench|master⚡* 91 | λ cargo run --release 32 1000000 10000 100 92 | Finished release [optimized] target(s) in 0.01s 93 | Running `target/release/lock-bench 32 1000000 10000 100` 94 | Options { 95 | n_threads: 32, 96 | n_locks: 1000000, 97 | n_ops: 10000, 98 | n_rounds: 100, 99 | } 100 | 101 | std::sync::Mutex avg 15.975801ms min 8.487617ms max 27.106072ms 102 | parking_lot::Mutex avg 7.142194ms min 4.594504ms max 9.178952ms 103 | spin::Mutex avg 5.947778ms min 4.600631ms max 8.262928ms 104 | AmdSpinlock avg 6.512221ms min 5.059464ms max 10.408178ms 105 | 106 | std::sync::Mutex avg 15.793032ms min 8.260458ms max 27.850666ms 107 | parking_lot::Mutex avg 6.910369ms min 4.32757ms max 9.08715ms 108 | spin::Mutex avg 5.890677ms min 4.424554ms max 7.622798ms 109 | AmdSpinlock avg 6.416132ms min 5.852334ms max 7.349909ms 110 | ``` 111 | 112 | ## Machine Spec 113 | 114 | ``` 115 | 12:45:27|~/projects/lock-bench|master⚡*? 116 | λ cat /etc/os-release 117 | NAME=NixOS 118 | ID=nixos 119 | VERSION="19.09.1693.eab4ee0c27c (Loris)" 120 | VERSION_CODENAME=loris 121 | VERSION_ID="19.09.1693.eab4ee0c27c" 122 | PRETTY_NAME="NixOS 19.09.1693.eab4ee0c27c (Loris)" 123 | LOGO="nix-snowflake" 124 | HOME_URL="https://nixos.org/" 125 | DOCUMENTATION_URL="https://nixos.org/nixos/manual/index.html" 126 | SUPPORT_URL="https://nixos.org/nixos/support.html" 127 | BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues" 128 | 129 | 12:45:32|~/projects/lock-bench|master⚡*? 130 | λ uname -r 131 | 4.19.91 132 | 133 | 12:45:34|~/projects/lock-bench|master⚡*? 134 | λ lscpu 135 | Architecture: x86_64 136 | CPU op-mode(s): 32-bit, 64-bit 137 | Byte Order: Little Endian 138 | Address sizes: 39 bits physical, 48 bits virtual 139 | CPU(s): 8 140 | On-line CPU(s) list: 0-7 141 | Thread(s) per core: 2 142 | Core(s) per socket: 4 143 | Socket(s): 1 144 | NUMA node(s): 1 145 | Vendor ID: GenuineIntel 146 | CPU family: 6 147 | Model: 158 148 | Model name: Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz 149 | Stepping: 9 150 | CPU MHz: 1073.198 151 | CPU max MHz: 3900.0000 152 | CPU min MHz: 800.0000 153 | BogoMIPS: 5808.00 154 | Virtualization: VT-x 155 | L1d cache: 32K 156 | L1i cache: 32K 157 | L2 cache: 256K 158 | L3 cache: 8192K 159 | NUMA node0 CPU(s): 0-7 160 | Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp flush_l1d 161 | ``` 162 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{iter, sync::Barrier, time}; 2 | 3 | use crossbeam_utils::{thread::scope, CachePadded}; 4 | 5 | fn main() { 6 | let mut args = std::env::args() 7 | .skip(1) 8 | .map(|it| it.parse::().unwrap()); 9 | 10 | let options = Options { 11 | n_threads: args.next().unwrap(), 12 | n_locks: args.next().unwrap(), 13 | n_ops: args.next().unwrap(), 14 | n_rounds: args.next().unwrap(), 15 | }; 16 | println!("{:#?}\n", options); 17 | 18 | bench::(&options); 19 | bench::(&options); 20 | bench::(&options); 21 | bench::(&options); 22 | 23 | println!(); 24 | bench::(&options); 25 | bench::(&options); 26 | bench::(&options); 27 | bench::(&options); 28 | } 29 | 30 | fn bench(options: &Options) { 31 | let mut times = (0..options.n_rounds) 32 | .map(|_| run_bench::(options)) 33 | .collect::>(); 34 | times.sort(); 35 | 36 | let avg = times.iter().sum::() / options.n_rounds; 37 | let min = times[0]; 38 | let max = *times.last().unwrap(); 39 | 40 | let avg = format!("{:?}", avg); 41 | let min = format!("{:?}", min); 42 | let max = format!("{:?}", max); 43 | 44 | println!( 45 | "{:<20} avg {:<12} min {:<12} max {:<12}", 46 | M::LABEL, 47 | avg, 48 | min, 49 | max 50 | ) 51 | } 52 | 53 | #[derive(Debug)] 54 | struct Options { 55 | n_threads: u32, 56 | n_locks: u32, 57 | n_ops: u32, 58 | n_rounds: u32, 59 | } 60 | 61 | fn random_numbers(seed: u32) -> impl Iterator { 62 | let mut random = seed; 63 | iter::repeat_with(move || { 64 | random ^= random << 13; 65 | random ^= random >> 17; 66 | random ^= random << 5; 67 | random 68 | }) 69 | } 70 | 71 | trait Mutex: Sync + Send + Default { 72 | const LABEL: &'static str; 73 | fn with_lock(&self, f: impl FnOnce(&mut u32)); 74 | } 75 | 76 | fn run_bench(options: &Options) -> time::Duration { 77 | let locks = &(0..options.n_locks) 78 | .map(|_| CachePadded::new(M::default())) 79 | .collect::>(); 80 | 81 | let start_barrier = &Barrier::new(options.n_threads as usize + 1); 82 | let end_barrier = &Barrier::new(options.n_threads as usize + 1); 83 | 84 | let elapsed = scope(|scope| { 85 | let thread_seeds = random_numbers(0x6F4A955E).scan(0x9BA2BF27, |state, n| { 86 | *state ^= n; 87 | Some(*state) 88 | }); 89 | for thread_seed in thread_seeds.take(options.n_threads as usize) { 90 | scope.spawn(move |_| { 91 | start_barrier.wait(); 92 | let indexes = random_numbers(thread_seed) 93 | .map(|it| it % options.n_locks) 94 | .map(|it| it as usize) 95 | .take(options.n_ops as usize); 96 | for idx in indexes { 97 | locks[idx].with_lock(|cnt| *cnt += 1); 98 | } 99 | end_barrier.wait(); 100 | }); 101 | } 102 | 103 | std::thread::sleep(time::Duration::from_millis(100)); 104 | start_barrier.wait(); 105 | let start = time::Instant::now(); 106 | end_barrier.wait(); 107 | let elapsed = start.elapsed(); 108 | 109 | let mut total = 0; 110 | for lock in locks.iter() { 111 | lock.with_lock(|cnt| total += *cnt); 112 | } 113 | assert_eq!(total, options.n_threads * options.n_ops); 114 | 115 | elapsed 116 | }) 117 | .unwrap(); 118 | elapsed 119 | } 120 | 121 | mod mutexes { 122 | use super::Mutex; 123 | 124 | pub(crate) type Std = std::sync::Mutex; 125 | impl Mutex for Std { 126 | const LABEL: &'static str = "std::sync::Mutex"; 127 | fn with_lock(&self, f: impl FnOnce(&mut u32)) { 128 | let mut guard = self.lock().unwrap(); 129 | f(&mut guard) 130 | } 131 | } 132 | 133 | pub(crate) type ParkingLot = parking_lot::Mutex; 134 | impl Mutex for ParkingLot { 135 | const LABEL: &'static str = "parking_lot::Mutex"; 136 | fn with_lock(&self, f: impl FnOnce(&mut u32)) { 137 | let mut guard = self.lock(); 138 | f(&mut guard) 139 | } 140 | } 141 | 142 | pub(crate) type Spin = spin::Mutex; 143 | impl Mutex for Spin { 144 | const LABEL: &'static str = "spin::Mutex"; 145 | fn with_lock(&self, f: impl FnOnce(&mut u32)) { 146 | let mut guard = self.lock(); 147 | f(&mut guard) 148 | } 149 | } 150 | 151 | pub(crate) type AmdSpin = crate::amd_spinlock::AmdSpinlock; 152 | impl Mutex for AmdSpin { 153 | const LABEL: &'static str = "AmdSpinlock"; 154 | fn with_lock(&self, f: impl FnOnce(&mut u32)) { 155 | let mut guard = self.lock(); 156 | f(&mut guard) 157 | } 158 | } 159 | } 160 | 161 | mod amd_spinlock { 162 | use std::{ 163 | cell::UnsafeCell, 164 | ops, 165 | sync::atomic::{spin_loop_hint, AtomicBool, Ordering}, 166 | }; 167 | 168 | #[derive(Default)] 169 | pub(crate) struct AmdSpinlock { 170 | locked: AtomicBool, 171 | data: UnsafeCell, 172 | } 173 | unsafe impl Send for AmdSpinlock {} 174 | unsafe impl Sync for AmdSpinlock {} 175 | 176 | pub(crate) struct AmdSpinlockGuard<'a, T> { 177 | lock: &'a AmdSpinlock, 178 | } 179 | 180 | impl AmdSpinlock { 181 | pub(crate) fn lock(&self) -> AmdSpinlockGuard { 182 | loop { 183 | let was_locked = self.locked.load(Ordering::Relaxed); 184 | if !was_locked 185 | && self 186 | .locked 187 | .compare_exchange_weak( 188 | was_locked, 189 | true, 190 | Ordering::Acquire, 191 | Ordering::Relaxed, 192 | ) 193 | .is_ok() 194 | { 195 | break; 196 | } 197 | spin_loop_hint() 198 | } 199 | AmdSpinlockGuard { lock: self } 200 | } 201 | } 202 | 203 | impl<'a, T> ops::Deref for AmdSpinlockGuard<'a, T> { 204 | type Target = T; 205 | fn deref(&self) -> &Self::Target { 206 | let ptr = self.lock.data.get(); 207 | unsafe { &*ptr } 208 | } 209 | } 210 | 211 | impl<'a, T> ops::DerefMut for AmdSpinlockGuard<'a, T> { 212 | fn deref_mut(&mut self) -> &mut Self::Target { 213 | let ptr = self.lock.data.get(); 214 | unsafe { &mut *ptr } 215 | } 216 | } 217 | 218 | impl<'a, T> Drop for AmdSpinlockGuard<'a, T> { 219 | fn drop(&mut self) { 220 | self.lock.locked.store(false, Ordering::Release) 221 | } 222 | } 223 | } 224 | --------------------------------------------------------------------------------