ciorent 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -86
- package/channel.d.ts +6 -15
- package/channel.js +1 -1
- package/concurrent.d.ts +4 -6
- package/index.d.ts +1 -2
- package/index.js +1 -1
- package/package.json +1 -1
- package/semaphore.d.ts +1 -1
- package/semaphore.js +1 -1
- package/topic.d.ts +3 -3
- package/topic.js +1 -1
- package/concurrent.js +0 -1
package/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
A lightweight, low-overhead concurrency library.
|
2
|
-
|
2
|
+
## Semaphore
|
3
3
|
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
4
4
|
|
5
5
|
```ts
|
@@ -23,50 +23,43 @@ const task = semaphore.task(
|
|
23
23
|
cio.concurrent(6, task, 4);
|
24
24
|
```
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
## Pubsub
|
27
|
+
A fast, simple publish-subscribe API.
|
28
28
|
|
29
29
|
```ts
|
30
|
-
import * as
|
30
|
+
import * as topic from 'ciorent/topic';
|
31
31
|
import * as cio from 'ciorent';
|
32
32
|
|
33
|
-
const
|
34
|
-
|
35
|
-
const task = async () => {
|
36
|
-
// Blocks until the latch is open
|
37
|
-
await latch.pause(fetchLatch);
|
33
|
+
const messages = topic.init<number>();
|
38
34
|
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
// A task that publish messages
|
36
|
+
const publisher = async () => {
|
37
|
+
for (let i = 0; i < 5; i++) {
|
38
|
+
await cio.sleep(100);
|
39
|
+
topic.pub(messages, i);
|
40
|
+
}
|
42
41
|
|
43
|
-
|
44
|
-
|
42
|
+
// Resolve all waiting promises
|
43
|
+
// And clear the value queue
|
44
|
+
topic.flush(messages);
|
45
45
|
}
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
prepare();
|
51
|
-
|
52
|
-
// Allows all previously blocked tasks to run
|
53
|
-
latch.open(fetchLatch);
|
54
|
-
|
55
|
-
// Reclose the latch
|
56
|
-
// Tasks that aren't blocked yet will be blocked
|
57
|
-
latch.reset(fetchLatch);
|
58
|
-
|
59
|
-
return p;
|
60
|
-
}
|
47
|
+
// Spawn 5 tasks that recieve messages
|
48
|
+
cio.concurrent(5, async (id: number) => {
|
49
|
+
const sub = topic.sub(messages);
|
61
50
|
|
62
|
-
|
63
|
-
|
51
|
+
while (true) {
|
52
|
+
// Block until the value is sent
|
53
|
+
const x = await topic.recieve(sub);
|
54
|
+
if (x == null) break;
|
55
|
+
console.log(`Task ${id}: ${x}`);
|
56
|
+
}
|
57
|
+
});
|
64
58
|
|
65
|
-
|
66
|
-
await main();
|
59
|
+
publisher();
|
67
60
|
```
|
68
61
|
|
69
|
-
|
62
|
+
## Channel
|
70
63
|
Channel is a synchronization primitive via message passing. A message may be sent over a channel, and another process is able to receive messages sent over a channel it has a reference to.
|
71
64
|
|
72
65
|
```ts
|
@@ -82,18 +75,15 @@ const run = async () => {
|
|
82
75
|
console.log('Sent', i);
|
83
76
|
}
|
84
77
|
|
85
|
-
//
|
86
|
-
//
|
87
|
-
channel.
|
78
|
+
// Resolve all waiting promises with `undefined`
|
79
|
+
// This is a way to tell the reciever to not listen to more data
|
80
|
+
channel.flush(c);
|
88
81
|
};
|
89
82
|
|
90
83
|
const log = async () => {
|
91
84
|
while (true) {
|
92
|
-
//
|
85
|
+
// Block until x is recieved
|
93
86
|
const x = await channel.recieve(c);
|
94
|
-
|
95
|
-
// 'recieve' returns undefined if
|
96
|
-
// The channel has been closed
|
97
87
|
if (x == null) break;
|
98
88
|
|
99
89
|
console.log('Recieved', x);
|
@@ -107,39 +97,51 @@ log();
|
|
107
97
|
console.log('Starting...');
|
108
98
|
```
|
109
99
|
|
110
|
-
|
111
|
-
|
100
|
+
## Latch
|
101
|
+
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
112
102
|
|
113
103
|
```ts
|
114
|
-
import * as
|
104
|
+
import * as latch from 'ciorent/latch';
|
115
105
|
import * as cio from 'ciorent';
|
116
106
|
|
117
|
-
const
|
107
|
+
const fetchLatch = latch.init();
|
118
108
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
109
|
+
const task = async () => {
|
110
|
+
// Blocks until the latch is open
|
111
|
+
await latch.pause(fetchLatch);
|
112
|
+
|
113
|
+
const res = await fetch('http://example.com');
|
114
|
+
console.log('Fetch status:', res.status);
|
125
115
|
}
|
126
116
|
|
127
|
-
|
128
|
-
|
129
|
-
|
117
|
+
const prepare = () => {
|
118
|
+
console.log('Run before fetch:', performance.now().toFixed(2));
|
119
|
+
}
|
130
120
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
}
|
136
|
-
});
|
121
|
+
const main = async () => {
|
122
|
+
const p = task();
|
123
|
+
await cio.sleep(500);
|
124
|
+
prepare();
|
137
125
|
|
138
|
-
|
126
|
+
// Allows all previously blocked tasks to run
|
127
|
+
latch.open(fetchLatch);
|
128
|
+
|
129
|
+
// Reclose the latch
|
130
|
+
// Tasks that aren't blocked yet will be blocked
|
131
|
+
latch.reset(fetchLatch);
|
132
|
+
|
133
|
+
return p;
|
134
|
+
}
|
135
|
+
|
136
|
+
// Run fetch after 500ms
|
137
|
+
await main();
|
138
|
+
|
139
|
+
// Run fetch after another 500ms
|
140
|
+
await main();
|
139
141
|
```
|
140
142
|
|
141
|
-
|
142
|
-
|
143
|
+
## Utilities
|
144
|
+
### Pausing
|
143
145
|
Delay the execution of a function for other asynchronous tasks to run.
|
144
146
|
```ts
|
145
147
|
import * as cio from 'ciorent';
|
@@ -169,7 +171,7 @@ task1();
|
|
169
171
|
task2();
|
170
172
|
```
|
171
173
|
|
172
|
-
|
174
|
+
### Sleep
|
173
175
|
A cross-runtime sleep function.
|
174
176
|
```ts
|
175
177
|
import { sleep } from 'ciorent';
|
@@ -178,23 +180,7 @@ await sleep(500);
|
|
178
180
|
console.log('Hi');
|
179
181
|
```
|
180
182
|
|
181
|
-
|
182
|
-
Control how many tasks can be executed concurrently.
|
183
|
-
```ts
|
184
|
-
import concurrent from 'ciorent/concurrent';
|
185
|
-
import * as cio from 'ciorent';
|
186
|
-
|
187
|
-
// Allow 3 tasks to run at the same time
|
188
|
-
const run = concurrent(3);
|
189
|
-
|
190
|
-
for (let id = 1; id <= 6; id++)
|
191
|
-
run(async () => {
|
192
|
-
await cio.sleep(Math.random() * 20 + 50);
|
193
|
-
console.log('Task', id, 'done');
|
194
|
-
});
|
195
|
-
```
|
196
|
-
|
197
|
-
## Spawning tasks
|
183
|
+
### Spawning tasks
|
198
184
|
Creating new tasks with controlled concurrency.
|
199
185
|
```ts
|
200
186
|
import * as cio from 'ciorent';
|
@@ -205,16 +191,11 @@ const task = async (id: number) => {
|
|
205
191
|
}
|
206
192
|
|
207
193
|
// Spawn and run 5 tasks sequentially
|
208
|
-
console.log('Running sequentially:');
|
194
|
+
console.log('Running 5 tasks sequentially:');
|
209
195
|
cio.sequential(5, task);
|
210
196
|
|
211
197
|
// Spawn and run 5 tasks concurrently
|
212
198
|
console.log('Running 5 tasks concurrently:');
|
213
199
|
cio.concurrent(5, task);
|
214
|
-
|
215
|
-
// Spawn and run 5 tasks, with the maximum
|
216
|
-
// tasks running concurrently set to 3
|
217
|
-
console.log('Running each 3 tasks concurrently:');
|
218
|
-
cio.concurrent(5, task, 3);
|
219
200
|
```
|
220
201
|
|
package/channel.d.ts
CHANGED
@@ -6,26 +6,22 @@ import type { QueueNode } from './fixed-queue';
|
|
6
6
|
* Describe a channel
|
7
7
|
*/
|
8
8
|
export interface Channel<T> {
|
9
|
-
/**
|
10
|
-
* Opening state of the channel
|
11
|
-
*/
|
12
|
-
0: boolean;
|
13
9
|
/**
|
14
10
|
* The head of the value queue
|
15
11
|
*/
|
16
|
-
|
12
|
+
0: QueueNode<T>;
|
17
13
|
/**
|
18
14
|
* The tail of the value queue
|
19
15
|
*/
|
20
|
-
|
16
|
+
1: QueueNode<T>;
|
21
17
|
/**
|
22
18
|
* The head of the Promise resolve queue
|
23
19
|
*/
|
24
|
-
|
20
|
+
2: QueueNode<(value?: T) => void>;
|
25
21
|
/**
|
26
22
|
* The tail of the Promise resolve queue
|
27
23
|
*/
|
28
|
-
|
24
|
+
3: QueueNode<(value?: T) => void>;
|
29
25
|
}
|
30
26
|
/**
|
31
27
|
* Create a channel
|
@@ -48,12 +44,7 @@ export declare const recieve: <T>(c: Channel<T>) => Promise<T | undefined>;
|
|
48
44
|
*/
|
49
45
|
export declare const poll: <T>(c: Channel<T>) => T | undefined;
|
50
46
|
/**
|
51
|
-
*
|
52
|
-
* @param c
|
53
|
-
*/
|
54
|
-
export declare const close: <T>(c: Channel<T>) => void;
|
55
|
-
/**
|
56
|
-
* Check whether a channel is still open
|
47
|
+
* Resolves all pending promises of a channel
|
57
48
|
* @param c
|
58
49
|
*/
|
59
|
-
export declare const
|
50
|
+
export declare const flush: <T>(c: Channel<T>) => void;
|
package/channel.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
export let init=()=>{let qu=[null,null];let resolveQu=[null,null];return[qu,qu,resolveQu,resolveQu]};export let send=(c,t)=>{if(c[3][1]!==null)(c[3]=c[3][1])[0](t);else c[0]=c[0][1]=[t,null]};export let recieve=(c)=>c[1][1]!==null?Promise.resolve((c[1]=c[1][1])[0]):new Promise((res)=>{c[2]=c[2][1]=[res,null]});export let poll=(c)=>c[1][1]!==null?(c[1]=c[1][1])[0]:undefined;export let flush=(c)=>{while(c[3][1]!==null)(c[3]=c[3][1])[0]()};
|
package/concurrent.d.ts
CHANGED
@@ -2,12 +2,10 @@
|
|
2
2
|
* @module Concurrency controls
|
3
3
|
*/
|
4
4
|
/**
|
5
|
-
* Describe
|
5
|
+
* Describe an async task
|
6
6
|
*/
|
7
|
-
export type
|
7
|
+
export type Task<T = unknown> = () => Promise<T>;
|
8
8
|
/**
|
9
|
-
*
|
10
|
-
* @param n
|
9
|
+
* Describe a concurrency controller
|
11
10
|
*/
|
12
|
-
|
13
|
-
export default _default;
|
11
|
+
export type Controller = <T>(task: Task<T>) => Promise<T>;
|
package/index.d.ts
CHANGED
@@ -24,6 +24,5 @@ export declare const sequential: (n: number, task: (id: number) => Promise<any>)
|
|
24
24
|
* Spawn n tasks that runs concurrently
|
25
25
|
* @param n
|
26
26
|
* @param task - The function to run
|
27
|
-
* @param concurrency - The amount of task to run concurrently
|
28
27
|
*/
|
29
|
-
export declare const concurrent: (n: number, task: (id: number) => Promise<any
|
28
|
+
export declare const concurrent: (n: number, task: (id: number) => Promise<any>) => Promise<any>;
|
package/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let pause=Promise.resolve();export let sleep=globalThis.Bun?.sleep??globalThis.process?.getBuiltinModule?.("timers/promises").setTimeout??((ms)=>new Promise((res)=>{setTimeout(res,ms)}));export let sequential=async(n,task)=>{for(let i=0;i<n;i++)await task(i)};export let concurrent=
|
1
|
+
export let pause=Promise.resolve();export let sleep=globalThis.Bun?.sleep??globalThis.process?.getBuiltinModule?.("timers/promises").setTimeout??((ms)=>new Promise((res)=>{setTimeout(res,ms)}));export let sequential=async(n,task)=>{for(let i=0;i<n;i++)await task(i)};export let concurrent=(n,task)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(i);return Promise.all(arr)};
|
package/package.json
CHANGED
package/semaphore.d.ts
CHANGED
@@ -32,6 +32,6 @@ export declare const pause: (s: Semaphore) => Promise<void>;
|
|
32
32
|
*/
|
33
33
|
export declare const signal: (s: Semaphore) => void;
|
34
34
|
/**
|
35
|
-
* Create a task that acquire a semaphore and release the access
|
35
|
+
* Create a task that acquire a semaphore and release the access when it's finished
|
36
36
|
*/
|
37
37
|
export declare const task: <F extends (...args: any[]) => Promise<any>>(s: Semaphore, f: F) => F;
|
package/semaphore.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as resolvedPromise}from".";export let init=(n)=>{let root=[null,null];return[n,root,root]};export let pause=(s)=>{s[0]--;if(s[0]<0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][1]=[r,null];return p}return resolvedPromise};export let signal=(s)=>{if(s[0]<0)(s[2]=s[2][1])[0]();s[0]++};export let task=(s,f)=>async(...a)=>{
|
1
|
+
import{pause as resolvedPromise}from".";export let init=(n)=>{let root=[null,null];return[n,root,root]};export let pause=(s)=>{s[0]--;if(s[0]<0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][1]=[r,null];return p}return resolvedPromise};export let signal=(s)=>{if(s[0]<0)(s[2]=s[2][1])[0]();s[0]++};export let task=(s,f)=>async(...a)=>{s[0]--;if(s[0]<0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][1]=[r,null];await p}try{return await f(...a)}finally{signal(s)}};
|
package/topic.d.ts
CHANGED
@@ -41,10 +41,10 @@ export declare const sub: <T extends {}>(t: Topic<T>) => Subscriber<T>;
|
|
41
41
|
*/
|
42
42
|
export declare const pub: <T extends {}>(t: Topic<T>, value: T) => void;
|
43
43
|
/**
|
44
|
-
* Resolve all waiting promises
|
44
|
+
* Resolve all waiting promises and clear all pending values
|
45
45
|
* @param t
|
46
46
|
*/
|
47
|
-
export declare const
|
47
|
+
export declare const flush: <T extends {}>(t: Topic<T>) => void;
|
48
48
|
/**
|
49
49
|
* Get the next value in the message queue.
|
50
50
|
*
|
@@ -58,4 +58,4 @@ export declare const poll: <T extends {}>(t: Subscriber<T>) => T | undefined;
|
|
58
58
|
* Returns a promise that resolves when the message queue is not empty
|
59
59
|
* @param t
|
60
60
|
*/
|
61
|
-
export declare const
|
61
|
+
export declare const recieve: <T extends {}>(t: Subscriber<T>) => Promise<T | undefined>;
|
package/topic.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let init=()=>[[null,null],[],[]];export let sub=(t)=>[t,t[0]];export let pub=(t,value)=>{let head=t[0]=t[0][1]=[value,null];for(let i=0,res=t[1],subs=t[2];i<res.length;i++){res[i](value);subs[i][1]=head}t[1]=[];t[2]=[]};export let
|
1
|
+
export let init=()=>[[null,null],[],[]];export let sub=(t)=>[t,t[0]];export let pub=(t,value)=>{let head=t[0]=t[0][1]=[value,null];for(let i=0,res=t[1],subs=t[2];i<res.length;i++){res[i](value);subs[i][1]=head}t[1]=[];t[2]=[]};export let flush=(t)=>{let head=t[0]=[null,null];for(let i=0,res=t[1],subs=t[2];i<res.length;i++){res[i]();subs[i][1]=head}t[1]=[];t[2]=[]};export let poll=(t)=>t[1][1]!==null?(t[1]=t[1][1])[0]:undefined;export let recieve=(t)=>{if(t[1][1]!==null)return Promise.resolve((t[1]=t[1][1])[0]);let topic=t[0];topic[2].push(t);return new Promise((res)=>{topic[1].push(res)})};
|
package/concurrent.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export default (n)=>{let pending=new Array(n);let cnt=0;return async(f)=>{if(cnt<n)return pending[cnt++]=f();await Promise.allSettled(pending);cnt=0;return pending[0]=f()}};
|