use crate::{
error::{TryAcquireError, TryAcquireError::*},
lock::Lock,
permit::Permit,
queue::{WaitKey, WaitQueue},
waiter::{AcquireFuture, Waiter},
};
use alloc::sync::Arc;
use core::{
sync::atomic::{AtomicUsize, Ordering},
task::Waker,
};
pub type Priority = i32;
const CLOSED: usize = 1 << (usize::BITS - 1);
const HAS_WAITERS: usize = 1 << (usize::BITS - 2);
const PERMIT_MASK: usize = HAS_WAITERS - 1;
pub(crate) enum RegisterResult {
Acquired,
Queued { key: WaitKey, waiter: Arc<Waiter> },
Closed,
}
pub struct PrioritySemaphore {
state: AtomicUsize,
pub(crate) waiters: Lock<WaitQueue>,
max_permits: usize,
}
impl core::fmt::Debug for PrioritySemaphore {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PrioritySemaphore")
.field("available", &self.available_permits())
.field("queued", &self.queued())
.field("max_permits", &self.max_permits)
.field("closed", &self.is_closed())
.finish()
}
}
impl PrioritySemaphore {
pub const fn new(permits: usize) -> Self {
assert!(permits <= Self::MAX_PERMITS, "too many semaphore permits");
Self {
state: AtomicUsize::new(permits),
waiters: Lock::new(WaitQueue::new()),
max_permits: permits,
}
}
pub const MAX_PERMITS: usize = PERMIT_MASK;
pub fn acquire(self: &Arc<Self>, priority: Priority) -> AcquireFuture {
AcquireFuture::new(self.clone(), priority)
}
pub fn try_acquire(self: &Arc<Self>, _priority: Priority) -> Result<Permit, TryAcquireError> {
self.try_take()?;
Ok(Permit::new(self.clone()))
}
pub fn close(&self) {
let entries = {
let mut queue = self.waiters.lock();
let previous = self.state.fetch_or(CLOSED, Ordering::AcqRel);
if previous & CLOSED != 0 {
return;
}
let entries = queue.drain();
for entry in &entries {
entry.waiter.close();
}
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
entries
};
for entry in entries {
entry.waker.wake();
}
}
pub fn available_permits(&self) -> usize {
self.state.load(Ordering::Acquire) & PERMIT_MASK
}
pub fn queued(&self) -> usize {
self.waiters.lock().len()
}
pub fn is_closed(&self) -> bool {
self.state.load(Ordering::Acquire) & CLOSED != 0
}
pub(crate) fn register(&self, priority: Priority, waker: &Waker) -> RegisterResult {
let mut queue = self.waiters.lock();
let previous = self.state.fetch_or(HAS_WAITERS, Ordering::AcqRel);
if previous & CLOSED != 0 {
if queue.is_empty() {
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
}
return RegisterResult::Closed;
}
if queue.is_empty() {
let state = self.state.load(Ordering::Acquire);
debug_assert_eq!(state & CLOSED, 0);
if state & PERMIT_MASK != 0 {
match self.state.compare_exchange(
state,
state - 1,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
return RegisterResult::Acquired;
}
Err(actual) => {
if actual & PERMIT_MASK != 0 {
let taken = self.state.compare_exchange(
actual,
actual - 1,
Ordering::AcqRel,
Ordering::Acquire,
);
if taken.is_ok() {
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
return RegisterResult::Acquired;
}
}
}
}
}
}
let waiter = Arc::new(Waiter::new());
let key = queue.push(priority, waiter.clone(), waker.clone());
RegisterResult::Queued { key, waiter }
}
pub(crate) fn refresh_waker(&self, key: WaitKey, waiter: &Waiter, waker: &Waker) {
let mut queue = self.waiters.lock();
if waiter.is_waiting() {
queue.update_waker(key, waker);
}
}
pub(crate) fn cancel_waiter(&self, key: WaitKey, waiter: &Waiter) {
let assigned = {
let mut queue = self.waiters.lock();
if waiter.is_waiting() {
let removed = queue.remove(key);
if removed.is_some() && queue.is_empty() {
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
}
false
} else {
waiter.is_assigned()
}
};
if assigned {
self.release_one();
}
}
pub(crate) fn release_one(&self) {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & HAS_WAITERS != 0 {
self.release_slow();
return;
}
debug_assert!((state & PERMIT_MASK) < self.max_permits);
match self.state.compare_exchange_weak(
state,
state + 1,
Ordering::Release,
Ordering::Acquire,
) {
Ok(_) => return,
Err(actual) => state = actual,
}
}
}
pub(crate) fn try_take(&self) -> Result<(), TryAcquireError> {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & CLOSED != 0 {
return Err(Closed);
}
if state & HAS_WAITERS != 0 || state & PERMIT_MASK == 0 {
return Err(NoPermits);
}
match self.state.compare_exchange_weak(
state,
state - 1,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return Ok(()),
Err(actual) => state = actual,
}
}
}
fn release_slow(&self) {
let wake = {
let mut queue = self.waiters.lock();
let state = self.state.load(Ordering::Acquire);
if state & CLOSED == 0 {
if let Some(entry) = queue.pop() {
entry.waiter.assign();
if queue.is_empty() {
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
}
Some(entry.waker)
} else {
self.return_to_pool(&queue);
None
}
} else {
let entries = queue.drain();
for entry in &entries {
entry.waiter.close();
}
self.return_to_pool(&queue);
drop(queue);
for entry in entries {
entry.waker.wake();
}
return;
}
};
if let Some(waker) = wake {
waker.wake();
}
}
fn return_to_pool(&self, queue: &WaitQueue) {
debug_assert!(queue.is_empty());
self.state.fetch_and(!HAS_WAITERS, Ordering::Release);
let mut state = self.state.load(Ordering::Acquire);
loop {
debug_assert!((state & PERMIT_MASK) < self.max_permits);
match self.state.compare_exchange_weak(
state,
state + 1,
Ordering::Release,
Ordering::Acquire,
) {
Ok(_) => return,
Err(actual) => state = actual,
}
}
}
}