ciorent 0.1.2 → 0.1.4
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 +40 -42
- package/fiber.d.ts +31 -27
- package/fiber.js +1 -1
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/latch.d.ts +2 -2
- package/latch.js +1 -1
- package/package.json +4 -4
- package/topic.d.ts +3 -3
- package/topic.js +1 -1
package/README.md
CHANGED
@@ -4,7 +4,7 @@ Semaphore is a concurrency primitive used to control access to a common resource
|
|
4
4
|
|
5
5
|
```ts
|
6
6
|
import * as semaphore from 'ciorent/semaphore';
|
7
|
-
import * as
|
7
|
+
import * as co from 'ciorent';
|
8
8
|
|
9
9
|
// Only allow 2 task to run concurrently
|
10
10
|
const sem = semaphore.init(2);
|
@@ -16,7 +16,7 @@ const task = async (id: number) => {
|
|
16
16
|
console.log('Task', id, 'started');
|
17
17
|
|
18
18
|
// Let the main thread schedules other tasks
|
19
|
-
for (let i = 1; i <= 5; i++) await
|
19
|
+
for (let i = 1; i <= 5; i++) await co.pause;
|
20
20
|
|
21
21
|
console.log('Task', id, 'end');
|
22
22
|
|
@@ -25,41 +25,39 @@ const task = async (id: number) => {
|
|
25
25
|
}
|
26
26
|
|
27
27
|
// Try to run 5 tasks concurrently
|
28
|
-
|
28
|
+
co.spawn(5, task);
|
29
29
|
```
|
30
30
|
|
31
31
|
## Fibers
|
32
32
|
Virtual threads with more controlled execution.
|
33
33
|
|
34
34
|
```ts
|
35
|
-
import * as
|
35
|
+
import * as co from 'ciorent';
|
36
36
|
import * as fiber from 'ciorent/fiber';
|
37
37
|
|
38
|
-
const
|
38
|
+
const f1 = fiber.fn(function* () {
|
39
39
|
console.log('Fiber 1 started');
|
40
40
|
|
41
|
-
//
|
42
|
-
|
43
|
-
yield cio.sleep(1000);
|
41
|
+
// Wait for a promise
|
42
|
+
yield co.sleep(1000);
|
44
43
|
|
45
44
|
console.log('Fiber 1 done');
|
45
|
+
return Math.random();
|
46
46
|
});
|
47
47
|
|
48
|
-
|
48
|
+
fiber.spawn(function* (proc) {
|
49
49
|
console.log('Fiber 2 started');
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
// Start f1, wait for it to finish and get the result
|
52
|
+
const res = yield* fiber.join(fiber.spawn(f1));
|
53
|
+
console.log('Fiber 1 result:', res);
|
53
54
|
|
54
|
-
// Start
|
55
|
-
|
56
|
-
fiber.mount(fiber.spawn(thread1), thread);
|
55
|
+
// Start f1 and make its lifetime depends on current fiber
|
56
|
+
fiber.mount(fiber.spawn(f1), proc);
|
57
57
|
|
58
|
+
// The runtime will interrupt f1
|
58
59
|
console.log('Fiber 2 done');
|
59
60
|
});
|
60
|
-
|
61
|
-
// Start running the thread
|
62
|
-
fiber.spawn(thread2);
|
63
61
|
```
|
64
62
|
|
65
63
|
## Latch
|
@@ -94,15 +92,15 @@ A fast, simple publish-subscribe API.
|
|
94
92
|
|
95
93
|
```ts
|
96
94
|
import * as topic from 'ciorent/topic';
|
97
|
-
import * as
|
95
|
+
import * as co from 'ciorent';
|
98
96
|
|
99
97
|
const messages = topic.init<number>();
|
100
98
|
|
101
99
|
// A task that publish messages
|
102
100
|
const publisher = async () => {
|
103
101
|
for (let i = 0; i < 3; i++) {
|
104
|
-
await
|
105
|
-
topic.
|
102
|
+
await co.sleep(100);
|
103
|
+
topic.publish(messages, i);
|
106
104
|
}
|
107
105
|
|
108
106
|
// Resolve all waiting promises
|
@@ -111,8 +109,8 @@ const publisher = async () => {
|
|
111
109
|
}
|
112
110
|
|
113
111
|
// Spawn 3 tasks that recieve messages
|
114
|
-
|
115
|
-
const sub = topic.
|
112
|
+
co.spawn(3, async (id: number) => {
|
113
|
+
const sub = topic.subscribe(messages);
|
116
114
|
|
117
115
|
while (true) {
|
118
116
|
// Block until the value is sent
|
@@ -130,13 +128,13 @@ Channel is a synchronization primitive via message passing. A message may be sen
|
|
130
128
|
|
131
129
|
```ts
|
132
130
|
import * as channel from 'ciorent/channel';
|
133
|
-
import * as
|
131
|
+
import * as co from 'ciorent';
|
134
132
|
|
135
133
|
const c = channel.init<number>();
|
136
134
|
|
137
135
|
const run = async () => {
|
138
136
|
for (let i = 0; i < 5; i++) {
|
139
|
-
await
|
137
|
+
await co.sleep(100);
|
140
138
|
channel.send(c, i);
|
141
139
|
console.log('Sent', i);
|
142
140
|
}
|
@@ -167,7 +165,7 @@ console.log('Starting...');
|
|
167
165
|
### Pausing
|
168
166
|
Delay the execution of a function for other asynchronous tasks to run.
|
169
167
|
```ts
|
170
|
-
import * as
|
168
|
+
import * as co from 'ciorent';
|
171
169
|
|
172
170
|
// Expensive sync task
|
173
171
|
const task1 = async () => {
|
@@ -175,7 +173,7 @@ const task1 = async () => {
|
|
175
173
|
|
176
174
|
// Yield control back to the runtime, allowing it to
|
177
175
|
// schedule other tasks
|
178
|
-
await
|
176
|
+
await co.pause;
|
179
177
|
|
180
178
|
// Simulate heavy operation
|
181
179
|
for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
|
@@ -199,82 +197,82 @@ task2();
|
|
199
197
|
### Sleep
|
200
198
|
Cross-runtime synchronous and asynchronous sleep functions.
|
201
199
|
```ts
|
202
|
-
import * as
|
200
|
+
import * as co from 'ciorent';
|
203
201
|
|
204
202
|
const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
|
205
203
|
|
206
204
|
logTime('Start');
|
207
205
|
|
208
206
|
// Non-blocking
|
209
|
-
await
|
207
|
+
await co.sleep(500);
|
210
208
|
logTime('After about 0.5s');
|
211
209
|
|
212
210
|
// This blocks the event loop
|
213
211
|
// On the browser this only works in workers and blocks the worker thread
|
214
|
-
|
212
|
+
co.sleepSync(500);
|
215
213
|
logTime('After another 0.5s');
|
216
214
|
```
|
217
215
|
|
218
216
|
### Spawning tasks
|
219
217
|
Utilities to create and run tasks.
|
220
218
|
```ts
|
221
|
-
import * as
|
219
|
+
import * as co from 'ciorent';
|
222
220
|
|
223
221
|
const task = async (id: number) => {
|
224
|
-
await
|
222
|
+
await co.sleep((10 - id) * 20 + 50);
|
225
223
|
console.log('Task', id, 'done');
|
226
224
|
}
|
227
225
|
|
228
226
|
// Spawn and run 5 tasks sequentially
|
229
227
|
console.log('Running 5 tasks sequentially:');
|
230
|
-
await
|
228
|
+
await co.sequential(5, task);
|
231
229
|
|
232
230
|
// Spawn and run 5 tasks concurrently
|
233
231
|
console.log('Running 5 tasks concurrently:');
|
234
|
-
await
|
232
|
+
await Promise.all(co.spawn(5, task));
|
235
233
|
```
|
236
234
|
|
237
235
|
### Debounce
|
238
236
|
Postpones execution until after an idle period.
|
239
237
|
```ts
|
240
|
-
import * as
|
238
|
+
import * as co from 'ciorent';
|
241
239
|
|
242
|
-
const fn =
|
240
|
+
const fn = co.debounce((id: number) => {
|
243
241
|
console.log('ID:', id);
|
244
242
|
}, 500);
|
245
243
|
|
246
244
|
fn(1); // fn(1) gets skipped
|
247
|
-
await
|
245
|
+
await co.sleep(100);
|
248
246
|
fn(2); // fn(2) gets executed
|
249
247
|
```
|
250
248
|
|
251
249
|
### Rate Limit
|
252
250
|
Limits the number of calls within a time window.
|
253
251
|
```ts
|
254
|
-
import * as
|
252
|
+
import * as co from 'ciorent';
|
255
253
|
|
256
254
|
// Allow 2 calls in 500ms, other calls are dropped
|
257
|
-
const fn =
|
255
|
+
const fn = co.rateLimit((id: number) => {
|
258
256
|
console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
|
259
257
|
}, 500, 2);
|
260
258
|
|
261
259
|
// Some calls will be dropped
|
262
260
|
for (let i = 0; i < 8; i++) {
|
263
261
|
fn(i);
|
264
|
-
await
|
262
|
+
await co.sleep(400);
|
265
263
|
}
|
266
264
|
```
|
267
265
|
|
268
266
|
### Throttle
|
269
267
|
Executes a function at a regular interval.
|
270
268
|
```ts
|
271
|
-
import * as
|
269
|
+
import * as co from 'ciorent';
|
272
270
|
|
273
271
|
// Allow 2 calls in 500ms
|
274
|
-
const fn =
|
272
|
+
const fn = co.throttle((id: number) => {
|
275
273
|
console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
|
276
274
|
}, 500, 2);
|
277
275
|
|
278
|
-
|
276
|
+
co.spawn(8, fn);
|
279
277
|
```
|
280
278
|
|
package/fiber.d.ts
CHANGED
@@ -2,47 +2,51 @@
|
|
2
2
|
* @module Fibers
|
3
3
|
*/
|
4
4
|
/**
|
5
|
-
*
|
5
|
+
* Describe a fiber process
|
6
6
|
*/
|
7
|
-
export
|
8
|
-
/**
|
9
|
-
* Check whether the fiber is running
|
10
|
-
*/
|
11
|
-
export declare const running: (t: Thread) => boolean;
|
12
|
-
/**
|
13
|
-
* Check whether the fiber is done
|
14
|
-
*/
|
15
|
-
export declare const done: (t: Thread) => boolean;
|
16
|
-
/**
|
17
|
-
* Describe a fiber
|
18
|
-
*/
|
19
|
-
export interface Thread<T = unknown, TReturn = unknown> {
|
7
|
+
export interface Process<TReturn = unknown> {
|
20
8
|
/**
|
21
9
|
* The waiting promise
|
22
10
|
*/
|
23
|
-
0: Promise<
|
11
|
+
0: Promise<TReturn | undefined>;
|
24
12
|
/**
|
25
13
|
* Fiber status
|
26
14
|
*/
|
27
|
-
1: 0 | 1 | 2;
|
15
|
+
1: 0 | 1 | 2 | 3;
|
28
16
|
/**
|
29
17
|
* Callback to continue running the fiber
|
30
18
|
*/
|
31
19
|
2: null | (() => void);
|
32
20
|
/**
|
33
|
-
* Bounded
|
21
|
+
* Bounded fibers
|
34
22
|
*/
|
35
|
-
3:
|
23
|
+
3: Process[];
|
36
24
|
}
|
37
25
|
/**
|
38
26
|
* Describe a fiber runtime
|
39
27
|
*/
|
40
|
-
export type Runtime = <const
|
28
|
+
export type Runtime = <const TReturn, const Args extends any[]>(gen: (proc: Process<TReturn>, ...args: Args) => Generator<any, TReturn>, ...args: Args) => Process<TReturn>;
|
29
|
+
/**
|
30
|
+
* Check whether the fiber has been paused
|
31
|
+
*/
|
32
|
+
export declare const paused: (t: Process) => boolean;
|
33
|
+
/**
|
34
|
+
* Check whether the fiber is running
|
35
|
+
*/
|
36
|
+
export declare const resumed: (t: Process) => boolean;
|
37
|
+
/**
|
38
|
+
* Check whether the fiber has finished
|
39
|
+
*/
|
40
|
+
export declare const done: (t: Process) => boolean;
|
41
|
+
/**
|
42
|
+
* Check whether the fiber has been interrupted
|
43
|
+
*/
|
44
|
+
export declare const stopped: (t: Process) => boolean;
|
41
45
|
/**
|
42
46
|
* Create a fiber function
|
43
47
|
* @param f
|
44
48
|
*/
|
45
|
-
export declare const fn: <const Fn extends (thread:
|
49
|
+
export declare const fn: <const Fn extends (thread: Process, ...args: any[]) => Generator>(f: Fn) => Fn;
|
46
50
|
/**
|
47
51
|
* A basic fiber runtime
|
48
52
|
* @param g
|
@@ -52,39 +56,39 @@ export declare const spawn: Runtime;
|
|
52
56
|
* Pause the execution of a fiber
|
53
57
|
* @param t
|
54
58
|
*/
|
55
|
-
export declare const pause: (t:
|
59
|
+
export declare const pause: (t: Process) => void;
|
56
60
|
/**
|
57
61
|
* Resume the execution of a fiber
|
58
62
|
* @param t
|
59
63
|
*/
|
60
|
-
export declare const resume: (t:
|
64
|
+
export declare const resume: (t: Process) => void;
|
61
65
|
/**
|
62
66
|
* Stop the execution of a fiber
|
63
67
|
* @param t
|
64
68
|
*/
|
65
|
-
export declare const stop: (t:
|
69
|
+
export declare const stop: (t: Process) => void;
|
66
70
|
/**
|
67
71
|
* Wait for a fiber and retrieve its result
|
68
72
|
* @param t
|
69
73
|
*/
|
70
|
-
export declare function join<T extends
|
74
|
+
export declare function join<T extends Process>(t: T): Generator<Awaited<T[0]>, Awaited<T[0]>>;
|
71
75
|
/**
|
72
76
|
* Wait for a fiber to finish and retrieve its result
|
73
77
|
* @param t
|
74
78
|
*/
|
75
|
-
export declare const finish: <T extends
|
79
|
+
export declare const finish: <T extends Process>(t: T) => T[3];
|
76
80
|
/**
|
77
81
|
* Mount child fiber lifetime to parent lifetime
|
78
82
|
* @param child
|
79
83
|
* @param parent
|
80
84
|
*/
|
81
|
-
export declare const mount: (child:
|
85
|
+
export declare const mount: (child: Process, parent: Process) => void;
|
82
86
|
/**
|
83
87
|
* Control the fiber with an abort signal
|
84
88
|
* @param t
|
85
89
|
* @param signal
|
86
90
|
*/
|
87
|
-
export declare const control: (t:
|
91
|
+
export declare const control: (t: Process, signal: AbortSignal) => void;
|
88
92
|
/**
|
89
93
|
* Unwrap a promise result
|
90
94
|
*/
|
package/fiber.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let paused=(t)=>t[1]===0;export let
|
1
|
+
export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let done=(t)=>t[1]===2;export let stopped=(t)=>t[1]===3;let invoke=async(g,thread)=>{try{let t=g.next();while(!t.done){let v=await t.value;if(thread[1]===0){let r;let p=new Promise((res)=>{r=res});thread[2]=r;await p}if(thread[1]===3)return;t=g.next(v)}thread[1]=2;return t.value}finally{thread[3].forEach(stop)}};export let fn=(f)=>f;export let spawn=(f,...args)=>{let thread=[null,1,null,[]];thread[0]=invoke(f(thread,...args),thread);return thread};export let pause=(t)=>{if(t[1]===1)t[1]=0};export let resume=(t)=>{if(t[1]===0){t[1]=1;t[2]?.()}};export let stop=(t)=>{if(t[1]===0)t[2]?.();t[1]=3};export function*join(t){return yield t[0]}export let finish=(t)=>t[3];export let mount=(child,parent)=>{parent[3].push(child)};export let control=(t,signal)=>{signal.addEventListener("abort",()=>{stop(t)})};export function*unwrap(t){return yield t}
|
package/index.d.ts
CHANGED
@@ -30,11 +30,11 @@ export declare const sleepSync: (ms: number) => void;
|
|
30
30
|
*/
|
31
31
|
export declare const sequential: <const T extends any[]>(n: number, task: (...args: [...T, id: number]) => Promise<any>, ...args: T) => Promise<void>;
|
32
32
|
/**
|
33
|
-
* Spawn n tasks
|
33
|
+
* Spawn n concurrent tasks
|
34
34
|
* @param n
|
35
35
|
* @param task - The function to run
|
36
36
|
*/
|
37
|
-
export declare const
|
37
|
+
export declare const spawn: <const T extends any[], const R>(n: number, task: (...args: [...T, id: number]) => Promise<R>, ...args: T) => Promise<R>[];
|
38
38
|
/**
|
39
39
|
* Drop function calls until it doesn't get called for a specific period.
|
40
40
|
* @param f - The target function to debounce (it must not throw errors)
|
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)}));let sharedBuf=new Int32Array(new SharedArrayBuffer(4));export let sleepSync=globalThis.Bun?.sleepSync??((ms)=>{Atomics.wait(sharedBuf,0,0,ms)});export let sequential=async(n,task,...args)=>{for(let i=0;i<n;i++)await task(...args,i)};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)}));let sharedBuf=new Int32Array(new SharedArrayBuffer(4));export let sleepSync=globalThis.Bun?.sleepSync??((ms)=>{Atomics.wait(sharedBuf,0,0,ms)});export let sequential=async(n,task,...args)=>{for(let i=0;i<n;i++)await task(...args,i)};export let spawn=(n,task,...args)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(...args,i);return arr};export let debounce=(f,ms)=>{let id;return(...a)=>{clearTimeout(id);id=setTimeout(f,ms,...a)}};export let rateLimit=(f,ms,limit)=>{let cur=limit;let unlock=()=>{cur=limit};return(...a)=>{if(cur>0){if(cur===1)setTimeout(unlock,ms);cur--;f(...a)}}};export let throttle=(f,ms,limit)=>{let head=[null];let tail=head;let cur=limit;let unlock=()=>{cur=limit;while(cur>0){if(tail===head)return;cur--;tail=tail[0];tail[1](f(...tail[2]))}setTimeout(unlock,ms)};return(...a)=>{if(cur===1){setTimeout(unlock,ms)}else if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r,a];return p}cur--;return f(...a)}};
|
package/latch.d.ts
CHANGED
@@ -18,6 +18,6 @@ export declare const pause: (latch: Latch) => Promise<void>;
|
|
18
18
|
*/
|
19
19
|
export declare const open: (latch: Latch) => void;
|
20
20
|
/**
|
21
|
-
*
|
21
|
+
* Close a latch
|
22
22
|
*/
|
23
|
-
export declare const
|
23
|
+
export declare const close: (latch: Latch) => void;
|
package/latch.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as endPromise}from"./index.js";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"./index.js";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 close=(latch)=>{if(latch[0]===endPromise){let r;latch[0]=new Promise((res)=>{r=res});latch[1]=r}};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.4",
|
4
4
|
"description": "A lightweight, low-overhead concurrency library",
|
5
5
|
"homepage": "https://ciorent.netlify.app",
|
6
6
|
"repository": {
|
@@ -19,13 +19,13 @@
|
|
19
19
|
"types": "./index.d.ts",
|
20
20
|
"exports": {
|
21
21
|
"./fixed-queue": "./fixed-queue.js",
|
22
|
-
"./sliding-queue": "./sliding-queue.js",
|
23
22
|
"./dropping-queue": "./dropping-queue.js",
|
24
|
-
"./
|
25
|
-
"./channel": "./channel.js",
|
23
|
+
"./sliding-queue": "./sliding-queue.js",
|
26
24
|
".": "./index.js",
|
27
25
|
"./topic": "./topic.js",
|
28
26
|
"./semaphore": "./semaphore.js",
|
27
|
+
"./channel": "./channel.js",
|
28
|
+
"./fiber": "./fiber.js",
|
29
29
|
"./latch": "./latch.js"
|
30
30
|
}
|
31
31
|
}
|
package/topic.d.ts
CHANGED
@@ -34,12 +34,12 @@ export interface Subscriber<T extends {}> {
|
|
34
34
|
* Subscribe to a topic
|
35
35
|
* @param t
|
36
36
|
*/
|
37
|
-
export declare const
|
37
|
+
export declare const subscribe: <T extends {}>(t: Topic<T>) => Subscriber<T>;
|
38
38
|
/**
|
39
|
-
*
|
39
|
+
* Publish to a topic
|
40
40
|
* @param t
|
41
41
|
*/
|
42
|
-
export declare const
|
42
|
+
export declare const publish: <T extends {}>(t: Topic<T>, value: T) => void;
|
43
43
|
/**
|
44
44
|
* Resolve all waiting promises and clear all pending values
|
45
45
|
* @param t
|
package/topic.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let init=()=>[[null],[],[]];export let
|
1
|
+
export let init=()=>[[null],[],[]];export let subscribe=(t)=>[t,t[0]];export let publish=(t,value)=>{let head=t[0]=t[0][0]=[null,value];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];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][0]!==null?(t[1]=t[1][0])[1]:undefined;export let recieve=(t)=>{if(t[1][0]!==null)return Promise.resolve((t[1]=t[1][0])[1]);let topic=t[0];topic[2].push(t);return new Promise((res)=>{topic[1].push(res)})};
|