ciorent 0.1.4 → 0.1.5
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 +109 -92
- package/fiber.d.ts +3 -3
- package/fiber.js +1 -1
- package/index.d.ts +0 -7
- package/index.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
@@ -1,34 +1,13 @@
|
|
1
1
|
A lightweight, low-overhead concurrency library.
|
2
|
-
## Semaphore
|
3
|
-
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
4
|
-
|
5
|
-
```ts
|
6
|
-
import * as semaphore from 'ciorent/semaphore';
|
7
|
-
import * as co from 'ciorent';
|
8
|
-
|
9
|
-
// Only allow 2 task to run concurrently
|
10
|
-
const sem = semaphore.init(2);
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
// Let the main thread schedules other tasks
|
19
|
-
for (let i = 1; i <= 5; i++) await co.pause;
|
3
|
+
## Features
|
4
|
+
- Micro-optimized utilities.
|
5
|
+
- Performance-oriented API design.
|
6
|
+
- Small bundle size.
|
7
|
+
- Fully type-safe.
|
20
8
|
|
21
|
-
|
22
|
-
|
23
|
-
// Release the semaphore
|
24
|
-
semaphore.signal(sem);
|
25
|
-
}
|
26
|
-
|
27
|
-
// Try to run 5 tasks concurrently
|
28
|
-
co.spawn(5, task);
|
29
|
-
```
|
30
|
-
|
31
|
-
## Fibers
|
9
|
+
## Examples
|
10
|
+
### Fibers
|
32
11
|
Virtual threads with more controlled execution.
|
33
12
|
|
34
13
|
```ts
|
@@ -36,31 +15,57 @@ import * as co from 'ciorent';
|
|
36
15
|
import * as fiber from 'ciorent/fiber';
|
37
16
|
|
38
17
|
const f1 = fiber.fn(function* () {
|
39
|
-
console.log('Fiber 1 started');
|
40
|
-
|
41
18
|
// Wait for a promise
|
42
19
|
yield co.sleep(1000);
|
43
20
|
|
44
|
-
|
21
|
+
// Wait for a promise and return its result
|
22
|
+
const res = yield* fiber.unwrap(Promise.resolve(1));
|
23
|
+
console.log('Fiber 1 recieved:', res);
|
24
|
+
|
45
25
|
return Math.random();
|
46
26
|
});
|
47
27
|
|
48
|
-
|
49
|
-
|
28
|
+
{
|
29
|
+
const main = fiber.spawn(function* (proc) {
|
30
|
+
// Start f1, wait for it to finish and get the result
|
31
|
+
const res = yield* fiber.join(fiber.spawn(f1));
|
32
|
+
console.log('Fiber 2 recieved:', res);
|
50
33
|
|
51
|
-
|
52
|
-
|
53
|
-
|
34
|
+
// Start f1 and make its lifetime depends on current fiber
|
35
|
+
const childProc = fiber.spawn(f1);
|
36
|
+
fiber.mount(childProc, proc);
|
37
|
+
});
|
54
38
|
|
55
|
-
|
56
|
-
fiber.mount(fiber.spawn(f1), proc);
|
39
|
+
console.log('Fiber 2 started:', fiber.resumed(main));
|
57
40
|
|
58
|
-
//
|
59
|
-
|
60
|
-
|
41
|
+
// Pause the current fiber process
|
42
|
+
fiber.pause(main);
|
43
|
+
console.log('Fiber 2 is paused:', fiber.paused(main));
|
44
|
+
|
45
|
+
// Resume the fiber
|
46
|
+
fiber.resume(main);
|
47
|
+
console.log('Fiber 2 is resumed:', fiber.resumed(main));
|
48
|
+
|
49
|
+
// Wait for the fiber process to finish
|
50
|
+
await fiber.done(main);
|
51
|
+
|
52
|
+
// Check finish status
|
53
|
+
console.log('Fiber 2 completed', fiber.completed(main));
|
54
|
+
}
|
55
|
+
|
56
|
+
{
|
57
|
+
console.log('------------------------');
|
58
|
+
|
59
|
+
const main = fiber.spawn(f1);
|
60
|
+
console.log('Fiber 1 started:', fiber.resumed(main));
|
61
|
+
|
62
|
+
// Stop a fiber
|
63
|
+
fiber.stop(main);
|
64
|
+
console.log('Fiber 1 stopped:', fiber.stopped(main));
|
65
|
+
}
|
61
66
|
```
|
62
67
|
|
63
|
-
|
68
|
+
### Latch
|
64
69
|
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
65
70
|
|
66
71
|
```ts
|
@@ -87,7 +92,45 @@ task();
|
|
87
92
|
prepare();
|
88
93
|
```
|
89
94
|
|
90
|
-
|
95
|
+
### Channel
|
96
|
+
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.
|
97
|
+
|
98
|
+
```ts
|
99
|
+
import * as channel from 'ciorent/channel';
|
100
|
+
import * as co from 'ciorent';
|
101
|
+
|
102
|
+
const c = channel.init<number>();
|
103
|
+
|
104
|
+
const run = async () => {
|
105
|
+
for (let i = 0; i < 5; i++) {
|
106
|
+
await co.sleep(100);
|
107
|
+
channel.send(c, i);
|
108
|
+
console.log('Sent', i);
|
109
|
+
}
|
110
|
+
|
111
|
+
// Resolve all waiting promises with `undefined`
|
112
|
+
// This is a way to tell the reciever to not listen to more data
|
113
|
+
channel.flush(c);
|
114
|
+
};
|
115
|
+
|
116
|
+
const log = async () => {
|
117
|
+
while (true) {
|
118
|
+
// Wait until a value is sent to the channel
|
119
|
+
const x = await channel.recieve(c);
|
120
|
+
if (x == null) break;
|
121
|
+
|
122
|
+
console.log('Recieved', x);
|
123
|
+
};
|
124
|
+
}
|
125
|
+
|
126
|
+
log();
|
127
|
+
run();
|
128
|
+
|
129
|
+
// This runs first
|
130
|
+
console.log('Starting...');
|
131
|
+
```
|
132
|
+
|
133
|
+
### Pubsub
|
91
134
|
A fast, simple publish-subscribe API.
|
92
135
|
|
93
136
|
```ts
|
@@ -123,46 +166,37 @@ co.spawn(3, async (id: number) => {
|
|
123
166
|
publisher();
|
124
167
|
```
|
125
168
|
|
126
|
-
|
127
|
-
|
169
|
+
### Semaphore
|
170
|
+
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
128
171
|
|
129
172
|
```ts
|
130
|
-
import * as
|
173
|
+
import * as semaphore from 'ciorent/semaphore';
|
131
174
|
import * as co from 'ciorent';
|
132
175
|
|
133
|
-
|
176
|
+
// Only allow 2 task to run concurrently
|
177
|
+
const sem = semaphore.init(2);
|
134
178
|
|
135
|
-
const
|
136
|
-
|
137
|
-
|
138
|
-
channel.send(c, i);
|
139
|
-
console.log('Sent', i);
|
140
|
-
}
|
179
|
+
const task = async (id: number) => {
|
180
|
+
// Acquire the semaphore or wait for the semaphore to be available
|
181
|
+
await semaphore.pause(sem);
|
141
182
|
|
142
|
-
|
143
|
-
// This is a way to tell the reciever to not listen to more data
|
144
|
-
channel.flush(c);
|
145
|
-
};
|
183
|
+
console.log('Task', id, 'started');
|
146
184
|
|
147
|
-
|
148
|
-
|
149
|
-
// Wait until a value is sent to the channel
|
150
|
-
const x = await channel.recieve(c);
|
151
|
-
if (x == null) break;
|
185
|
+
// Let the main thread schedules other tasks
|
186
|
+
for (let i = 1; i <= 5; i++) await co.pause;
|
152
187
|
|
153
|
-
|
154
|
-
};
|
155
|
-
}
|
188
|
+
console.log('Task', id, 'end');
|
156
189
|
|
157
|
-
|
158
|
-
|
190
|
+
// Release the semaphore
|
191
|
+
semaphore.signal(sem);
|
192
|
+
}
|
159
193
|
|
160
|
-
//
|
161
|
-
|
194
|
+
// Try to run 5 tasks concurrently
|
195
|
+
co.spawn(5, task);
|
162
196
|
```
|
163
197
|
|
164
|
-
|
165
|
-
|
198
|
+
### Utilities
|
199
|
+
#### Pausing
|
166
200
|
Delay the execution of a function for other asynchronous tasks to run.
|
167
201
|
```ts
|
168
202
|
import * as co from 'ciorent';
|
@@ -194,7 +228,7 @@ task1();
|
|
194
228
|
task2();
|
195
229
|
```
|
196
230
|
|
197
|
-
|
231
|
+
#### Sleep
|
198
232
|
Cross-runtime synchronous and asynchronous sleep functions.
|
199
233
|
```ts
|
200
234
|
import * as co from 'ciorent';
|
@@ -213,7 +247,7 @@ co.sleepSync(500);
|
|
213
247
|
logTime('After another 0.5s');
|
214
248
|
```
|
215
249
|
|
216
|
-
|
250
|
+
#### Spawning tasks
|
217
251
|
Utilities to create and run tasks.
|
218
252
|
```ts
|
219
253
|
import * as co from 'ciorent';
|
@@ -232,7 +266,7 @@ console.log('Running 5 tasks concurrently:');
|
|
232
266
|
await Promise.all(co.spawn(5, task));
|
233
267
|
```
|
234
268
|
|
235
|
-
|
269
|
+
#### Debounce
|
236
270
|
Postpones execution until after an idle period.
|
237
271
|
```ts
|
238
272
|
import * as co from 'ciorent';
|
@@ -246,24 +280,7 @@ await co.sleep(100);
|
|
246
280
|
fn(2); // fn(2) gets executed
|
247
281
|
```
|
248
282
|
|
249
|
-
|
250
|
-
Limits the number of calls within a time window.
|
251
|
-
```ts
|
252
|
-
import * as co from 'ciorent';
|
253
|
-
|
254
|
-
// Allow 2 calls in 500ms, other calls are dropped
|
255
|
-
const fn = co.rateLimit((id: number) => {
|
256
|
-
console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
|
257
|
-
}, 500, 2);
|
258
|
-
|
259
|
-
// Some calls will be dropped
|
260
|
-
for (let i = 0; i < 8; i++) {
|
261
|
-
fn(i);
|
262
|
-
await co.sleep(400);
|
263
|
-
}
|
264
|
-
```
|
265
|
-
|
266
|
-
### Throttle
|
283
|
+
#### Throttle
|
267
284
|
Executes a function at a regular interval.
|
268
285
|
```ts
|
269
286
|
import * as co from 'ciorent';
|
package/fiber.d.ts
CHANGED
@@ -35,9 +35,9 @@ export declare const paused: (t: Process) => boolean;
|
|
35
35
|
*/
|
36
36
|
export declare const resumed: (t: Process) => boolean;
|
37
37
|
/**
|
38
|
-
* Check whether the fiber has
|
38
|
+
* Check whether the fiber has completed
|
39
39
|
*/
|
40
|
-
export declare const
|
40
|
+
export declare const completed: (t: Process) => boolean;
|
41
41
|
/**
|
42
42
|
* Check whether the fiber has been interrupted
|
43
43
|
*/
|
@@ -76,7 +76,7 @@ export declare function join<T extends Process>(t: T): Generator<Awaited<T[0]>,
|
|
76
76
|
* Wait for a fiber to finish and retrieve its result
|
77
77
|
* @param t
|
78
78
|
*/
|
79
|
-
export declare const
|
79
|
+
export declare const done: <T extends Process>(t: T) => T[0];
|
80
80
|
/**
|
81
81
|
* Mount child fiber lifetime to parent lifetime
|
82
82
|
* @param child
|
package/fiber.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let
|
1
|
+
export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let completed=(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{if(thread[1]!==2)thread[1]=3;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]!==2){if(t[1]===0)t[2]?.();t[1]=3}};export function*join(t){return yield t[0]}export let done=(t)=>t[0];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
@@ -41,13 +41,6 @@ export declare const spawn: <const T extends any[], const R>(n: number, task: (.
|
|
41
41
|
* @param ms - The time period in milliseconds
|
42
42
|
*/
|
43
43
|
export declare const debounce: <const Args extends any[]>(f: (...args: Args) => any, ms: number) => ((...args: Args) => void);
|
44
|
-
/**
|
45
|
-
* Drop function calls for a specific period
|
46
|
-
* @param f - The target function to rate limit (it must not throw errors)
|
47
|
-
* @param ms - The time period in milliseconds
|
48
|
-
* @param limit - The call limit in the time period
|
49
|
-
*/
|
50
|
-
export declare const rateLimit: <const Args extends any[]>(f: (...args: Args) => any, ms: number, limit: number) => ((...args: Args) => void);
|
51
44
|
/**
|
52
45
|
* Throttle function execution for a time period
|
53
46
|
* @param f - The function to throttle (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 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
|
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 throttle=(f,ms,limit)=>{let head=[null];let tail=head;let cur=limit;let scheduled=false;let unlock=()=>{cur=limit;while(cur>0){if(tail===head){scheduled=false;return}cur--;tail=tail[0];tail[1](f(...tail[2]))}setTimeout(unlock,ms)};return(...a)=>{if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r,a];return p}if(!scheduled){scheduled=true;setTimeout(unlock,ms)}cur--;return f(...a)}};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.5",
|
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
|
-
"./dropping-queue": "./dropping-queue.js",
|
23
22
|
"./sliding-queue": "./sliding-queue.js",
|
24
|
-
"
|
25
|
-
"./topic": "./topic.js",
|
26
|
-
"./semaphore": "./semaphore.js",
|
23
|
+
"./latch": "./latch.js",
|
27
24
|
"./channel": "./channel.js",
|
28
25
|
"./fiber": "./fiber.js",
|
29
|
-
"./
|
26
|
+
"./dropping-queue": "./dropping-queue.js",
|
27
|
+
".": "./index.js",
|
28
|
+
"./topic": "./topic.js",
|
29
|
+
"./semaphore": "./semaphore.js"
|
30
30
|
}
|
31
31
|
}
|