Files
Notes/notes-service/vendor/mea/src/internal/semaphore.rs
T
2026-08-01 16:11:49 +03:00

370 lines
11 KiB
Rust

// Copyright 2024 tison <wander4096@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::future::Future;
use std::pin::Pin;
use std::sync::MutexGuard;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use crate::internal::Mutex;
use crate::internal::WaitList;
/// The internal semaphore that provides low-level async primitives.
#[derive(Debug)]
pub(crate) struct Semaphore {
/// The current number of available permits in the semaphore.
permits: AtomicUsize,
waiters: Mutex<WaitList<WaitNode>>,
}
#[derive(Debug)]
struct WaitNode {
permits: usize,
waker: Option<Waker>,
}
impl Semaphore {
pub(crate) const fn new(permits: usize) -> Self {
Self {
permits: AtomicUsize::new(permits),
waiters: Mutex::new(WaitList::new()),
}
}
/// Returns the current number of available permits.
pub(crate) fn available_permits(&self) -> usize {
self.permits.load(Ordering::Acquire)
}
/// Tries to acquire `n` permits from the semaphore.
///
/// Returns `true` if the permits were acquired, `false` otherwise.
pub(crate) fn try_acquire(&self, n: usize) -> bool {
let mut current = self.permits.load(Ordering::Acquire);
loop {
if current < n {
return false;
}
let next = current - n;
match self
.permits
.compare_exchange(current, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return true,
Err(actual) => current = actual,
}
}
}
/// Decrease the semaphore's permits by a maximum of `n`.
///
/// Return the number of permits that were actually reduced.
pub(crate) fn forget(&self, n: usize) -> usize {
if n == 0 {
return 0;
}
let mut current = self.permits.load(Ordering::Acquire);
loop {
let new = current.saturating_sub(n);
match self.permits.compare_exchange_weak(
current,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return n.min(current),
Err(actual) => current = actual,
}
}
}
/// Decrease the semaphore's permits by `n`.
///
/// If the semaphore has not enough permits, enqueue front an empty waiter to consume the
/// permits.
pub(crate) fn forget_exact(&self, n: usize) {
acquired_or_enqueue(self, n, &mut None, None, false);
}
/// Acquires `n` permits from the semaphore.
pub(crate) async fn acquire(&self, n: usize) {
let fut = Acquire {
permits: n,
index: None,
semaphore: self,
done: false,
};
fut.await
}
/// Returns a future that is resolved when acquired `n` permits from the semaphore.
pub(crate) fn poll_acquire(&self, n: usize) -> Acquire<'_> {
Acquire {
permits: n,
index: None,
semaphore: self,
done: false,
}
}
/// Adds `n` permits to the semaphore.
pub(crate) fn release(&self, n: usize) {
if n != 0 {
self.insert_permits_with_lock(n, self.waiters.lock());
}
}
/// Adds `n` permits to the semaphore if there is any waiter.
pub(crate) fn release_if_nonempty(&self, n: usize) {
let waiters = self.waiters.lock();
if !waiters.is_empty() {
self.insert_permits_with_lock(n, waiters);
}
}
/// Adds as many permits until there is no waiter.
pub(crate) fn notify_all(&self) {
let mut waiters = self.waiters.lock();
let mut wakers = Vec::new();
loop {
match waiters.remove_first_waiter(|node| {
node.permits = 0;
true
}) {
None => break,
Some(waiter) => {
if let Some(waker) = waiter.waker.take() {
wakers.push(waker);
}
}
}
}
drop(waiters);
for w in wakers.drain(..) {
w.wake();
}
}
fn insert_permits_with_lock(
&self,
mut rem: usize,
waiters: MutexGuard<'_, WaitList<WaitNode>>,
) {
const NUM_WAKER: usize = 32;
let mut wakers = Vec::with_capacity(NUM_WAKER);
let mut lock = Some(waiters);
while rem > 0 {
let mut waiters = lock.take().unwrap_or_else(|| self.waiters.lock());
while wakers.len() < NUM_WAKER {
match waiters.remove_first_waiter(|node| {
if node.permits <= rem {
rem -= node.permits;
node.permits = 0;
true
} else {
node.permits -= rem;
rem = 0;
false
}
}) {
None => break,
Some(waiter) => {
if let Some(waker) = waiter.waker.take() {
wakers.push(waker);
}
}
}
}
if rem > 0 && waiters.is_empty() {
let permits = rem;
let prev = self.permits.fetch_add(permits, Ordering::Release);
assert!(
prev.checked_add(permits).is_some(),
"number of added permits ({permits}) would overflow usize::MAX (prev: {prev})"
);
rem = 0;
}
drop(waiters);
for w in wakers.drain(..) {
w.wake();
}
}
}
}
#[derive(Debug)]
pub(crate) struct Acquire<'a> {
permits: usize,
index: Option<usize>,
semaphore: &'a Semaphore,
done: bool,
}
impl Drop for Acquire<'_> {
fn drop(&mut self) {
if let Some(index) = self.index {
let mut waiters = self.semaphore.waiters.lock();
let mut acquired = 0;
waiters.remove_waiter(index, |node| {
acquired = self.permits - node.permits;
node.permits = 0;
true
});
waiters.with_mut(index, |_| true); // drop
if acquired > 0 {
self.semaphore.insert_permits_with_lock(acquired, waiters);
}
}
}
}
impl Acquire<'_> {
pub(crate) fn poll_once(&mut self, waker: &Waker) -> Poll<()> {
let Self {
permits,
index,
semaphore,
done,
} = self;
if *done {
return Poll::Ready(());
}
match index {
Some(idx) => {
let mut waiters = semaphore.waiters.lock();
let mut ready = false;
waiters.with_mut(*idx, |node| {
if node.permits > 0 {
let update_waker = node.waker.as_ref().is_none_or(|w| !w.will_wake(waker));
if update_waker {
node.waker = Some(waker.clone());
}
false
} else {
ready = true;
true
}
});
if ready {
*index = None;
*done = true;
return Poll::Ready(());
}
}
None => {
// not yet enqueued
let needed = *permits;
if acquired_or_enqueue(semaphore, needed, index, Some(waker), true) {
*done = true;
return Poll::Ready(());
}
}
};
Poll::Pending
}
}
impl Future for Acquire<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
this.poll_once(cx.waker())
}
}
/// Returns `true` if successfully acquired the semaphore; `false` otherwise.
fn acquired_or_enqueue(
sem: &Semaphore,
needed: usize,
idx: &mut Option<usize>,
waker: Option<&Waker>,
enqueue_last: bool,
) -> bool {
let mut current = sem.permits.load(Ordering::Acquire);
let mut lock = None;
loop {
let (remaining, next) = if current >= needed {
(0, current - needed)
} else {
(needed - current, 0)
};
if remaining > 0 && lock.is_none() {
// No permits were immediately available, so this permit will
// (probably) need to wait. We'll need to acquire a lock on the
// wait queue before continuing. We need to do this _before_ the
// CAS that sets the new value of the semaphore's `permits`
// counter. Otherwise, if we subtract the permits and then
// acquire the lock, we might miss additional permits being
// added while waiting for the lock.
lock = Some(sem.waiters.lock());
}
if let Err(actual) =
sem.permits
.compare_exchange(current, next, Ordering::AcqRel, Ordering::Acquire)
{
// other thread changed the permits; retry
current = actual;
continue;
}
// all needed permits were acquired
if remaining == 0 {
return true;
}
// all available permits were acquired, but more are needed;
// enqueue a waiter with the remaining needed permits
let mut waiters = lock.take().unwrap_or_else(|| {
unreachable!("lock must be acquired when remaining {remaining} > 0");
});
if enqueue_last {
waiters.register_waiter_to_tail(idx, || {
Some(WaitNode {
permits: remaining,
waker: waker.cloned(),
})
});
} else {
waiters.register_waiter_to_head(idx, || {
Some(WaitNode {
permits: remaining,
waker: waker.cloned(),
})
});
}
return false;
}
}