1 //! Strategies that determine the behaviour of locks when encountering contention. 2 3 /// A trait implemented by spinning relax strategies. 4 pub trait RelaxStrategy { 5 /// Perform the relaxing operation during a period of contention. relax()6 fn relax(); 7 } 8 9 /// A strategy that rapidly spins while informing the CPU that it should power down non-essential components via 10 /// [`core::hint::spin_loop`]. 11 /// 12 /// Note that spinning is a 'dumb' strategy and most schedulers cannot correctly differentiate it from useful work, 13 /// thereby misallocating even more CPU time to the spinning process. This is known as 14 /// ['priority inversion'](https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html). 15 /// 16 /// If you see signs that priority inversion is occurring, consider switching to [`Yield`] or, even better, not using a 17 /// spinlock at all and opting for a proper scheduler-aware lock. Remember also that different targets, operating 18 /// systems, schedulers, and even the same scheduler with different workloads will exhibit different behaviour. Just 19 /// because priority inversion isn't occurring in your tests does not mean that it will not occur. Use a scheduler- 20 /// aware lock if at all possible. 21 pub struct Spin; 22 23 impl RelaxStrategy for Spin { 24 #[inline(always)] relax()25 fn relax() { 26 // Use the deprecated spin_loop_hint() to ensure that we don't get 27 // a higher MSRV than we need to. 28 #[allow(deprecated)] 29 core::sync::atomic::spin_loop_hint(); 30 } 31 } 32 33 /// A strategy that yields the current time slice to the scheduler in favour of other threads or processes. 34 /// 35 /// This is generally used as a strategy for minimising power consumption and priority inversion on targets that have a 36 /// standard library available. Note that such targets have scheduler-integrated concurrency primitives available, and 37 /// you should generally use these instead, except in rare circumstances. 38 #[cfg(feature = "std")] 39 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 40 pub struct Yield; 41 42 #[cfg(feature = "std")] 43 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 44 impl RelaxStrategy for Yield { 45 #[inline(always)] relax()46 fn relax() { 47 std::thread::yield_now(); 48 } 49 } 50 51 /// A strategy that rapidly spins, without telling the CPU to do any powering down. 52 /// 53 /// You almost certainly do not want to use this. Use [`Spin`] instead. It exists for completeness and for targets 54 /// that, for some reason, miscompile or do not support spin hint intrinsics despite attempting to generate code for 55 /// them (i.e: this is a workaround for possible compiler bugs). 56 pub struct Loop; 57 58 impl RelaxStrategy for Loop { 59 #[inline(always)] relax()60 fn relax() {} 61 } 62