/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
package std.sync
public class Semaphore {
// Since there is a `prop` to read this field, we should make it atomic.
private var countValue: AtomicInt64
private let monitor: Monitor
private let initValue: Int64
/**
* Construct a semephore with an initial value.
* Throw IllegalArgumentException, if `count` is negative.
*/
public init(count: Int64) {
if (count < 0) {
throw IllegalArgumentException("Negative count.")
}
initValue = count
countValue = AtomicInt64(count)
monitor = Monitor()
}
/**
* Return the current available counts.
*/
public prop count: Int64 {
get() {
return countValue.load()
}
}
/**
* Acquire `amount` from the semaphore.
* If there is no sufficient available counts,
* the current thread will be blocked until enough counts become available.
* The acquisition is atomic, which means it will never acquire partial counts before blocked.
* Throw IllegalArgumentException, if `amount` is negative or larger than the initial value.
* The acquisition order is not guranteed under contention.
*/
public func acquire(amount!: Int64 = 1): Unit {
if (amount < 0 || amount > initValue) {
throw IllegalArgumentException("Invalid amount to acquire.")
}
synchronized(monitor) {
while (countValue.load() < amount) {
monitor.wait()
}
countValue.fetchSub(amount)
}
}
/**
* Try to acquire `amount` from the semaphore.
* If there is no sufficient available counts, return false;
* otherwise, return true.
* Throw IllegalArgumentException, if `amount` is negative or larger than the initial value.
*/
public func tryAcquire(amount!: Int64 = 1): Bool {
if (amount < 0 || amount > initValue) {
throw IllegalArgumentException("Invalid amount to acquire.")
}
synchronized(monitor) {
if (countValue.load() < amount) {
return false
} else {
countValue.fetchSub(amount)
return true
}
}
}
/**
* Release `amount` to the semaphore.
* Wake blocked threads if there are enough counts for them.
* The number of accumulated counts will never exceed the initial value.
* Throw IllegalArgumentException, if `amount` is negative or larger than the initial value.
* All events before `release()` happens-before events after `acquire/tryAcquire()`.
*/
public func release(amount!: Int64 = 1): Unit {
if (amount < 0 || amount > initValue) {
throw IllegalArgumentException("Invalid amount to release.")
}
synchronized(monitor) {
let oldValue = countValue.load()
let newValue = oldValue + amount
// The new value to store cannot exceed the initial value.
if (newValue > initValue) {
countValue.store(initValue)
} else {
countValue.store(newValue)
}
monitor.notifyAll()
}
}
}