ciorent 0.0.7 → 0.0.9
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 +88 -78
- package/concurrent.d.ts +14 -0
- package/concurrent.js +1 -0
- package/index.d.ts +9 -4
- package/index.js +1 -1
- package/latch.d.ts +2 -2
- package/latch.js +1 -1
- package/package.json +1 -1
- package/semaphore.js +1 -1
package/README.md
CHANGED
@@ -1,44 +1,33 @@
|
|
1
1
|
A low-overhead, cross-runtime concurrency library.
|
2
2
|
|
3
|
-
#
|
4
|
-
|
3
|
+
# Examples
|
4
|
+
## Semaphore
|
5
|
+
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
6
|
+
|
5
7
|
```ts
|
8
|
+
import * as semaphore from 'ciorent/semaphore';
|
6
9
|
import * as cio from 'ciorent';
|
7
10
|
|
8
|
-
//
|
9
|
-
const
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
// Only allow 2 of these tasks to run concurrently
|
12
|
+
const task = semaphore.task(
|
13
|
+
semaphore.init(2),
|
14
|
+
async (task: number) => {
|
15
|
+
for (let i = 1; i <= 5; i++) {
|
16
|
+
console.log('Task', task, 'iteration', i);
|
14
17
|
await cio.pause;
|
18
|
+
}
|
15
19
|
|
16
|
-
|
20
|
+
console.log('Task', task, 'end');
|
17
21
|
}
|
18
|
-
|
19
|
-
};
|
20
|
-
|
21
|
-
// Short async task
|
22
|
-
const task2 = async () => {
|
23
|
-
console.log('Fetch start', performance.now().toFixed(2) + 'ms');
|
24
|
-
const txt = await fetch('http://example.com');
|
25
|
-
console.log('Fetch status', txt.status);
|
26
|
-
};
|
22
|
+
);
|
27
23
|
|
28
|
-
//
|
29
|
-
|
30
|
-
task2();
|
24
|
+
// Try to run 6 tasks with 4 tasks running concurrently
|
25
|
+
cio.concurrent(6, task, 4);
|
31
26
|
```
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
import * as cio from 'ciorent';
|
28
|
+
## Channel
|
29
|
+
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.
|
36
30
|
|
37
|
-
await cio.sleep(1000);
|
38
|
-
console.log('Slept for about 1s');
|
39
|
-
```
|
40
|
-
|
41
|
-
Go-like channels for synchronizations:
|
42
31
|
```ts
|
43
32
|
import * as channel from 'ciorent/channel';
|
44
33
|
|
@@ -77,7 +66,7 @@ console.log('Starting...');
|
|
77
66
|
```
|
78
67
|
|
79
68
|
## Latch
|
80
|
-
|
69
|
+
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
81
70
|
|
82
71
|
```ts
|
83
72
|
import * as latch from 'ciorent/latch';
|
@@ -95,9 +84,6 @@ const task = async () => {
|
|
95
84
|
|
96
85
|
const prepare = () => {
|
97
86
|
console.log('Run before fetch:', performance.now().toFixed(2));
|
98
|
-
|
99
|
-
// Unblock the latch
|
100
|
-
latch.open(fetchLatch);
|
101
87
|
}
|
102
88
|
|
103
89
|
const main = async () => {
|
@@ -105,76 +91,100 @@ const main = async () => {
|
|
105
91
|
await cio.sleep(500);
|
106
92
|
prepare();
|
107
93
|
|
94
|
+
// Allows all previously blocked tasks to run
|
95
|
+
latch.open(fetchLatch);
|
96
|
+
|
97
|
+
// Reclose the latch
|
98
|
+
// Tasks that aren't blocked yet will be blocked
|
99
|
+
latch.reset(fetchLatch);
|
100
|
+
|
108
101
|
return p;
|
109
102
|
}
|
110
103
|
|
111
104
|
// Run fetch after 500ms
|
112
105
|
await main();
|
113
106
|
|
114
|
-
// Re-close the latch
|
115
|
-
latch.close(fetchLatch);
|
116
|
-
|
117
107
|
// Run fetch after another 500ms
|
118
108
|
await main();
|
119
109
|
```
|
120
110
|
|
121
|
-
|
111
|
+
## Utilities
|
112
|
+
### Pausing
|
113
|
+
Delay the execution of a function for other asynchronous tasks to run.
|
122
114
|
```ts
|
123
|
-
import * as latch from 'ciorent/latch';
|
124
115
|
import * as cio from 'ciorent';
|
125
116
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
console.log('Fetch status:', res.status);
|
134
|
-
}
|
117
|
+
// Expensive sync task
|
118
|
+
const task1 = async () => {
|
119
|
+
let x = 0;
|
120
|
+
for (let i = 0; i < (Math.random() + 15) * 1e7; i++) {
|
121
|
+
// Frequent pausing
|
122
|
+
if (i % 2e6 === 0)
|
123
|
+
await cio.pause;
|
135
124
|
|
136
|
-
|
137
|
-
|
125
|
+
x += Math.random() * 32 + i * Math.round(Math.random() * 16);
|
126
|
+
}
|
127
|
+
console.log('Finish task 1:', x);
|
128
|
+
};
|
138
129
|
|
139
|
-
|
140
|
-
|
141
|
-
|
130
|
+
// Short async task
|
131
|
+
const task2 = async () => {
|
132
|
+
console.log('Fetch start', performance.now().toFixed(2) + 'ms');
|
133
|
+
const txt = await fetch('http://example.com');
|
134
|
+
console.log('Fetch status', txt.status);
|
135
|
+
};
|
142
136
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
137
|
+
// Task 2 will not get blocked by task 1
|
138
|
+
task1();
|
139
|
+
task2();
|
140
|
+
```
|
147
141
|
|
148
|
-
|
149
|
-
|
142
|
+
### Sleep
|
143
|
+
A cross-runtime sleep function.
|
144
|
+
```ts
|
145
|
+
import { sleep } from 'ciorent';
|
150
146
|
|
151
|
-
|
152
|
-
|
147
|
+
await sleep(500);
|
148
|
+
console.log('Hi');
|
153
149
|
```
|
154
150
|
|
155
|
-
|
156
|
-
|
151
|
+
### Concurrency
|
152
|
+
Control how many tasks can be executed concurrently.
|
153
|
+
```ts
|
154
|
+
import concurrent from 'ciorent/concurrent';
|
155
|
+
import * as cio from 'ciorent';
|
156
|
+
|
157
|
+
// Allow 3 tasks to run at the same time
|
158
|
+
const run = concurrent(3);
|
157
159
|
|
158
|
-
|
160
|
+
for (let id = 1; id <= 6; id++)
|
161
|
+
run(async () => {
|
162
|
+
await cio.sleep(Math.random() * 20 + 50);
|
163
|
+
console.log('Task', id, 'done');
|
164
|
+
});
|
165
|
+
```
|
159
166
|
|
167
|
+
### Spawning tasks
|
168
|
+
Creating new tasks with controlled concurrency.
|
160
169
|
```ts
|
161
|
-
import * as semaphore from 'ciorent/semaphore';
|
162
170
|
import * as cio from 'ciorent';
|
163
171
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
for (let i = 1; i <= 5; i++) {
|
169
|
-
await cio.pause;
|
170
|
-
console.log('Task', task, 'iteration', i);
|
171
|
-
}
|
172
|
+
const task = async (id: number) => {
|
173
|
+
await cio.sleep(Math.random() * 20 + 50);
|
174
|
+
console.log('Task', id, 'done');
|
175
|
+
}
|
172
176
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
+
// Spawn and run 5 tasks sequentially
|
178
|
+
console.log('Running sequentially:');
|
179
|
+
cio.sequential(5, task);
|
180
|
+
|
181
|
+
// Spawn and run 5 tasks concurrently
|
182
|
+
console.log('Running concurrently:');
|
183
|
+
cio.concurrent(5, task);
|
177
184
|
|
178
|
-
//
|
179
|
-
|
185
|
+
// Spawn and run 5 tasks, with the maximum
|
186
|
+
// tasks running concurrently set to 3
|
187
|
+
console.log('Running each 3 tasks concurrently:');
|
188
|
+
cio.concurrent(5, task, 3);
|
180
189
|
```
|
190
|
+
|
package/concurrent.d.ts
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
/**
|
2
|
+
* @module
|
3
|
+
* Concurrency controls
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* Describe a concurrency controller
|
7
|
+
*/
|
8
|
+
export type Controller = <T>(task: () => Promise<T>) => Promise<T>;
|
9
|
+
/**
|
10
|
+
* Create a concurrency controller, allow n tasks to run concurrently
|
11
|
+
* @param n
|
12
|
+
*/
|
13
|
+
declare const _default: (n: number) => Controller;
|
14
|
+
export default _default;
|
package/concurrent.js
ADDED
@@ -0,0 +1 @@
|
|
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()}};
|
package/index.d.ts
CHANGED
@@ -16,10 +16,15 @@ export declare const pause: Promise<void>;
|
|
16
16
|
*/
|
17
17
|
export declare const sleep: (ms: number) => Promise<void>;
|
18
18
|
/**
|
19
|
-
* Spawn n tasks
|
19
|
+
* Spawn n tasks that runs sequentially
|
20
20
|
* @param n
|
21
21
|
* @param task - The function to run
|
22
|
-
* @param args - The arguments to pass in the function
|
23
|
-
* @returns
|
24
22
|
*/
|
25
|
-
export declare const
|
23
|
+
export declare const sequential: (n: number, task: (id: number) => Promise<any>) => Promise<void>;
|
24
|
+
/**
|
25
|
+
* Spawn n tasks that runs concurrently
|
26
|
+
* @param n
|
27
|
+
* @param task - The function to run
|
28
|
+
* @param concurrency - The amount of task to run concurrently
|
29
|
+
*/
|
30
|
+
export declare const concurrent: (n: number, task: (id: number) => Promise<any>, concurrency?: number) => 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
|
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=async(n,task,concurrency)=>{if(concurrency==null){let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(i);return Promise.all(arr)}let arr=new Array(concurrency);let pre=0;for(let block=n/concurrency>>>0;block>0;block--){for(let j=0;j<concurrency;j++)arr[j]=task(pre+j);await Promise.all(arr);pre+=concurrency}n-=pre;for(let i=0;i<n;i++)arr[i]=task(pre+i);return Promise.all(arr)};
|
package/latch.d.ts
CHANGED
@@ -22,6 +22,6 @@ export declare const pause: (latch: Latch) => Promise<void>;
|
|
22
22
|
*/
|
23
23
|
export declare const open: (latch: Latch) => void;
|
24
24
|
/**
|
25
|
-
*
|
25
|
+
* Re-close a latch
|
26
26
|
*/
|
27
|
-
export declare const
|
27
|
+
export declare const reset: (latch: Latch) => void;
|
package/latch.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as endPromise}from".";export let init=()=>{let r;return[new Promise((res)=>{r=res}),r]};export let pause=(latch)=>latch[0];export let open=(latch)=>{latch[1]();latch[0]=endPromise};export let
|
1
|
+
import{pause as endPromise}from".";export let init=()=>{let r;return[new Promise((res)=>{r=res}),r]};export let pause=(latch)=>latch[0];export let open=(latch)=>{latch[1]();latch[0]=endPromise};export let reset=(latch)=>{if(latch[0]===endPromise){let r;latch[0]=new Promise((res)=>{r=res});latch[1]=r}};
|
package/package.json
CHANGED
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)=>{if(s[0]
|
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)=>{try{await pause(s);return await f(...a)}finally{signal(s)}};
|