ciorent 0.2.0 → 0.3.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 +152 -137
- package/channel.d.ts +6 -17
- package/channel.js +1 -1
- package/dropping-queue.d.ts +3 -3
- package/dropping-queue.js +1 -1
- package/fiber.d.ts +9 -3
- package/fiber.js +1 -1
- package/index.d.ts +1 -2
- package/index.js +1 -1
- package/lock.d.ts +36 -0
- package/lock.js +1 -0
- package/package.json +7 -6
- package/{fixed-queue.d.ts → queue.d.ts} +7 -4
- package/queue.js +1 -0
- package/semaphore.d.ts +5 -13
- package/semaphore.js +1 -1
- package/sliding-queue.d.ts +4 -4
- package/sliding-queue.js +1 -1
- package/topic.d.ts +2 -6
- package/topic.js +1 -1
- package/fixed-queue.js +0 -1
package/README.md
CHANGED
@@ -7,91 +7,103 @@ A lightweight, low-overhead concurrency library.
|
|
7
7
|
- Fully type-safe.
|
8
8
|
|
9
9
|
## Examples
|
10
|
-
###
|
11
|
-
|
12
|
-
|
10
|
+
### Utilities
|
11
|
+
#### Spawning tasks
|
12
|
+
Utilities to create and run tasks.
|
13
13
|
```ts
|
14
|
-
import * as semaphore from 'ciorent/semaphore';
|
15
14
|
import * as co from 'ciorent';
|
16
15
|
|
17
|
-
// Only allow 2 task to run concurrently
|
18
|
-
const sem = semaphore.init(2);
|
19
|
-
|
20
16
|
const task = async (id: number) => {
|
21
|
-
|
22
|
-
|
17
|
+
await co.sleep((10 - id) * 20 + 50);
|
18
|
+
console.log('Task', id, 'done');
|
19
|
+
}
|
23
20
|
|
24
|
-
|
21
|
+
// Spawn and run 5 tasks sequentially
|
22
|
+
console.log('Running 5 tasks sequentially:');
|
23
|
+
await co.sequential(5, task);
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
// Spawn and run 5 tasks concurrently
|
26
|
+
console.log('Running 5 tasks concurrently:');
|
27
|
+
await Promise.all(co.spawn(5, task));
|
28
|
+
```
|
28
29
|
|
29
|
-
|
30
|
+
#### Sleep
|
31
|
+
Cross-runtime synchronous and asynchronous sleep functions.
|
32
|
+
```ts
|
33
|
+
import * as co from 'ciorent';
|
30
34
|
|
31
|
-
|
32
|
-
semaphore.signal(sem);
|
33
|
-
}
|
35
|
+
const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
|
34
36
|
|
35
|
-
|
36
|
-
co.spawn(5, task);
|
37
|
-
```
|
37
|
+
logTime('Start');
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
// Non-blocking
|
40
|
+
await co.sleep(500);
|
41
|
+
logTime('After about 0.5s');
|
41
42
|
|
43
|
+
// This blocks the event loop
|
44
|
+
// On the browser this only works in workers and blocks the worker thread
|
45
|
+
co.sleepSync(500);
|
46
|
+
logTime('After another 0.5s');
|
47
|
+
```
|
48
|
+
|
49
|
+
#### Debounce
|
50
|
+
Postpones execution until after an idle period.
|
42
51
|
```ts
|
43
52
|
import * as co from 'ciorent';
|
44
|
-
import * as fiber from 'ciorent/fiber';
|
45
|
-
|
46
|
-
const f1 = fiber.fn(function* () {
|
47
|
-
// Wait for a promise
|
48
|
-
yield co.sleep(1000);
|
49
53
|
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
const fn = co.debounce((id: number) => {
|
55
|
+
console.log('ID:', id);
|
56
|
+
}, 500);
|
53
57
|
|
54
|
-
|
55
|
-
|
58
|
+
fn(1); // fn(1) gets skipped
|
59
|
+
await co.sleep(100);
|
60
|
+
fn(2); // fn(2) gets executed
|
61
|
+
```
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
console.log('Fiber 2 recieved:', res);
|
63
|
+
#### Throttle
|
64
|
+
Executes a function at a regular interval.
|
65
|
+
```ts
|
66
|
+
import * as co from 'ciorent';
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
fiber.mount(childProc, proc);
|
66
|
-
});
|
68
|
+
// Allow 2 calls in 500ms
|
69
|
+
const throttle = co.throttle(500, 2);
|
67
70
|
|
68
|
-
|
71
|
+
co.spawn(8, async (id) => {
|
72
|
+
await throttle();
|
73
|
+
console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
|
74
|
+
});
|
75
|
+
```
|
69
76
|
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
#### Pausing
|
78
|
+
Delay the execution of a function for other asynchronous tasks to run.
|
79
|
+
```ts
|
80
|
+
import * as co from 'ciorent';
|
73
81
|
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
// Expensive sync task
|
83
|
+
const task1 = async () => {
|
84
|
+
let x = 0;
|
77
85
|
|
78
|
-
//
|
79
|
-
|
86
|
+
// Yield control back to the runtime, allowing it to
|
87
|
+
// schedule other tasks
|
88
|
+
await co.pause;
|
80
89
|
|
81
|
-
//
|
82
|
-
|
83
|
-
|
90
|
+
// Simulate heavy operation
|
91
|
+
for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
|
92
|
+
x += Math.random() * 32 + i * Math.round(Math.random() * 16);
|
84
93
|
|
85
|
-
|
86
|
-
|
94
|
+
console.log('Finish task 1:', x);
|
95
|
+
};
|
87
96
|
|
88
|
-
|
89
|
-
|
97
|
+
// Short async task
|
98
|
+
const task2 = async () => {
|
99
|
+
console.log('Start fetching...');
|
100
|
+
const txt = await fetch('http://example.com');
|
101
|
+
console.log('Fetch status', txt.status);
|
102
|
+
};
|
90
103
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
}
|
104
|
+
// Task 2 will not get blocked by task 1
|
105
|
+
task1();
|
106
|
+
task2();
|
95
107
|
```
|
96
108
|
|
97
109
|
### Pubsub
|
@@ -130,6 +142,35 @@ co.spawn(3, async (id: number) => {
|
|
130
142
|
publisher();
|
131
143
|
```
|
132
144
|
|
145
|
+
### Semaphore
|
146
|
+
Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
|
147
|
+
|
148
|
+
```ts
|
149
|
+
import * as semaphore from 'ciorent/semaphore';
|
150
|
+
import * as co from 'ciorent';
|
151
|
+
|
152
|
+
// Only allow 2 task to run concurrently
|
153
|
+
const sem = semaphore.init(2);
|
154
|
+
|
155
|
+
const task = async (id: number) => {
|
156
|
+
// Acquire the semaphore or wait for the semaphore to be available
|
157
|
+
await semaphore.acquire(sem);
|
158
|
+
|
159
|
+
console.log('Task', id, 'started');
|
160
|
+
|
161
|
+
// Let the main thread schedules other tasks
|
162
|
+
for (let i = 1; i <= 5; i++) await co.pause;
|
163
|
+
|
164
|
+
console.log('Task', id, 'end');
|
165
|
+
|
166
|
+
// Release the semaphore
|
167
|
+
semaphore.release(sem);
|
168
|
+
}
|
169
|
+
|
170
|
+
// Try to run 5 tasks concurrently
|
171
|
+
co.spawn(5, task);
|
172
|
+
```
|
173
|
+
|
133
174
|
### Latch
|
134
175
|
Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
|
135
176
|
|
@@ -195,102 +236,76 @@ run();
|
|
195
236
|
console.log('Starting...');
|
196
237
|
```
|
197
238
|
|
198
|
-
###
|
199
|
-
|
200
|
-
|
239
|
+
### Fibers
|
240
|
+
Virtual threads with more controlled execution.
|
241
|
+
|
201
242
|
```ts
|
202
243
|
import * as co from 'ciorent';
|
244
|
+
import * as fiber from 'ciorent/fiber';
|
203
245
|
|
204
|
-
|
205
|
-
const task1 = async () => {
|
206
|
-
let x = 0;
|
207
|
-
|
208
|
-
// Yield control back to the runtime, allowing it to
|
209
|
-
// schedule other tasks
|
210
|
-
await co.pause;
|
211
|
-
|
212
|
-
// Simulate heavy operation
|
213
|
-
for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
|
214
|
-
x += Math.random() * 32 + i * Math.round(Math.random() * 16);
|
215
|
-
|
216
|
-
console.log('Finish task 1:', x);
|
217
|
-
};
|
218
|
-
|
219
|
-
// Short async task
|
220
|
-
const task2 = async () => {
|
221
|
-
console.log('Start fetching...');
|
222
|
-
const txt = await fetch('http://example.com');
|
223
|
-
console.log('Fetch status', txt.status);
|
224
|
-
};
|
246
|
+
const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
|
225
247
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
248
|
+
const f1 = fiber.fn(function* () {
|
249
|
+
// Wait for a promise
|
250
|
+
console.log('Fiber 1 waiting: 1s');
|
251
|
+
yield co.sleep(1000);
|
230
252
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
import * as co from 'ciorent';
|
253
|
+
// Wait for a promise and return its result
|
254
|
+
const res = yield* fiber.unwrap(Promise.resolve(1));
|
255
|
+
console.log('Fiber 1 recieved:', res);
|
235
256
|
|
236
|
-
|
257
|
+
return Math.random();
|
258
|
+
});
|
237
259
|
|
238
|
-
|
260
|
+
{
|
261
|
+
// Start the fiber process on next event loop cycle
|
262
|
+
const main = fiber.spawn(function* (proc) {
|
263
|
+
// Start f1, wait for the process to complete and get the result
|
264
|
+
console.log('Fiber 2: joins fiber 1');
|
265
|
+
const res = yield* fiber.join(fiber.spawn(f1));
|
266
|
+
console.log('Fiber 2 recieved:', res);
|
239
267
|
|
240
|
-
//
|
241
|
-
|
242
|
-
|
268
|
+
// Start f1 and make its lifetime depends on current fiber
|
269
|
+
console.log('Fiber 2: spawns fiber 1');
|
270
|
+
const childProc = fiber.spawn(f1);
|
271
|
+
fiber.mount(childProc, proc);
|
272
|
+
});
|
243
273
|
|
244
|
-
|
245
|
-
// On the browser this only works in workers and blocks the worker thread
|
246
|
-
co.sleepSync(500);
|
247
|
-
logTime('After another 0.5s');
|
248
|
-
```
|
274
|
+
console.log('Fiber 2 started:', fiber.resumed(main));
|
249
275
|
|
250
|
-
|
251
|
-
|
252
|
-
```ts
|
253
|
-
import * as co from 'ciorent';
|
276
|
+
// Wait for the fiber process to finish
|
277
|
+
await fiber.done(main);
|
254
278
|
|
255
|
-
|
256
|
-
|
257
|
-
console.log('Task', id, 'done');
|
279
|
+
// Check finish status
|
280
|
+
console.log('Fiber 2 completed:', fiber.completed(main));
|
258
281
|
}
|
259
282
|
|
260
|
-
|
261
|
-
console.log('
|
262
|
-
await co.sequential(5, task);
|
283
|
+
{
|
284
|
+
console.log('------------------------');
|
263
285
|
|
264
|
-
|
265
|
-
console.log('
|
266
|
-
await Promise.all(co.spawn(5, task));
|
267
|
-
```
|
286
|
+
const main = fiber.spawn(f1);
|
287
|
+
console.log('Fiber 1 started:', fiber.resumed(main));
|
268
288
|
|
269
|
-
|
270
|
-
|
271
|
-
```ts
|
272
|
-
import * as co from 'ciorent';
|
289
|
+
// Interrupt a fiber
|
290
|
+
fiber.interrupt(main);
|
273
291
|
|
274
|
-
|
275
|
-
|
276
|
-
}, 500);
|
292
|
+
// Execution will be stopped on the last yield
|
293
|
+
await fiber.done(main);
|
277
294
|
|
278
|
-
|
279
|
-
|
280
|
-
fn(2); // fn(2) gets executed
|
281
|
-
```
|
295
|
+
console.log('Fiber 1 interrupted:', fiber.interrupted(main));
|
296
|
+
}
|
282
297
|
|
283
|
-
|
284
|
-
|
285
|
-
```ts
|
286
|
-
import * as co from 'ciorent';
|
298
|
+
{
|
299
|
+
console.log('------------------------');
|
287
300
|
|
288
|
-
|
289
|
-
|
301
|
+
const main = fiber.spawn(f1);
|
302
|
+
logTime('Fiber 1 started');
|
290
303
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
304
|
+
// Wait for a time period then interrupt the fiber
|
305
|
+
fiber.timeout(main, 500);
|
306
|
+
await fiber.done(main);
|
307
|
+
|
308
|
+
logTime('Fiber 1 interrupted');
|
309
|
+
}
|
295
310
|
```
|
296
311
|
|
package/channel.d.ts
CHANGED
@@ -1,27 +1,20 @@
|
|
1
1
|
/**
|
2
2
|
* @module Channels
|
3
3
|
*/
|
4
|
-
import type { QueueNode } from './
|
4
|
+
import type { Node as QueueNode } from './queue.js';
|
5
|
+
import { type Lock } from './lock.js';
|
5
6
|
/**
|
6
7
|
* Describe a channel
|
7
8
|
*/
|
8
|
-
export interface Channel<T> {
|
9
|
+
export interface Channel<T = any> extends Lock<T> {
|
9
10
|
/**
|
10
11
|
* The head of the value queue
|
11
12
|
*/
|
12
|
-
|
13
|
+
2: QueueNode<T>;
|
13
14
|
/**
|
14
15
|
* The tail of the value queue
|
15
16
|
*/
|
16
|
-
|
17
|
-
/**
|
18
|
-
* The head of the Promise resolve queue
|
19
|
-
*/
|
20
|
-
2: QueueNode<(value?: T) => void>;
|
21
|
-
/**
|
22
|
-
* The tail of the Promise resolve queue
|
23
|
-
*/
|
24
|
-
3: QueueNode<(value?: T) => void>;
|
17
|
+
3: QueueNode<T>;
|
25
18
|
}
|
26
19
|
/**
|
27
20
|
* Create a channel
|
@@ -43,8 +36,4 @@ export declare const recieve: <T>(c: Channel<T>) => Promise<T | undefined>;
|
|
43
36
|
* @param c
|
44
37
|
*/
|
45
38
|
export declare const poll: <T>(c: Channel<T>) => T | undefined;
|
46
|
-
|
47
|
-
* Resolves all pending promises of a channel
|
48
|
-
* @param c
|
49
|
-
*/
|
50
|
-
export declare const flush: <T>(c: Channel<T>) => void;
|
39
|
+
export { flush } from './lock.js';
|
package/channel.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let init=()=>{let
|
1
|
+
import{acquire as lockAcquire,release as lockRelease,released as lockReleased}from"./lock.js";export let init=()=>{let resolveQu=[null];let qu=[null];return[resolveQu,resolveQu,qu,qu]};export let send=(c,t)=>{if(lockReleased(c))c[2]=c[2][0]=[null,t];else lockRelease(c,t)};export let recieve=(c)=>c[3][0]!==null?Promise.resolve((c[3]=c[3][0])[1]):lockAcquire(c);export let poll=(c)=>c[3][0]!==null?(c[3]=c[3][0])[1]:undefined;export{flush}from"./lock.js";
|
package/dropping-queue.d.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
/**
|
2
2
|
* @module Dropping queues
|
3
3
|
*/
|
4
|
-
import type {
|
5
|
-
export { init } from './
|
4
|
+
import type { Fixed } from './queue.js';
|
5
|
+
export { fixed as init } from './queue.js';
|
6
6
|
export { pop } from './sliding-queue.js';
|
7
7
|
/**
|
8
8
|
* Push an item to a dropping queue
|
9
9
|
* @param q - The queue to push to
|
10
10
|
* @param item
|
11
11
|
*/
|
12
|
-
export declare const push: <T extends {}>(q:
|
12
|
+
export declare const push: <T extends {}>(q: Fixed<T>, item: T) => boolean;
|
package/dropping-queue.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export{init}from"./
|
1
|
+
export{fixed as init}from"./queue.js";export{pop}from"./sliding-queue.js";export let push=(q,item)=>{if(q[0][(q[2]+1)%q[1]]!=null)return false;q[0][q[2]=(q[2]+1)%q[1]]=item;return true};
|
package/fiber.d.ts
CHANGED
@@ -41,7 +41,7 @@ export declare const completed: (t: Process) => boolean;
|
|
41
41
|
/**
|
42
42
|
* Check whether the fiber has been interrupted
|
43
43
|
*/
|
44
|
-
export declare const
|
44
|
+
export declare const interrupted: (t: Process) => boolean;
|
45
45
|
/**
|
46
46
|
* Create a fiber function
|
47
47
|
* @param f
|
@@ -63,10 +63,16 @@ export declare const pause: (t: Process) => void;
|
|
63
63
|
*/
|
64
64
|
export declare const resume: (t: Process) => void;
|
65
65
|
/**
|
66
|
-
*
|
66
|
+
* Interrupt the execution of a fiber
|
67
67
|
* @param t
|
68
68
|
*/
|
69
|
-
export declare const
|
69
|
+
export declare const interrupt: (t: Process) => void;
|
70
|
+
/**
|
71
|
+
* Timeout a fiber
|
72
|
+
* @param t
|
73
|
+
* @param ms
|
74
|
+
*/
|
75
|
+
export declare const timeout: (t: Process, ms: number) => Promise<void>;
|
70
76
|
/**
|
71
77
|
* Wait for a fiber and retrieve its result
|
72
78
|
* @param t
|
package/fiber.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let completed=(t)=>t[1]===2;export let
|
1
|
+
import{sleep}from"./index.js";export let paused=(t)=>t[1]===0;export let resumed=(t)=>t[1]===1;export let completed=(t)=>t[1]===2;export let interrupted=(t)=>t[1]===3;let invoke=async(g,thread)=>{await 0;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(interrupt)}};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 interrupt=(t)=>{if(t[1]!==2){if(t[1]===0)t[2]?.();t[1]=3}};export let timeout=async(t,ms)=>{await sleep(ms);interrupt(t)};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",()=>{interrupt(t)})};export function*unwrap(t){return yield t}
|
package/index.d.ts
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
* @module Other utilities
|
3
3
|
*/
|
4
4
|
/**
|
5
|
-
*
|
5
|
+
* Continue the execution on next event loop cycle.
|
6
6
|
*
|
7
7
|
* You can `await` this **occasionally** in an expensive synchronous operation to avoid
|
8
8
|
*
|
@@ -43,7 +43,6 @@ export declare const spawn: <const T extends any[], const R>(n: number, task: (.
|
|
43
43
|
export declare const debounce: <const Args extends any[]>(f: (...args: Args) => any, ms: number) => ((...args: Args) => void);
|
44
44
|
/**
|
45
45
|
* Throttle function execution for a time period
|
46
|
-
* @param f - The function to throttle (it must not throw errors)
|
47
46
|
* @param ms - The time in milliseconds
|
48
47
|
* @param limit - The call limit in the time period
|
49
48
|
*/
|
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 throttle=(ms,limit)=>{let head=[null];let tail=head;let cur=limit;let scheduled=false;let unlock=()=>{cur=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 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=(ms,limit)=>{let head=[null];let tail=head;let cur=limit;let scheduled=false;let unlock=()=>{cur=limit;if(tail===head){scheduled=false;return}do{cur--;(tail=tail[0])[1]()}while(cur>0&&tail!==head);setTimeout(unlock,ms)};return()=>{if(cur===0){return new Promise((res)=>{head=head[0]=[null,res]})}if(!scheduled){scheduled=true;setTimeout(unlock,ms)}cur--;return pause}};
|
package/lock.d.ts
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
import type { Node as QueueNode } from './queue.js';
|
2
|
+
/**
|
3
|
+
* Describe a lock
|
4
|
+
*/
|
5
|
+
export interface Lock<T = any> {
|
6
|
+
/**
|
7
|
+
* The head of the Promise resolve queue
|
8
|
+
*/
|
9
|
+
0: QueueNode<(value?: T) => void>;
|
10
|
+
/**
|
11
|
+
* The tail of the Promise resolve queue
|
12
|
+
*/
|
13
|
+
1: QueueNode<(value?: T) => void>;
|
14
|
+
}
|
15
|
+
/**
|
16
|
+
* Acquire an item
|
17
|
+
* @param lock
|
18
|
+
* @param value
|
19
|
+
*/
|
20
|
+
export declare const acquire: <T>(lock: Lock<T>) => Promise<T | undefined>;
|
21
|
+
/**
|
22
|
+
* Release an item
|
23
|
+
* @param lock
|
24
|
+
* @param value
|
25
|
+
*/
|
26
|
+
export declare const release: <T>(lock: Lock<T>, value?: T) => void;
|
27
|
+
/**
|
28
|
+
* Return true if all items are released
|
29
|
+
* @param lock
|
30
|
+
*/
|
31
|
+
export declare const released: (lock: Lock) => boolean;
|
32
|
+
/**
|
33
|
+
* Release all items of a lock
|
34
|
+
* @param lock
|
35
|
+
*/
|
36
|
+
export declare const flush: (lock: Lock) => void;
|
package/lock.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export let acquire=(lock)=>new Promise((res)=>{lock[0]=lock[0][0]=[null,res]});export let release=(lock,value)=>{(lock[1]=lock[1][0])[1](value)};export let released=(lock)=>lock[1][0]===null;export let flush=(lock)=>{while(lock[1][0]!==null)(lock[1]=lock[1][0])[1]()};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ciorent",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"description": "A lightweight, low-overhead concurrency library",
|
5
5
|
"homepage": "https://ciorent.netlify.app",
|
6
6
|
"repository": {
|
@@ -18,14 +18,15 @@
|
|
18
18
|
"main": "./index.js",
|
19
19
|
"types": "./index.d.ts",
|
20
20
|
"exports": {
|
21
|
-
"./fixed-queue": "./fixed-queue.js",
|
22
|
-
"./fiber": "./fiber.js",
|
23
21
|
"./sliding-queue": "./sliding-queue.js",
|
24
|
-
"./
|
22
|
+
"./latch": "./latch.js",
|
23
|
+
"./lock": "./lock.js",
|
25
24
|
"./dropping-queue": "./dropping-queue.js",
|
25
|
+
"./semaphore": "./semaphore.js",
|
26
26
|
"./channel": "./channel.js",
|
27
|
-
"
|
27
|
+
"./fiber": "./fiber.js",
|
28
|
+
"./queue": "./queue.js",
|
28
29
|
"./topic": "./topic.js",
|
29
|
-
"
|
30
|
+
".": "./index.js"
|
30
31
|
}
|
31
32
|
}
|
@@ -5,7 +5,7 @@
|
|
5
5
|
/**
|
6
6
|
* Describe a fixed-sized queue
|
7
7
|
*/
|
8
|
-
export interface
|
8
|
+
export interface Fixed<T extends {}> {
|
9
9
|
/**
|
10
10
|
* Pre-allocated queue
|
11
11
|
*/
|
@@ -26,9 +26,12 @@ export interface FixedQueue<T extends {}> {
|
|
26
26
|
/**
|
27
27
|
* Describe a queue node (singly linked list node)
|
28
28
|
*/
|
29
|
-
export
|
29
|
+
export interface Node<T> {
|
30
|
+
0: Node<T> | null;
|
31
|
+
1: T;
|
32
|
+
}
|
30
33
|
/**
|
31
|
-
* Create a fixed queue
|
34
|
+
* Create a fixed queue
|
32
35
|
* @param n - The queue size
|
33
36
|
*/
|
34
|
-
export declare const
|
37
|
+
export declare const fixed: <T extends {}>(n: number) => Fixed<T>;
|
package/queue.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export let fixed=(n)=>[new Array(n),n,-1,-1];
|
package/semaphore.d.ts
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
/**
|
2
2
|
* @module Semaphores
|
3
3
|
*/
|
4
|
-
import type
|
4
|
+
import { type Lock } from './lock.js';
|
5
5
|
/**
|
6
6
|
* Describe a semaphore
|
7
7
|
*/
|
8
|
-
export interface Semaphore {
|
8
|
+
export interface Semaphore extends Lock<undefined> {
|
9
9
|
/**
|
10
10
|
* Current remaining process allowed
|
11
11
|
*/
|
12
|
-
|
13
|
-
/**
|
14
|
-
* The head of the Promise resolve queue
|
15
|
-
*/
|
16
|
-
1: QueueNode<() => void>;
|
17
|
-
/**
|
18
|
-
* The tail of the Promise resolve queue
|
19
|
-
*/
|
20
|
-
2: QueueNode<() => void>;
|
12
|
+
2: number;
|
21
13
|
}
|
22
14
|
/**
|
23
15
|
* Create a semaphore that allows n accesses
|
@@ -26,11 +18,11 @@ export declare const init: (n: number) => Semaphore;
|
|
26
18
|
/**
|
27
19
|
* Wait until the semaphore allows access
|
28
20
|
*/
|
29
|
-
export declare const
|
21
|
+
export declare const acquire: (s: Semaphore) => Promise<void>;
|
30
22
|
/**
|
31
23
|
* Signal to the semaphore to release access
|
32
24
|
*/
|
33
|
-
export declare const
|
25
|
+
export declare const release: (s: Semaphore) => void;
|
34
26
|
/**
|
35
27
|
* Bind a task to a semaphore
|
36
28
|
*/
|
package/semaphore.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{pause as resolvedPromise}from"./index.js";export let init=(n)=>{let root=[null];return[
|
1
|
+
import{pause as resolvedPromise}from"./index.js";import{acquire as lockAcquire,release as lockRelease}from"./lock.js";export let init=(n)=>{let root=[null];return[root,root,n]};export let acquire=(s)=>{s[2]--;return s[2]>=0?resolvedPromise:lockAcquire(s)};export let release=(s)=>{if(s[2]<0)lockRelease(s);s[2]++};export let bind=(f,s)=>async(...a)=>{s[2]--;if(s[2]<0)await acquire(s);try{return await f(...a)}finally{if(s[2]<0)release(s);s[2]++}};
|
package/sliding-queue.d.ts
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
/**
|
2
2
|
* @module Sliding queues
|
3
3
|
*/
|
4
|
-
import type {
|
5
|
-
export { init } from './
|
4
|
+
import type { Fixed } from './queue.js';
|
5
|
+
export { fixed as init } from './queue.js';
|
6
6
|
/**
|
7
7
|
* Push an item to a sliding queue
|
8
8
|
* @param q - The queue to push to
|
9
9
|
* @param item
|
10
10
|
*/
|
11
|
-
export declare const push: <T extends {}>(q:
|
11
|
+
export declare const push: <T extends {}>(q: Fixed<T>, item: T) => void;
|
12
12
|
/**
|
13
13
|
* Pop an item from the queue
|
14
14
|
* @param q - The queue to pop from
|
15
15
|
*/
|
16
|
-
export declare const pop: <T extends {}>(q:
|
16
|
+
export declare const pop: <T extends {}>(q: Fixed<T>) => T | undefined;
|
package/sliding-queue.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export{init}from"./
|
1
|
+
export{fixed as init}from"./queue.js";export let push=(q,item)=>{q[0][q[2]=(q[2]+1)%q[1]]=item};export let pop=(q)=>{let val=q[0][(q[3]+1)%q[1]];if(val!=null){q[0][q[3]=(q[3]+1)%q[1]]=null;return val}};
|
package/topic.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* @module Pubsub
|
3
3
|
*/
|
4
|
-
import type { QueueNode } from './
|
4
|
+
import type { Node as QueueNode } from './queue.js';
|
5
5
|
/**
|
6
6
|
* Describe a topic
|
7
7
|
*/
|
@@ -13,11 +13,7 @@ export interface Topic<T extends {}> {
|
|
13
13
|
/**
|
14
14
|
* The waiting subscriber resolves
|
15
15
|
*/
|
16
|
-
1: ((res
|
17
|
-
/**
|
18
|
-
* The waiting subscribers
|
19
|
-
*/
|
20
|
-
2: Subscriber<T>[];
|
16
|
+
1: ((res: QueueNode<T>) => void)[];
|
21
17
|
}
|
22
18
|
/**
|
23
19
|
* Create a topic
|
package/topic.js
CHANGED
@@ -1 +1 @@
|
|
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]
|
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];i<res.length;i++)res[i](head);t[1]=[]};export let flush=(t)=>{let head=t[0]=t[0][0]=[null,undefined];for(let i=0,res=t[1];i<res.length;i++)res[i](head);t[1]=[]};export let poll=(t)=>t[1][0]!==null?(t[1]=t[1][0])[1]:undefined;export let recieve=async(t)=>t[1][0]!==null?(t[1]=t[1][0])[1]:(t[1]=await new Promise((res)=>{t[0][1].push(res)}))[1];
|
package/fixed-queue.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export let init=(n)=>[new Array(n),n,-1,-1];
|