Skip to main content

relibc/header/pthread/
spin.rs

1use core::sync::atomic::{AtomicI32 as AtomicInt, Ordering};
2
3use crate::{
4    header::errno::EBUSY,
5    platform::types::{c_int, pthread_spinlock_t},
6};
7
8/// The spin lock is in an unlocked state.
9const UNLOCKED: c_int = 0;
10/// The spin lock is in a locked state.
11const LOCKED: c_int = 1;
12
13/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_spin_destroy.html>.
14///
15/// Destroys the spin lock referenced by `lock` and releases any resources used
16/// by the lock.
17///
18/// Upon success, returns `0`. Upon failure, an error number is returned.
19///
20/// # Implementation
21/// Cannot fail on the Rust side so no error number is ever returned.
22///
23/// # Safety
24/// It is undefined behaviour for any of the following:
25/// - Subsequent use of `lock` after calling this function unless it is
26///   reinitialized with `pthread_spin_init()`.
27/// - This function is called when a thread holds the lock.
28/// - `lock` is uninitialized when this function is called.
29#[unsafe(no_mangle)]
30pub unsafe extern "C" fn pthread_spin_destroy(lock: *mut pthread_spinlock_t) -> c_int {
31    let _spinlock = unsafe { &mut *lock.cast::<RlctSpinlock>() };
32
33    // No-op
34    0
35}
36/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_spin_init.html>.
37///
38/// Allocates any resources required to use the spin lock referenced to by
39/// `lock` and initializes the lock to an unlocked state.
40///
41/// Upon success, returns `0`. Upon failure, an error number is returned.
42///
43/// # Implementation
44/// Cannot fail on the Rust side so no error number is ever returned.
45///
46/// # Safety
47/// It is undefined behaviour for any of the following:
48/// - `lock` has already been initialized.
49/// - `lock` has been used before being initialized by this function.
50#[unsafe(no_mangle)]
51pub unsafe extern "C" fn pthread_spin_init(
52    lock: *mut pthread_spinlock_t,
53    _pshared: c_int,
54) -> c_int {
55    // TODO: pshared doesn't matter in most situations, as memory is just memory, but this may be
56    // different on some architectures...
57
58    unsafe {
59        lock.cast::<RlctSpinlock>().write(RlctSpinlock {
60            inner: AtomicInt::new(UNLOCKED),
61        })
62    };
63
64    0
65}
66/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_spin_lock.html>.
67///
68/// Locks the spin lock referenced by `lock`.
69///
70/// Upon success, returns `0`. Upon failure, an error number is returned.
71///
72/// # Safety
73/// It is undefined behaviour for any of the following:
74/// - `lock` is uninitialized.
75/// - The calling thread holds `lock` at the time the call is made.
76#[unsafe(no_mangle)]
77pub unsafe extern "C" fn pthread_spin_lock(lock: *mut pthread_spinlock_t) -> c_int {
78    let spinlock = unsafe { &*lock.cast::<RlctSpinlock>() };
79
80    loop {
81        match spinlock.inner.compare_exchange_weak(
82            UNLOCKED,
83            LOCKED,
84            Ordering::Acquire,
85            Ordering::Relaxed,
86        ) {
87            Ok(_) => break,
88            Err(_) => core::hint::spin_loop(),
89        }
90    }
91
92    0
93}
94/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_spin_trylock.html>.
95///
96/// Locks the spin lock referenced by `lock` if it is not held by any thread.
97///
98/// Upon success, returns `0`. Upon failure, an error number is returned.
99///
100/// # Safety
101/// It is undefined behaviour if `lock` is uninitialized.
102#[unsafe(no_mangle)]
103pub unsafe extern "C" fn pthread_spin_trylock(lock: *mut pthread_spinlock_t) -> c_int {
104    let spinlock = unsafe { &*lock.cast::<RlctSpinlock>() };
105
106    match spinlock
107        .inner
108        .compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed)
109    {
110        Ok(_) => (),
111        Err(_) => return EBUSY,
112    }
113
114    0
115}
116/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_spin_unlock.html>.
117///
118/// Releases the spin lock referenced by `lock` which was locked via the
119/// `pthread_spin_lock()` or `pthread_spin_trylock()` functions.
120///
121/// Upon success, returns `0`. Upon failure, an error number is returned.
122///
123/// # Implementation
124/// Cannot fail on the Rust side so no error number is ever returned.
125///
126/// # Safety
127/// It is undefined behaviour for any of the following:
128/// - `lock` is uninitialized.
129/// - `lock` is not held by the calling thread.
130#[unsafe(no_mangle)]
131pub unsafe extern "C" fn pthread_spin_unlock(lock: *mut pthread_spinlock_t) -> c_int {
132    let spinlock = unsafe { &*lock.cast::<RlctSpinlock>() };
133
134    spinlock.inner.store(UNLOCKED, Ordering::Release);
135
136    0
137}
138
139pub(crate) struct RlctSpinlock {
140    pub inner: AtomicInt,
141}