Skip to content

Commit 0b3dd81

Browse files
authored
Merge pull request #24 from jsr-core/refine-asyncutil
Refine for `@core/asyncutil`
2 parents c86ef00 + 66f62fb commit 0b3dd81

28 files changed

+409
-291
lines changed

.github/workflows/test.yml

+17-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ env:
44
DENO_VERSION: 1.x
55

66
on:
7-
schedule:
8-
- cron: "0 7 * * 0"
97
push:
108
branches:
119
- main
@@ -36,8 +34,23 @@ jobs:
3634
deno-version: ${{ env.DENO_VERSION }}
3735
- name: Test
3836
run: |
39-
deno task test
37+
deno task test:coverage
4038
timeout-minutes: 5
41-
- name: JSR publish (dry-run)
39+
- run: |
40+
deno task coverage --lcov > coverage.lcov
41+
- uses: codecov/codecov-action@v4
42+
with:
43+
os: ${{ runner.os }}
44+
files: ./coverage.lcov
45+
token: ${{ secrets.CODECOV_TOKEN }}
46+
47+
jsr-publish:
48+
runs-on: ubuntu-latest
49+
steps:
50+
- uses: actions/checkout@v4
51+
- uses: denoland/setup-deno@v1
52+
with:
53+
deno-version: ${{ env.DENO_VERSION }}
54+
- name: Publish (dry-run)
4255
run: |
4356
deno publish --dry-run

.gitmessage

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
# **Conventional Commits**
3+
#
4+
# <type>[optional scope]: <description>
5+
#
6+
# feat: feature (minor)
7+
# deps: dependencies (minor/patch)
8+
# fix: bug fix (patch)
9+
# refactor: refactoring code
10+
# test: test fix; no code change
11+
# docs: documentation fix; no code change
12+
# style: formatting, missing semi colons, etc; no code change
13+
# chore: updating build tasks, package manager configs, etc; no code change
14+
#
15+
# **Install**
16+
#
17+
# git config commit.template .gitmessage
18+
#
19+
# **Reference**
20+
#
21+
# - https://www.conventionalcommits.org/en/v1.0.0/

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2021 Alisue <[email protected]>
1+
Copyright 2024 jsr-core
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to deal

README.md

+73-80
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,45 @@
1-
# async
1+
# asyncutil
22

3-
[![jsr](https://img.shields.io/jsr/v/%40lambdalisue/async?logo=javascript&logoColor=white)](https://jsr.io/@lambdalisue/async)
4-
[![denoland](https://img.shields.io/github/v/release/lambdalisue/deno-async?logo=deno&label=denoland)](https://github.com/lambdalisue/deno-async/releases)
5-
[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/async/mod.ts)
6-
[![Test](https://github.com/lambdalisue/deno-async/workflows/Test/badge.svg)](https://github.com/lambdalisue/deno-async/actions?query=workflow%3ATest)
3+
[![Test](https://github.com/jsr-core/asyncutil/actions/workflows/test.yml/badge.svg)](https://github.com/jsr-core/asyncutil/actions/workflows/test.yml)
74

8-
Asynchronous primitive modules for [Deno][deno].
9-
10-
[python's asyncio]: https://docs.python.org/3/library/asyncio.html
11-
[deno]: https://deno.land/
5+
Asynchronous primitive utility pack.
126

137
## Usage
148

15-
### Barrier
9+
### AsyncValue
1610

17-
`Barrier` is a synchronization primitive that allows multiple tasks to wait
18-
until all of them have reached a certain point of execution before continuing.
11+
`AsyncValue` is a class that wraps a value and allows it to be set
12+
asynchronously.
1913

2014
```ts
21-
import { Barrier } from "https://deno.land/x/async@$MODULE_VERSION/barrier.ts";
22-
23-
const barrier = new Barrier(3);
24-
25-
async function worker(id: number) {
26-
console.log(`worker ${id} is waiting`);
27-
await barrier.wait();
28-
console.log(`worker ${id} is done`);
29-
}
15+
import { assertEquals } from "@std/assert";
16+
import { AsyncValue } from "@core/asyncutil/async-value";
3017

31-
worker(1);
32-
worker(2);
33-
worker(3);
18+
const v = new AsyncValue(0);
19+
assertEquals(await v.get(), 0);
20+
await v.set(1);
21+
assertEquals(await v.get(), 1);
3422
```
3523

36-
### WaitGroup
24+
### Barrier
3725

38-
`WaitGroup` is a synchronization primitive that enables promises to coordinate
39-
and synchronize their execution. It is particularly useful in scenarios where a
40-
specific number of tasks must complete before the program can proceed.
26+
`Barrier` is a synchronization primitive that allows multiple tasks to wait
27+
until all of them have reached a certain point of execution before continuing.
4128

4229
```ts
43-
import { delay } from "https://deno.land/[email protected]/async/delay.ts";
44-
import { WaitGroup } from "https://deno.land/x/async@$MODULE_VERSION/wait_group.ts";
30+
import { Barrier } from "@core/asyncutil/barrier";
4531

46-
const wg = new WaitGroup();
32+
const barrier = new Barrier(3);
4733

4834
async function worker(id: number) {
49-
wg.add(1);
5035
console.log(`worker ${id} is waiting`);
51-
await delay(100);
36+
await barrier.wait();
5237
console.log(`worker ${id} is done`);
53-
wg.done();
5438
}
5539

5640
worker(1);
5741
worker(2);
5842
worker(3);
59-
await wg.wait();
6043
```
6144

6245
### Lock/RwLock
@@ -65,8 +48,8 @@ await wg.wait();
6548
shared value.
6649

6750
```ts
68-
import { AsyncValue } from "https://deno.land/x/async@$MODULE_VERSION/testutil.ts";
69-
import { Lock } from "https://deno.land/x/async@$MODULE_VERSION/lock.ts";
51+
import { AsyncValue } from "@core/asyncutil/async-value";
52+
import { Lock } from "@core/asyncutil/lock";
7053

7154
// Critical section
7255
const count = new Lock(new AsyncValue(0));
@@ -82,8 +65,8 @@ as long as there are no writers holding the lock. Writers block all other
8265
readers and writers until the write operation completes.
8366

8467
```ts
85-
import { AsyncValue } from "https://deno.land/x/async@$MODULE_VERSION/testutil.ts";
86-
import { RwLock } from "https://deno.land/x/async@$MODULE_VERSION/rw_lock.ts";
68+
import { AsyncValue } from "@core/asyncutil/async-value";
69+
import { RwLock } from "@core/asyncutil/rw-lock";
8770

8871
const count = new RwLock(new AsyncValue(0));
8972

@@ -117,8 +100,8 @@ This is a low-level primitive. Use `Lock` instead of `Mutex` if you need to
117100
access a shared value concurrently.
118101

119102
```ts
120-
import { AsyncValue } from "https://deno.land/x/async@$MODULE_VERSION/testutil.ts";
121-
import { Mutex } from "https://deno.land/x/async@$MODULE_VERSION/mutex.ts";
103+
import { AsyncValue } from "@core/asyncutil/async-value";
104+
import { Mutex } from "@core/asyncutil/mutex";
122105

123106
const count = new AsyncValue(0);
124107

@@ -127,13 +110,12 @@ async function doSomething() {
127110
await count.set(v + 1);
128111
}
129112

130-
// Critical section
131113
const mu = new Mutex();
132-
await mu.acquire();
133-
try {
114+
115+
// Critical section
116+
{
117+
using _lock = await mu.acquire();
134118
await doSomething();
135-
} finally {
136-
mu.release();
137119
}
138120
```
139121

@@ -143,9 +125,9 @@ try {
143125
notification.
144126

145127
```ts
146-
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
147-
import { promiseState } from "https://deno.land/x/async@$MODULE_VERSION/state.ts";
148-
import { Notify } from "https://deno.land/x/async@$MODULE_VERSION/notify.ts";
128+
import { assertEquals } from "@std/assert";
129+
import { promiseState } from "@core/asyncutil/promise-state";
130+
import { Notify } from "@core/asyncutil/notify";
149131

150132
const notify = new Notify();
151133
const waiter1 = notify.notified();
@@ -158,14 +140,32 @@ assertEquals(await promiseState(waiter1), "fulfilled");
158140
assertEquals(await promiseState(waiter2), "fulfilled");
159141
```
160142

143+
### promiseState
144+
145+
`promiseState` is used to determine the state of the promise. Mainly for testing
146+
purpose.
147+
148+
```typescript
149+
import { promiseState } from "@core/asyncutil/promise-state";
150+
151+
const p1 = Promise.resolve("Resolved promise");
152+
console.log(await promiseState(p1)); // fulfilled
153+
154+
const p2 = Promise.reject("Rejected promise").catch(() => undefined);
155+
console.log(await promiseState(p2)); // rejected
156+
157+
const p3 = new Promise(() => undefined);
158+
console.log(await promiseState(p3)); // pending
159+
```
160+
161161
### Queue/Stack
162162

163163
`Queue` is a queue implementation that allows for adding and removing elements,
164164
with optional waiting when popping elements from an empty queue.
165165

166166
```ts
167-
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
168-
import { Queue } from "https://deno.land/x/async@$MODULE_VERSION/queue.ts";
167+
import { assertEquals } from "@std/assert";
168+
import { Queue } from "@core/asyncutil/queue";
169169

170170
const queue = new Queue<number>();
171171
queue.push(1);
@@ -180,8 +180,8 @@ assertEquals(await queue.pop(), 3);
180180
with optional waiting when popping elements from an empty stack.
181181

182182
```ts
183-
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
184-
import { Stack } from "https://deno.land/x/async@$MODULE_VERSION/stack.ts";
183+
import { assertEquals } from "@std/assert";
184+
import { Stack } from "@core/asyncutil/stack";
185185

186186
const stack = new Stack<number>();
187187
stack.push(1);
@@ -198,7 +198,7 @@ A semaphore that allows a limited number of concurrent executions of an
198198
operation.
199199

200200
```ts
201-
import { Semaphore } from "https://deno.land/x/async@$MODULE_VERSION/semaphore.ts";
201+
import { Semaphore } from "@core/asyncutil/semaphore";
202202

203203
const sem = new Semaphore(5);
204204
const worker = () => {
@@ -209,37 +209,30 @@ const worker = () => {
209209
await Promise.all([...Array(10)].map(() => worker()));
210210
```
211211

212-
### promiseState
213-
214-
`promiseState` is used to determine the state of the promise. Mainly for testing
215-
purpose.
216-
217-
```typescript
218-
import { promiseState } from "https://deno.land/x/async@$MODULE_VERSION/mod.ts";
219-
220-
const p1 = Promise.resolve("Resolved promise");
221-
console.log(await promiseState(p1)); // fulfilled
222-
223-
const p2 = Promise.reject("Rejected promise").catch(() => undefined);
224-
console.log(await promiseState(p2)); // rejected
212+
### WaitGroup
225213

226-
const p3 = new Promise(() => undefined);
227-
console.log(await promiseState(p3)); // pending
228-
```
214+
`WaitGroup` is a synchronization primitive that enables promises to coordinate
215+
and synchronize their execution. It is particularly useful in scenarios where a
216+
specific number of tasks must complete before the program can proceed.
229217

230-
### AsyncValue
218+
```ts
219+
import { delay } from "@std/async/delay";
220+
import { WaitGroup } from "@core/asyncutil/wait-group";
231221

232-
`AsyncValue` is a class that wraps a value and allows it to be set
233-
asynchronously.
222+
const wg = new WaitGroup();
234223

235-
```ts
236-
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
237-
import { AsyncValue } from "https://deno.land/x/async@$MODULE_VERSION/testutil.ts";
224+
async function worker(id: number) {
225+
wg.add(1);
226+
console.log(`worker ${id} is waiting`);
227+
await delay(100);
228+
console.log(`worker ${id} is done`);
229+
wg.done();
230+
}
238231

239-
const v = new AsyncValue(0);
240-
assertEquals(await v.get(), 0);
241-
await v.set(1);
242-
assertEquals(await v.get(), 1);
232+
worker(1);
233+
worker(2);
234+
worker(3);
235+
await wg.wait();
243236
```
244237

245238
## License

testutil.ts async_value.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@
22
* A class that wraps a value and allows it to be set asynchronously.
33
*
44
* ```ts
5-
* import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
6-
* import { AsyncValue } from "https://deno.land/x/async@$MODULE_VERSION/testutil.ts";
5+
* import { assertEquals } from "@std/assert";
6+
* import { AsyncValue } from "@core/asyncutil/async-value";
77
*
88
* const v = new AsyncValue(0);
99
* assertEquals(await v.get(), 0);
1010
* await v.set(1);
1111
* assertEquals(await v.get(), 1);
1212
* ```
13-
*
14-
* @typeParam T - The type of the value.
1513
*/
1614
export class AsyncValue<T> {
1715
#value: T;
1816

1917
/**
2018
* Constructs a new AsyncValue with the given initial value.
2119
*
22-
* @param value - The initial value.
20+
* @param value The initial value.
2321
*/
2422
constructor(value: T) {
2523
this.#value = value;

testutil_test.ts async_value_test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts";
2-
import { AsyncValue } from "./testutil.ts";
1+
import { assertEquals } from "@std/assert";
2+
import { AsyncValue } from "./async_value.ts";
33

44
Deno.test("AsyncValue", async (t) => {
55
await t.step(

barrier.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Notify } from "./notify.ts";
1010
* unblock and continue executing.
1111
*
1212
* ```ts
13-
* import { Barrier } from "https://deno.land/x/async@$MODULE_VERSION/barrier.ts";
13+
* import { Barrier } from "@core/asyncutil/barrier";
1414
*
1515
* const barrier = new Barrier(3);
1616
*
@@ -32,8 +32,8 @@ export class Barrier {
3232
/**
3333
* Creates a new `Barrier` that blocks until `size` threads have called `wait`.
3434
*
35-
* @param size - The number of threads that must reach the barrier before it unblocks.
36-
* @throws Error if size is negative.
35+
* @param size The number of threads that must reach the barrier before it unblocks.
36+
* @throws Error if the size is negative.
3737
*/
3838
constructor(size: number) {
3939
if (size < 0) {
@@ -46,15 +46,16 @@ export class Barrier {
4646
* Wait for all threads to reach the barrier.
4747
* Blocks until all threads reach the barrier.
4848
*/
49-
async wait(): Promise<void> {
49+
async wait({ signal }: { signal?: AbortSignal } = {}): Promise<void> {
50+
signal?.throwIfAborted();
5051
this.#rest -= 1;
5152
if (this.#rest === 0) {
5253
await Promise.all([
53-
this.#notify.notified(),
54+
this.#notify.notified({ signal }),
5455
this.#notify.notifyAll(),
5556
]);
5657
} else {
57-
await this.#notify.notified();
58+
await this.#notify.notified({ signal });
5859
}
5960
}
6061
}

0 commit comments

Comments
 (0)