ciorent 0.0.26 → 0.1.0
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 -89
- package/channel.js +1 -1
- package/fiber.js +1 -1
- package/fixed-queue.d.ts +1 -1
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/package.json +7 -7
- package/semaphore.js +1 -1
- package/topic.js +1 -1
package/README.md
CHANGED
@@ -1,27 +1,74 @@
|
|
1
1
|
A lightweight, low-overhead concurrency library.
|
2
|
-
##
|
3
|
-
|
2
|
+
## Channel
|
3
|
+
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.
|
4
4
|
|
5
5
|
```ts
|
6
|
-
import * as
|
6
|
+
import * as channel from 'ciorent/channel';
|
7
7
|
import * as cio from 'ciorent';
|
8
8
|
|
9
|
-
const
|
10
|
-
async (task: number) => {
|
11
|
-
for (let i = 1; i <= 5; i++) {
|
12
|
-
console.log('Task', task, 'iteration', i);
|
13
|
-
await cio.pause;
|
14
|
-
}
|
9
|
+
const c = channel.init<number>();
|
15
10
|
|
16
|
-
|
11
|
+
const run = async () => {
|
12
|
+
for (let i = 0; i < 5; i++) {
|
13
|
+
await cio.sleep(100);
|
14
|
+
channel.send(c, i);
|
15
|
+
console.log('Sent', i);
|
17
16
|
}
|
18
|
-
);
|
19
17
|
|
20
|
-
//
|
21
|
-
|
18
|
+
// Resolve all waiting promises with `undefined`
|
19
|
+
// This is a way to tell the reciever to not listen to more data
|
20
|
+
channel.flush(c);
|
21
|
+
};
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
const log = async () => {
|
24
|
+
while (true) {
|
25
|
+
// Wait until a value is sent to the channel
|
26
|
+
const x = await channel.recieve(c);
|
27
|
+
if (x == null) break;
|
28
|
+
|
29
|
+
console.log('Recieved', x);
|
30
|
+
};
|
31
|
+
}
|
32
|
+
|
33
|
+
log();
|
34
|
+
run();
|
35
|
+
|
36
|
+
// This runs first
|
37
|
+
console.log('Starting...');
|
38
|
+
```
|
39
|
+
|
40
|
+
## Fibers
|
41
|
+
Virtual threads with more controlled execution.
|
42
|
+
|
43
|
+
```ts
|
44
|
+
import * as cio from 'ciorent';
|
45
|
+
import * as fiber from 'ciorent/fiber';
|
46
|
+
|
47
|
+
const thread1 = fiber.fn(function* () {
|
48
|
+
console.log('Fiber 1 started');
|
49
|
+
|
50
|
+
// Thread1 will be interrupted by thread2
|
51
|
+
// As thread2 will end first
|
52
|
+
yield cio.sleep(1000);
|
53
|
+
|
54
|
+
console.log('Fiber 1 done');
|
55
|
+
});
|
56
|
+
|
57
|
+
const thread2 = fiber.fn(function* (thread) {
|
58
|
+
console.log('Fiber 2 started');
|
59
|
+
|
60
|
+
yield;
|
61
|
+
console.log('Fiber 2 resumed');
|
62
|
+
|
63
|
+
// Start thread 1 and make thread1
|
64
|
+
// lifetime depends on thread2
|
65
|
+
fiber.mount(fiber.spawn(thread1), thread);
|
66
|
+
|
67
|
+
console.log('Fiber 2 done');
|
68
|
+
});
|
69
|
+
|
70
|
+
// Start running the thread
|
71
|
+
fiber.spawn(thread2);
|
25
72
|
```
|
26
73
|
|
27
74
|
## Latch
|
@@ -67,40 +114,6 @@ await main();
|
|
67
114
|
await main();
|
68
115
|
```
|
69
116
|
|
70
|
-
## Fibers
|
71
|
-
Virtual threads with more controlled execution.
|
72
|
-
|
73
|
-
```ts
|
74
|
-
import * as cio from 'ciorent';
|
75
|
-
import * as fiber from 'ciorent/fiber';
|
76
|
-
|
77
|
-
const thread1 = fiber.fn(function* () {
|
78
|
-
console.log('Fiber 1 started');
|
79
|
-
|
80
|
-
// Thread1 will be interrupted by thread2
|
81
|
-
// As thread2 will end first
|
82
|
-
yield cio.sleep(1000);
|
83
|
-
|
84
|
-
console.log('Fiber 1 done');
|
85
|
-
});
|
86
|
-
|
87
|
-
const thread2 = fiber.fn(function* (thread) {
|
88
|
-
console.log('Fiber 2 started');
|
89
|
-
|
90
|
-
yield;
|
91
|
-
console.log('Fiber 2 resumed');
|
92
|
-
|
93
|
-
// Start thread 1 and make thread1
|
94
|
-
// lifetime depends on thread2
|
95
|
-
fiber.mount(fiber.spawn(thread1), thread);
|
96
|
-
|
97
|
-
console.log('Fiber 2 done');
|
98
|
-
});
|
99
|
-
|
100
|
-
// Start running the thread
|
101
|
-
fiber.spawn(thread2);
|
102
|
-
```
|
103
|
-
|
104
117
|
## Pubsub
|
105
118
|
A fast, simple publish-subscribe API.
|
106
119
|
|
@@ -112,7 +125,7 @@ const messages = topic.init<number>();
|
|
112
125
|
|
113
126
|
// A task that publish messages
|
114
127
|
const publisher = async () => {
|
115
|
-
for (let i = 0; i <
|
128
|
+
for (let i = 0; i < 3; i++) {
|
116
129
|
await cio.sleep(100);
|
117
130
|
topic.pub(messages, i);
|
118
131
|
}
|
@@ -122,57 +135,44 @@ const publisher = async () => {
|
|
122
135
|
topic.flush(messages);
|
123
136
|
}
|
124
137
|
|
125
|
-
// Spawn
|
126
|
-
cio.concurrent(
|
138
|
+
// Spawn 3 tasks that recieve messages
|
139
|
+
cio.concurrent(3, async (id: number) => {
|
127
140
|
const sub = topic.sub(messages);
|
128
141
|
|
129
142
|
while (true) {
|
130
143
|
// Block until the value is sent
|
131
144
|
const x = await topic.recieve(sub);
|
132
145
|
if (x == null) break;
|
133
|
-
console.log(`Task ${id}: ${x}`);
|
146
|
+
console.log(`Task ${id} recieved: ${x}`);
|
134
147
|
}
|
135
148
|
});
|
136
149
|
|
137
150
|
publisher();
|
138
151
|
```
|
139
152
|
|
140
|
-
##
|
141
|
-
|
153
|
+
## Semaphore
|
154
|
+
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
142
155
|
|
143
156
|
```ts
|
144
|
-
import * as
|
157
|
+
import * as semaphore from 'ciorent/semaphore';
|
145
158
|
import * as cio from 'ciorent';
|
146
159
|
|
147
|
-
const
|
148
|
-
|
149
|
-
|
150
|
-
for (let i = 0; i < 10; i++) {
|
151
|
-
await cio.sleep(10);
|
152
|
-
channel.send(c, i);
|
153
|
-
console.log('Sent', i);
|
154
|
-
}
|
155
|
-
|
156
|
-
// Resolve all waiting promises with `undefined`
|
157
|
-
// This is a way to tell the reciever to not listen to more data
|
158
|
-
channel.flush(c);
|
159
|
-
};
|
160
|
+
const task = semaphore.wrap(
|
161
|
+
async (task: number) => {
|
162
|
+
console.log('Task', task, 'started');
|
160
163
|
|
161
|
-
|
162
|
-
|
163
|
-
// Wait until a value is sent
|
164
|
-
const x = await channel.recieve(c);
|
165
|
-
if (x == null) break;
|
164
|
+
for (let i = 1; i <= 5; i++)
|
165
|
+
await cio.pause;
|
166
166
|
|
167
|
-
console.log('
|
168
|
-
}
|
169
|
-
|
167
|
+
console.log('Task', task, 'end');
|
168
|
+
}
|
169
|
+
);
|
170
170
|
|
171
|
-
run
|
172
|
-
|
171
|
+
// Only allow 2 task to run concurrently
|
172
|
+
const sem = semaphore.init(2);
|
173
173
|
|
174
|
-
//
|
175
|
-
|
174
|
+
// Try to run 6 tasks concurrently
|
175
|
+
cio.concurrent(6, (sem, id) => task(sem, id), sem);
|
176
176
|
```
|
177
177
|
|
178
178
|
## Utilities
|
@@ -212,14 +212,14 @@ task2();
|
|
212
212
|
### Sleep
|
213
213
|
Cross-runtime synchronous and asynchronous sleep functions.
|
214
214
|
```ts
|
215
|
-
import
|
215
|
+
import * as cio from 'ciorent';
|
216
216
|
|
217
|
-
await sleep(500);
|
217
|
+
await cio.sleep(500);
|
218
218
|
console.log('Hi');
|
219
219
|
|
220
220
|
// This blocks the current thread
|
221
221
|
// On the browser this only works in workers
|
222
|
-
sleepSync(500);
|
222
|
+
cio.sleepSync(500);
|
223
223
|
console.log('Hi');
|
224
224
|
```
|
225
225
|
|
@@ -229,17 +229,17 @@ Utilities to create and run tasks.
|
|
229
229
|
import * as cio from 'ciorent';
|
230
230
|
|
231
231
|
const task = async (id: number) => {
|
232
|
-
await cio.sleep(
|
232
|
+
await cio.sleep((10 - id) * 20 + 50);
|
233
233
|
console.log('Task', id, 'done');
|
234
234
|
}
|
235
235
|
|
236
236
|
// Spawn and run 5 tasks sequentially
|
237
237
|
console.log('Running 5 tasks sequentially:');
|
238
|
-
cio.sequential(5, task);
|
238
|
+
await cio.sequential(5, task);
|
239
239
|
|
240
240
|
// Spawn and run 5 tasks concurrently
|
241
241
|
console.log('Running 5 tasks concurrently:');
|
242
|
-
cio.concurrent(5, task);
|
242
|
+
await cio.concurrent(5, task);
|
243
243
|
```
|
244
244
|
|
245
245
|
### Debounce
|
@@ -283,7 +283,6 @@ const fn = cio.throttle((id: number) => {
|
|
283
283
|
console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
|
284
284
|
}, 500, 2);
|
285
285
|
|
286
|
-
|
287
|
-
fn(i);
|
286
|
+
cio.concurrent(8, (id) => fn(id));
|
288
287
|
```
|
289
288
|
|
package/channel.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let init=()=>{let qu=[null
|
1
|
+
export let init=()=>{let qu=[null];let resolveQu=[null,null];return[qu,qu,resolveQu,resolveQu]};export let send=(c,t)=>{if(c[3][0]!==null)(c[3]=c[3][0])[1](t);else c[0]=c[0][0]=[null,t]};export let recieve=(c)=>c[1][0]!==null?Promise.resolve((c[1]=c[1][0])[1]):new Promise((res)=>{c[2]=c[2][0]=[null,res]});export let poll=(c)=>c[1][0]!==null?(c[0]=c[1][0])[1]:undefined;export let flush=(c)=>{while(c[3][0]!==null)(c[3]=c[3][0])[1]()};
|
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)=>{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]===2){thread[3].forEach(stop);return v}t=g.next(v)}thread[1]=2;thread[3].forEach(stop)
|
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]===2){thread[3].forEach(stop);return v}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[1]=2;t[2]?.()}else t[1]=2};export function*join(t){return yield t[1]}export let finish=(t)=>t[1];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/fixed-queue.d.ts
CHANGED
@@ -26,7 +26,7 @@ export interface FixedQueue<T extends {}> {
|
|
26
26
|
/**
|
27
27
|
* Describe a queue node (singly linked list node)
|
28
28
|
*/
|
29
|
-
export type QueueNode<T> = [
|
29
|
+
export type QueueNode<T> = [next: QueueNode<T> | null, value: T];
|
30
30
|
/**
|
31
31
|
* Create a fixed queue.
|
32
32
|
* @param n - The queue size
|
package/index.d.ts
CHANGED
@@ -35,20 +35,20 @@ export declare const sequential: <const T extends any[]>(n: number, task: (...ar
|
|
35
35
|
export declare const concurrent: <const T extends any[], const R>(n: number, task: (...args: [...T, id: number]) => Promise<R>, ...args: T) => Promise<R[]>;
|
36
36
|
/**
|
37
37
|
* Drop function calls until it doesn't get called for a specific period.
|
38
|
-
* @param f - The target function to debounce
|
38
|
+
* @param f - The target function to debounce (it must not throw errors)
|
39
39
|
* @param ms - The time period in milliseconds
|
40
40
|
*/
|
41
41
|
export declare const debounce: <const Args extends any[]>(f: (...args: Args) => any, ms: number) => ((...args: Args) => void);
|
42
42
|
/**
|
43
43
|
* Drop function calls for a specific period
|
44
|
-
* @param f - The target function to rate limit
|
44
|
+
* @param f - The target function to rate limit (it must not throw errors)
|
45
45
|
* @param ms - The time period in milliseconds
|
46
46
|
* @param limit - The call limit in the time period
|
47
47
|
*/
|
48
48
|
export declare const rateLimit: <const Args extends any[]>(f: (...args: Args) => any, ms: number, limit: number) => ((...args: Args) => void);
|
49
49
|
/**
|
50
50
|
* Throttle function execution for a time period
|
51
|
-
* @param f - The function to throttle
|
51
|
+
* @param f - The function to throttle (it must not throw errors)
|
52
52
|
* @param ms - The time in milliseconds
|
53
53
|
* @param limit - The call limit in the time period
|
54
54
|
*/
|
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 concurrent=(n,task,...args)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(...args,i);return Promise.all(arr)};export let debounce=(f,ms)=>{let id;return(...a)=>{clearTimeout(id);id=setTimeout(f,ms,...a)}};export let rateLimit=(f,ms,limit)=>{let call=()=>{limit++};return(...a)=>{if(limit>0){limit--;
|
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 concurrent=(n,task,...args)=>{let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(...args,i);return Promise.all(arr)};export let debounce=(f,ms)=>{let id;return(...a)=>{clearTimeout(id);id=setTimeout(f,ms,...a)}};export let rateLimit=(f,ms,limit)=>{let call=()=>{limit++};return(...a)=>{if(limit>0){limit--;f(...a);setTimeout(call,ms)}}};export let throttle=(f,ms,limit)=>{let head=[null];let tail=head;let unlock=()=>{if(tail!==head){tail=tail[0];tail[1](f(...tail[2]));setTimeout(unlock,ms)}else limit++};return(...a)=>{if(limit===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r,a];return p}limit--;setTimeout(unlock,ms);return f(...a)}};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.0
|
3
|
+
"version": "0.1.0",
|
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
|
-
"./latch": "./latch.js",
|
23
22
|
"./sliding-queue": "./sliding-queue.js",
|
24
|
-
"
|
25
|
-
"./
|
26
|
-
"./topic": "./topic.js",
|
27
|
-
"./channel": "./channel.js",
|
23
|
+
"./fiber": "./fiber.js",
|
24
|
+
"./latch": "./latch.js",
|
28
25
|
"./dropping-queue": "./dropping-queue.js",
|
29
|
-
"./
|
26
|
+
"./topic": "./topic.js",
|
27
|
+
"./semaphore": "./semaphore.js",
|
28
|
+
".": "./index.js",
|
29
|
+
"./channel": "./channel.js"
|
30
30
|
}
|
31
31
|
}
|
package/semaphore.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as resolvedPromise}from"./index.js";export let init=(n)=>{let root=[null
|
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 wrap=(f)=>async(s,...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{signal(s)}};
|
package/topic.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let init=()=>[[null
|
1
|
+
export let init=()=>[[null],[],[]];export let sub=(t)=>[t,t[0]];export let pub=(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)})};
|