From b7f324918085c63745be91ee5d444c219bb45829 Mon Sep 17 00:00:00 2001 From: Avril Date: Mon, 14 Feb 2022 19:05:28 +0000 Subject: [PATCH] Update README for a section on `Semaphore` (semaphore.js). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update README for the new behaviour around `AsyncTimeout` regarding being passed async functions. (asynctimeout.js) Fortune for async-timeout-js's current commit: Small curse − 小凶 --- README.md | 39 ++++++++++++++++++++++++++++++++++++++- semaphore.js | 4 +++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 31eb3d7..c3582f9 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,18 @@ There are two methods on the class `AsyncTimeout(func, time)`: * `timeout()`: Creates an `await`able promise that will resolve to the value of `func()` when the timeout `time` is reached. * `interval()`: Creates a `for await`able async iterator that will resolve to the value of `func()` each `time` it is called. To cancel the interval, simply break from the loop or stop calling `.next()` on the returned async iterator. +### Static methods +There are two helper functions in `AsyncTimeout`: +* `await_timeout(time)`: Returns a promise that resolves with no value after `time`. +* `await_interval(time)`: Returns an infinite async iterator that yields no value for every interval of `time`. + ## Parameters * `thing` - The function to call after the timeout/interval. The result of the promise (or the yield for `interval()`) is the result of this function call. If the funtion throws, then the rejection of the promise will be that error thrown. +If this is an async function, then it is awaited inside the returned/yielded promise, and its resolution becomes the resolved value for the returned/yielded promise; likewise, its rejection becomes the rejection for the returned/yielded promise. * `interval` - The time to wait for the timeout or interval ## Example usages -See `example()` in the file for detail. +See `timeout_example()`, or `try_example()` in the file for detail. ### `setTimeout()` promisified `await (new AsyncTimeout(() => "value", 100)).timeout() /* === "value" */` is `setTimeout(()=> "value", 100)` promisified. @@ -24,4 +30,35 @@ See `example()` in the file for detail. This is an infinite iterator. To cancel the interval, simply break out of the `for await` loop or otherwise stop using the iterator returned from `.interval()`. +# Semaphore - A simple async seamphore implementation for JavaScript +The class `Semaphore(cap)` inside `semaphore.js` is a robust async semaphore implementation. +Its locking permits are queued (meaning, multiple waiting *acquire*ing calls will resolve in the order they are called.) + +See the file `semaphore.js` for more information on how it's used, and the function `sem_example(cap || 1)` in that file. + +## Methods +Raw acquire/release of semaphore lock: + +* `acquire_raw()`: An async function that resolves when a lock is available, acquiring that lock. You must `release_raw()` this lock after its use. +* `release_raw()`: A function that releases a semaphore lock, or passes it to a waiting *acquireing* call. +* `try_acquire_raw()`: Attempts to acquire a semaphore lock if one is available, and returns `true` if one was available and the lock was taken, `false` if there were no available locks and one was not taken. If this function returns `true`, you must pair it with a `release_raw()` call after the lock has been used. + +Automatic semaphore lock acquisition functions: + +* `using(func)`: An async function that acquires a lock, runs `func()` (and awaits it if it is an async function), and then releases the lock no matter if the function threw or not. +* `bind_using(func)`: A function that returns a function that when invoked, acts as `using(func)`. This can be used to bind functions to the semaphore to be executed later. +* `try_using(func, opt)`: A function that attempts to acquire a lock, runs `func()` if it can, releases the lock, then returns the value from the `func()` call if it succeeded. If a lock cannot be acquired, `opt` can be used to dictate what or how values are returned (see doc comment on `try_using` for more information on how to use this parameter, by default the return value on a failure to acquire a lock immediately is `false`.) If `func` is an async function, a promise is returned that when awaited, will release the lock and return the resolution of the promise (or throw the exception of the promise's rejection.) + +### Static methods + +* `Semaphore.mutex()`: Returns a new mutex (a `Semaphore` instance with a capacity of `1`.) + +## Parameters + +* `cap`: An integer larger than 0 which dictates how many locks can be acquired at one time before *acquire*ing methods need to wait for one to be available. + +## Fields +These fields should almost never be modified directly. +* `length`: The number of locks currently held. +* `capacity`: The max number of concurrent locks allowed to be held before new *acquire*ing methods must wait for a lock to be released. diff --git a/semaphore.js b/semaphore.js index 4be8bc8..b1ea04a 100644 --- a/semaphore.js +++ b/semaphore.js @@ -131,7 +131,9 @@ function Semaphore(cap) // `rv` is a promise, wrap it's awaiting inside a new promise that release the lock regardless of if awaiting `rv` throws. try { return wrap_success(await rv, true); - } finally { self.release_raw(); } + } /*catch(e) { // Prevent an exception from crashing, use it to resolve the promise instead. //XXX: This might not be needed, since we are returning this promise, it should be awaited which will prevent the exception from bubbling up. + return wrap_success(opt.or, false); + }*/ finally { self.release_raw(); } })(); else { // `rv` is not a promise, we can release the lock and return the value