2022-07-01 18:19:57 +00:00
|
|
|
# Nim-Libp2p
|
2023-01-20 14:47:40 +00:00
|
|
|
# Copyright (c) 2023 Status Research & Development GmbH
|
2022-07-01 18:19:57 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
2021-01-04 18:59:05 +00:00
|
|
|
|
2023-06-07 11:12:49 +00:00
|
|
|
{.push raises: [].}
|
2021-05-21 16:27:01 +00:00
|
|
|
|
2021-01-04 18:59:05 +00:00
|
|
|
import sequtils
|
|
|
|
import chronos, chronicles
|
|
|
|
|
|
|
|
# TODO: this should probably go in chronos
|
|
|
|
|
|
|
|
logScope:
|
|
|
|
topics = "libp2p semaphore"
|
|
|
|
|
|
|
|
type AsyncSemaphore* = ref object of RootObj
|
|
|
|
size*: int
|
2021-01-14 09:11:12 +00:00
|
|
|
count: int
|
|
|
|
queue: seq[Future[void]]
|
2021-01-04 18:59:05 +00:00
|
|
|
|
2021-01-14 09:11:12 +00:00
|
|
|
proc newAsyncSemaphore*(size: int): AsyncSemaphore =
|
|
|
|
AsyncSemaphore(size: size, count: size)
|
|
|
|
|
|
|
|
proc `count`*(s: AsyncSemaphore): int =
|
|
|
|
s.count
|
2021-01-04 18:59:05 +00:00
|
|
|
|
|
|
|
proc tryAcquire*(s: AsyncSemaphore): bool =
|
|
|
|
## Attempts to acquire a resource, if successful
|
|
|
|
## returns true, otherwise false
|
|
|
|
##
|
|
|
|
|
|
|
|
if s.count > 0 and s.queue.len == 0:
|
|
|
|
s.count.dec
|
|
|
|
trace "Acquired slot", available = s.count, queue = s.queue.len
|
|
|
|
return true
|
|
|
|
|
|
|
|
proc acquire*(s: AsyncSemaphore): Future[void] =
|
|
|
|
## Acquire a resource and decrement the resource
|
|
|
|
## counter. If no more resources are available,
|
|
|
|
## the returned future will not complete until
|
2021-01-14 09:11:12 +00:00
|
|
|
## the resource count goes above 0.
|
2021-01-04 18:59:05 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
let fut = newFuture[void]("AsyncSemaphore.acquire")
|
|
|
|
if s.tryAcquire():
|
|
|
|
fut.complete()
|
|
|
|
return fut
|
|
|
|
|
2021-01-14 09:11:12 +00:00
|
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
|
|
fut.cancelCallback = nil
|
|
|
|
if not fut.finished:
|
|
|
|
s.queue.keepItIf(it != fut)
|
|
|
|
|
|
|
|
fut.cancelCallback = cancellation
|
|
|
|
|
2021-01-04 18:59:05 +00:00
|
|
|
s.queue.add(fut)
|
2021-01-14 09:11:12 +00:00
|
|
|
|
2021-01-04 18:59:05 +00:00
|
|
|
trace "Queued slot", available = s.count, queue = s.queue.len
|
|
|
|
return fut
|
|
|
|
|
2022-02-24 16:31:47 +00:00
|
|
|
proc forceAcquire*(s: AsyncSemaphore) =
|
|
|
|
## ForceAcquire will always succeed,
|
|
|
|
## creating a temporary slot if required.
|
|
|
|
## This temporary slot will stay usable until
|
|
|
|
## there is less `acquire`s than `release`s
|
|
|
|
s.count.dec
|
|
|
|
|
2021-01-04 18:59:05 +00:00
|
|
|
proc release*(s: AsyncSemaphore) =
|
|
|
|
## Release a resource from the semaphore,
|
|
|
|
## by picking the first future from the queue
|
|
|
|
## and completing it and incrementing the
|
|
|
|
## internal resource count
|
|
|
|
##
|
|
|
|
|
|
|
|
doAssert(s.count <= s.size)
|
|
|
|
|
|
|
|
if s.count < s.size:
|
|
|
|
trace "Releasing slot", available = s.count, queue = s.queue.len
|
|
|
|
|
2022-02-24 16:31:47 +00:00
|
|
|
s.count.inc
|
|
|
|
while s.queue.len > 0:
|
2021-01-14 09:11:12 +00:00
|
|
|
var fut = s.queue[0]
|
|
|
|
s.queue.delete(0)
|
2021-01-04 18:59:05 +00:00
|
|
|
if not fut.finished():
|
2022-02-24 16:31:47 +00:00
|
|
|
s.count.dec
|
2021-01-04 18:59:05 +00:00
|
|
|
fut.complete()
|
2022-02-24 16:31:47 +00:00
|
|
|
break
|
2021-01-04 18:59:05 +00:00
|
|
|
|
|
|
|
trace "Released slot", available = s.count, queue = s.queue.len
|
|
|
|
return
|