ciorent 0.1.1 → 0.1.3
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 +71 -73
- package/fiber.d.ts +31 -27
- package/fiber.js +1 -1
- package/package.json +6 -6
- package/semaphore.d.ts +2 -2
- package/semaphore.js +1 -1
package/README.md
CHANGED
@@ -1,63 +1,31 @@
|
|
1
1
|
A lightweight, low-overhead concurrency library.
|
2
|
-
##
|
3
|
-
|
2
|
+
## Semaphore
|
3
|
+
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
4
4
|
|
5
5
|
```ts
|
6
|
+
import * as semaphore from 'ciorent/semaphore';
|
6
7
|
import * as cio from 'ciorent';
|
7
|
-
import * as fiber from 'ciorent/fiber';
|
8
|
-
|
9
|
-
const thread1 = fiber.fn(function* () {
|
10
|
-
console.log('Fiber 1 started');
|
11
|
-
|
12
|
-
// Thread1 will be interrupted by thread2
|
13
|
-
// As thread2 will end first
|
14
|
-
yield cio.sleep(1000);
|
15
|
-
|
16
|
-
console.log('Fiber 1 done');
|
17
|
-
});
|
18
|
-
|
19
|
-
const thread2 = fiber.fn(function* (thread) {
|
20
|
-
console.log('Fiber 2 started');
|
21
|
-
|
22
|
-
yield;
|
23
|
-
console.log('Fiber 2 resumed');
|
24
|
-
|
25
|
-
// Start thread 1 and make thread1
|
26
|
-
// lifetime depends on thread2
|
27
|
-
fiber.mount(fiber.spawn(thread1), thread);
|
28
|
-
|
29
|
-
console.log('Fiber 2 done');
|
30
|
-
});
|
31
|
-
|
32
|
-
// Start running the thread
|
33
|
-
fiber.spawn(thread2);
|
34
|
-
```
|
35
8
|
|
36
|
-
|
37
|
-
|
9
|
+
// Only allow 2 task to run concurrently
|
10
|
+
const sem = semaphore.init(2);
|
38
11
|
|
39
|
-
|
40
|
-
|
12
|
+
const task = async (id: number) => {
|
13
|
+
// Acquire the semaphore or wait for the semaphore to be available
|
14
|
+
await semaphore.pause(sem);
|
41
15
|
|
42
|
-
|
16
|
+
console.log('Task', id, 'started');
|
43
17
|
|
44
|
-
|
45
|
-
|
46
|
-
await latch.pause(startFetch);
|
18
|
+
// Let the main thread schedules other tasks
|
19
|
+
for (let i = 1; i <= 5; i++) await cio.pause;
|
47
20
|
|
48
|
-
console.log('
|
49
|
-
const res = await fetch('http://example.com');
|
50
|
-
console.log('Fetch status:', res.status);
|
51
|
-
}
|
21
|
+
console.log('Task', id, 'end');
|
52
22
|
|
53
|
-
|
54
|
-
|
55
|
-
console.log('Run before fetch:', performance.now().toFixed(2));
|
56
|
-
latch.open(startFetch);
|
23
|
+
// Release the semaphore
|
24
|
+
semaphore.signal(sem);
|
57
25
|
}
|
58
26
|
|
59
|
-
|
60
|
-
|
27
|
+
// Try to run 5 tasks concurrently
|
28
|
+
cio.concurrent(5, task);
|
61
29
|
```
|
62
30
|
|
63
31
|
## Pubsub
|
@@ -134,33 +102,63 @@ run();
|
|
134
102
|
console.log('Starting...');
|
135
103
|
```
|
136
104
|
|
137
|
-
##
|
138
|
-
|
105
|
+
## Latch
|
106
|
+
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
107
|
+
|
108
|
+
```ts
|
109
|
+
import * as latch from 'ciorent/latch';
|
110
|
+
|
111
|
+
const startFetch = latch.init();
|
112
|
+
|
113
|
+
const task = async () => {
|
114
|
+
// Blocks until the latch is open
|
115
|
+
await latch.pause(startFetch);
|
116
|
+
|
117
|
+
console.log('Start fetching...');
|
118
|
+
const res = await fetch('http://example.com');
|
119
|
+
console.log('Fetch status:', res.status);
|
120
|
+
}
|
121
|
+
|
122
|
+
const prepare = () => {
|
123
|
+
// This always run first
|
124
|
+
console.log('Run before fetch:', performance.now().toFixed(2));
|
125
|
+
latch.open(startFetch);
|
126
|
+
}
|
127
|
+
|
128
|
+
task();
|
129
|
+
prepare();
|
130
|
+
```
|
131
|
+
|
132
|
+
## Fibers
|
133
|
+
Virtual threads with more controlled execution.
|
139
134
|
|
140
135
|
```ts
|
141
|
-
import * as semaphore from 'ciorent/semaphore';
|
142
136
|
import * as cio from 'ciorent';
|
137
|
+
import * as fiber from 'ciorent/fiber';
|
143
138
|
|
144
|
-
|
145
|
-
|
139
|
+
const f1 = fiber.fn(function* () {
|
140
|
+
console.log('Fiber 1 started');
|
146
141
|
|
147
|
-
|
148
|
-
|
149
|
-
await semaphore.pause(sem);
|
142
|
+
// Wait for a promise
|
143
|
+
yield cio.sleep(1000);
|
150
144
|
|
151
|
-
console.log('
|
145
|
+
console.log('Fiber 1 done');
|
146
|
+
return Math.random();
|
147
|
+
});
|
152
148
|
|
153
|
-
|
154
|
-
|
149
|
+
fiber.spawn(function* (proc) {
|
150
|
+
console.log('Fiber 2 started');
|
155
151
|
|
156
|
-
|
152
|
+
// Start f1, wait for it to finish and get the result
|
153
|
+
const res = yield* fiber.join(fiber.spawn(f1));
|
154
|
+
console.log('Fiber 1 result:', res);
|
157
155
|
|
158
|
-
//
|
159
|
-
|
160
|
-
}
|
156
|
+
// Start f1 and make its lifetime depends on current fiber
|
157
|
+
fiber.mount(fiber.spawn(f1), proc);
|
161
158
|
|
162
|
-
//
|
163
|
-
|
159
|
+
// The runtime will interrupt f1
|
160
|
+
console.log('Fiber 2 done');
|
161
|
+
});
|
164
162
|
```
|
165
163
|
|
166
164
|
## Utilities
|
@@ -253,16 +251,16 @@ Limits the number of calls within a time window.
|
|
253
251
|
```ts
|
254
252
|
import * as cio from 'ciorent';
|
255
253
|
|
256
|
-
// Allow
|
254
|
+
// Allow 2 calls in 500ms, other calls are dropped
|
257
255
|
const fn = cio.rateLimit((id: number) => {
|
258
256
|
console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
|
259
|
-
}, 500,
|
257
|
+
}, 500, 2);
|
260
258
|
|
261
|
-
|
262
|
-
|
263
|
-
fn(
|
264
|
-
await cio.sleep(
|
265
|
-
|
259
|
+
// Some calls will be dropped
|
260
|
+
for (let i = 0; i < 8; i++) {
|
261
|
+
fn(i);
|
262
|
+
await cio.sleep(400);
|
263
|
+
}
|
266
264
|
```
|
267
265
|
|
268
266
|
### Throttle
|
@@ -275,6 +273,6 @@ const fn = cio.throttle((id: number) => {
|
|
275
273
|
console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
|
276
274
|
}, 500, 2);
|
277
275
|
|
278
|
-
cio.concurrent(8,
|
276
|
+
cio.concurrent(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 is paused
|
31
|
+
*/
|
32
|
+
export declare const paused: (t: Process) => boolean;
|
33
|
+
/**
|
34
|
+
* Check whether the fiber is running
|
35
|
+
*/
|
36
|
+
export declare const running: (t: Process) => boolean;
|
37
|
+
/**
|
38
|
+
* Check whether the fiber is finished
|
39
|
+
*/
|
40
|
+
export declare const done: (t: Process) => boolean;
|
41
|
+
/**
|
42
|
+
* Check whether the fiber is 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 running=(t)=>t[1]===1;export let done=(t)=>t[1]===2;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]===
|
1
|
+
export let paused=(t)=>t[1]===0;export let running=(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/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.3",
|
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
|
"./sliding-queue": "./sliding-queue.js",
|
22
|
+
"./fixed-queue": "./fixed-queue.js",
|
23
|
+
"./fiber": "./fiber.js",
|
24
|
+
".": "./index.js",
|
22
25
|
"./semaphore": "./semaphore.js",
|
26
|
+
"./dropping-queue": "./dropping-queue.js",
|
23
27
|
"./topic": "./topic.js",
|
24
|
-
".": "./index.js",
|
25
|
-
"./latch": "./latch.js",
|
26
28
|
"./channel": "./channel.js",
|
27
|
-
"./
|
28
|
-
"./dropping-queue": "./dropping-queue.js",
|
29
|
-
"./fiber": "./fiber.js"
|
29
|
+
"./latch": "./latch.js"
|
30
30
|
}
|
31
31
|
}
|
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
|
-
*
|
35
|
+
* Bind a task to a semaphore
|
36
36
|
*/
|
37
|
-
export declare const
|
37
|
+
export declare const bind: <T extends (...args: any[]) => Promise<any>>(f: T, s: Semaphore) => T;
|
package/semaphore.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as resolvedPromise}from"./index.js";export let init=(n)=>{let root=[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][0]=[null,r];return p}return resolvedPromise};export let signal=(s)=>{if(s[0]<0)(s[2]=s[2][0])[1]();s[0]++};export let
|
1
|
+
import{pause as resolvedPromise}from"./index.js";export let init=(n)=>{let root=[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][0]=[null,r];return p}return resolvedPromise};export let signal=(s)=>{if(s[0]<0)(s[2]=s[2][0])[1]();s[0]++};export let bind=(f,s)=>async(...a)=>{s[0]--;if(s[0]<0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][0]=[null,r];await p}try{return await f(...a)}finally{if(s[0]<0)(s[2]=s[2][0])[1]();s[0]++}};
|