@@ -25,7 +25,7 @@ pub use internal::AtomicImpl;
2525pub use ordering:: { Acquire , Full , Relaxed , Release } ;
2626
2727use crate :: build_error;
28- use internal:: { AtomicBasicOps , AtomicRepr } ;
28+ use internal:: { AtomicBasicOps , AtomicExchangeOps , AtomicRepr } ;
2929use ordering:: OrderingType ;
3030
3131/// A memory location which can be safely modified from multiple execution contexts.
@@ -293,3 +293,169 @@ where
293293 }
294294 }
295295}
296+
297+ impl < T : AtomicType > Atomic < T >
298+ where
299+ T :: Repr : AtomicExchangeOps ,
300+ {
301+ /// Atomic exchange.
302+ ///
303+ /// Atomically updates `*self` to `v` and returns the old value of `*self`.
304+ ///
305+ /// # Examples
306+ ///
307+ /// ```
308+ /// use kernel::sync::atomic::{Atomic, Acquire, Relaxed};
309+ ///
310+ /// let x = Atomic::new(42);
311+ ///
312+ /// assert_eq!(42, x.xchg(52, Acquire));
313+ /// assert_eq!(52, x.load(Relaxed));
314+ /// ```
315+ #[ doc( alias( "atomic_xchg" , "atomic64_xchg" , "swap" ) ) ]
316+ #[ inline( always) ]
317+ pub fn xchg < Ordering : ordering:: Ordering > ( & self , v : T , _: Ordering ) -> T {
318+ let v = into_repr ( v) ;
319+
320+ // INVARIANT: `self.0` is a valid `T` after `atomic_xchg*()` because `v` is transmutable to
321+ // `T`.
322+ let ret = {
323+ match Ordering :: TYPE {
324+ OrderingType :: Full => T :: Repr :: atomic_xchg ( & self . 0 , v) ,
325+ OrderingType :: Acquire => T :: Repr :: atomic_xchg_acquire ( & self . 0 , v) ,
326+ OrderingType :: Release => T :: Repr :: atomic_xchg_release ( & self . 0 , v) ,
327+ OrderingType :: Relaxed => T :: Repr :: atomic_xchg_relaxed ( & self . 0 , v) ,
328+ }
329+ } ;
330+
331+ // SAFETY: `ret` comes from reading `*self`, which is a valid `T` per type invariants.
332+ unsafe { from_repr ( ret) }
333+ }
334+
335+ /// Atomic compare and exchange.
336+ ///
337+ /// If `*self` == `old`, atomically updates `*self` to `new`. Otherwise, `*self` is not
338+ /// modified.
339+ ///
340+ /// Compare: The comparison is done via the byte level comparison between `*self` and `old`.
341+ ///
342+ /// Ordering: When succeeds, provides the corresponding ordering as the `Ordering` type
343+ /// parameter indicates, and a failed one doesn't provide any ordering, the load part of a
344+ /// failed cmpxchg is a [`Relaxed`] load.
345+ ///
346+ /// Returns `Ok(value)` if cmpxchg succeeds, and `value` is guaranteed to be equal to `old`,
347+ /// otherwise returns `Err(value)`, and `value` is the current value of `*self`.
348+ ///
349+ /// # Examples
350+ ///
351+ /// ```
352+ /// use kernel::sync::atomic::{Atomic, Full, Relaxed};
353+ ///
354+ /// let x = Atomic::new(42);
355+ ///
356+ /// // Checks whether cmpxchg succeeded.
357+ /// let success = x.cmpxchg(52, 64, Relaxed).is_ok();
358+ /// # assert!(!success);
359+ ///
360+ /// // Checks whether cmpxchg failed.
361+ /// let failure = x.cmpxchg(52, 64, Relaxed).is_err();
362+ /// # assert!(failure);
363+ ///
364+ /// // Uses the old value if failed, probably re-try cmpxchg.
365+ /// match x.cmpxchg(52, 64, Relaxed) {
366+ /// Ok(_) => { },
367+ /// Err(old) => {
368+ /// // do something with `old`.
369+ /// # assert_eq!(old, 42);
370+ /// }
371+ /// }
372+ ///
373+ /// // Uses the latest value regardlessly, same as atomic_cmpxchg() in C.
374+ /// let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
375+ /// # assert_eq!(42, latest);
376+ /// assert_eq!(64, x.load(Relaxed));
377+ /// ```
378+ ///
379+ /// [`Relaxed`]: ordering::Relaxed
380+ #[ doc( alias(
381+ "atomic_cmpxchg" ,
382+ "atomic64_cmpxchg" ,
383+ "atomic_try_cmpxchg" ,
384+ "atomic64_try_cmpxchg" ,
385+ "compare_exchange"
386+ ) ) ]
387+ #[ inline( always) ]
388+ pub fn cmpxchg < Ordering : ordering:: Ordering > (
389+ & self ,
390+ mut old : T ,
391+ new : T ,
392+ o : Ordering ,
393+ ) -> Result < T , T > {
394+ // Note on code generation:
395+ //
396+ // try_cmpxchg() is used to implement cmpxchg(), and if the helper functions are inlined,
397+ // the compiler is able to figure out that branch is not needed if the users don't care
398+ // about whether the operation succeeds or not. One exception is on x86, due to commit
399+ // 44fe84459faf ("locking/atomic: Fix atomic_try_cmpxchg() semantics"), the
400+ // atomic_try_cmpxchg() on x86 has a branch even if the caller doesn't care about the
401+ // success of cmpxchg and only wants to use the old value. For example, for code like:
402+ //
403+ // let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
404+ //
405+ // It will still generate code:
406+ //
407+ // movl $0x40, %ecx
408+ // movl $0x34, %eax
409+ // lock
410+ // cmpxchgl %ecx, 0x4(%rsp)
411+ // jne 1f
412+ // 2:
413+ // ...
414+ // 1: movl %eax, %ecx
415+ // jmp 2b
416+ //
417+ // This might be "fixed" by introducing a try_cmpxchg_exclusive() that knows the "*old"
418+ // location in the C function is always safe to write.
419+ if self . try_cmpxchg ( & mut old, new, o) {
420+ Ok ( old)
421+ } else {
422+ Err ( old)
423+ }
424+ }
425+
426+ /// Atomic compare and exchange and returns whether the operation succeeds.
427+ ///
428+ /// If `*self` == `old`, atomically updates `*self` to `new`. Otherwise, `*self` is not
429+ /// modified, `*old` is updated to the current value of `*self`.
430+ ///
431+ /// "Compare" and "Ordering" part are the same as [`Atomic::cmpxchg()`].
432+ ///
433+ /// Returns `true` means the cmpxchg succeeds otherwise returns `false`.
434+ #[ inline( always) ]
435+ fn try_cmpxchg < Ordering : ordering:: Ordering > ( & self , old : & mut T , new : T , _: Ordering ) -> bool {
436+ let mut tmp = into_repr ( * old) ;
437+ let new = into_repr ( new) ;
438+
439+ // INVARIANT: `self.0` is a valid `T` after `atomic_try_cmpxchg*()` because `new` is
440+ // transmutable to `T`.
441+ let ret = {
442+ match Ordering :: TYPE {
443+ OrderingType :: Full => T :: Repr :: atomic_try_cmpxchg ( & self . 0 , & mut tmp, new) ,
444+ OrderingType :: Acquire => {
445+ T :: Repr :: atomic_try_cmpxchg_acquire ( & self . 0 , & mut tmp, new)
446+ }
447+ OrderingType :: Release => {
448+ T :: Repr :: atomic_try_cmpxchg_release ( & self . 0 , & mut tmp, new)
449+ }
450+ OrderingType :: Relaxed => {
451+ T :: Repr :: atomic_try_cmpxchg_relaxed ( & self . 0 , & mut tmp, new)
452+ }
453+ }
454+ } ;
455+
456+ // SAFETY: `tmp` comes from reading `*self`, which is a valid `T` per type invariants.
457+ * old = unsafe { from_repr ( tmp) } ;
458+
459+ ret
460+ }
461+ }
0 commit comments