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 CHANGED
@@ -7,91 +7,103 @@ A lightweight, low-overhead concurrency library.
7
7
  - Fully type-safe.
8
8
 
9
9
  ## Examples
10
- ### Semaphore
11
- Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
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
- // Acquire the semaphore or wait for the semaphore to be available
22
- await semaphore.pause(sem);
17
+ await co.sleep((10 - id) * 20 + 50);
18
+ console.log('Task', id, 'done');
19
+ }
23
20
 
24
- console.log('Task', id, 'started');
21
+ // Spawn and run 5 tasks sequentially
22
+ console.log('Running 5 tasks sequentially:');
23
+ await co.sequential(5, task);
25
24
 
26
- // Let the main thread schedules other tasks
27
- for (let i = 1; i <= 5; i++) await co.pause;
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
- console.log('Task', id, 'end');
30
+ #### Sleep
31
+ Cross-runtime synchronous and asynchronous sleep functions.
32
+ ```ts
33
+ import * as co from 'ciorent';
30
34
 
31
- // Release the semaphore
32
- semaphore.signal(sem);
33
- }
35
+ const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
34
36
 
35
- // Try to run 5 tasks concurrently
36
- co.spawn(5, task);
37
- ```
37
+ logTime('Start');
38
38
 
39
- ### Fibers
40
- Virtual threads with more controlled execution.
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
- // Wait for a promise and return its result
51
- const res = yield* fiber.unwrap(Promise.resolve(1));
52
- console.log('Fiber 1 recieved:', res);
54
+ const fn = co.debounce((id: number) => {
55
+ console.log('ID:', id);
56
+ }, 500);
53
57
 
54
- return Math.random();
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
- const main = fiber.spawn(function* (proc) {
59
- // Start f1, wait for it to finish and get the result
60
- const res = yield* fiber.join(fiber.spawn(f1));
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
- // Start f1 and make its lifetime depends on current fiber
64
- const childProc = fiber.spawn(f1);
65
- fiber.mount(childProc, proc);
66
- });
68
+ // Allow 2 calls in 500ms
69
+ const throttle = co.throttle(500, 2);
67
70
 
68
- console.log('Fiber 2 started:', fiber.resumed(main));
71
+ co.spawn(8, async (id) => {
72
+ await throttle();
73
+ console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
74
+ });
75
+ ```
69
76
 
70
- // Pause the current fiber process
71
- fiber.pause(main);
72
- console.log('Fiber 2 is paused:', fiber.paused(main));
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
- // Resume the fiber
75
- fiber.resume(main);
76
- console.log('Fiber 2 is resumed:', fiber.resumed(main));
82
+ // Expensive sync task
83
+ const task1 = async () => {
84
+ let x = 0;
77
85
 
78
- // Wait for the fiber process to finish
79
- await fiber.done(main);
86
+ // Yield control back to the runtime, allowing it to
87
+ // schedule other tasks
88
+ await co.pause;
80
89
 
81
- // Check finish status
82
- console.log('Fiber 2 completed', fiber.completed(main));
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
- console.log('------------------------');
94
+ console.log('Finish task 1:', x);
95
+ };
87
96
 
88
- const main = fiber.spawn(f1);
89
- console.log('Fiber 1 started:', fiber.resumed(main));
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
- // Stop a fiber
92
- fiber.stop(main);
93
- console.log('Fiber 1 stopped:', fiber.stopped(main));
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
- ### Utilities
199
- #### Pausing
200
- Delay the execution of a function for other asynchronous tasks to run.
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
- // Expensive sync task
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
- // Task 2 will not get blocked by task 1
227
- task1();
228
- task2();
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
- #### Sleep
232
- Cross-runtime synchronous and asynchronous sleep functions.
233
- ```ts
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
- const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
257
+ return Math.random();
258
+ });
237
259
 
238
- logTime('Start');
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
- // Non-blocking
241
- await co.sleep(500);
242
- logTime('After about 0.5s');
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
- // This blocks the event loop
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
- #### Spawning tasks
251
- Utilities to create and run tasks.
252
- ```ts
253
- import * as co from 'ciorent';
276
+ // Wait for the fiber process to finish
277
+ await fiber.done(main);
254
278
 
255
- const task = async (id: number) => {
256
- await co.sleep((10 - id) * 20 + 50);
257
- console.log('Task', id, 'done');
279
+ // Check finish status
280
+ console.log('Fiber 2 completed:', fiber.completed(main));
258
281
  }
259
282
 
260
- // Spawn and run 5 tasks sequentially
261
- console.log('Running 5 tasks sequentially:');
262
- await co.sequential(5, task);
283
+ {
284
+ console.log('------------------------');
263
285
 
264
- // Spawn and run 5 tasks concurrently
265
- console.log('Running 5 tasks concurrently:');
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
- #### Debounce
270
- Postpones execution until after an idle period.
271
- ```ts
272
- import * as co from 'ciorent';
289
+ // Interrupt a fiber
290
+ fiber.interrupt(main);
273
291
 
274
- const fn = co.debounce((id: number) => {
275
- console.log('ID:', id);
276
- }, 500);
292
+ // Execution will be stopped on the last yield
293
+ await fiber.done(main);
277
294
 
278
- fn(1); // fn(1) gets skipped
279
- await co.sleep(100);
280
- fn(2); // fn(2) gets executed
281
- ```
295
+ console.log('Fiber 1 interrupted:', fiber.interrupted(main));
296
+ }
282
297
 
283
- #### Throttle
284
- Executes a function at a regular interval.
285
- ```ts
286
- import * as co from 'ciorent';
298
+ {
299
+ console.log('------------------------');
287
300
 
288
- // Allow 2 calls in 500ms
289
- const throttle = co.throttle(500, 2);
301
+ const main = fiber.spawn(f1);
302
+ logTime('Fiber 1 started');
290
303
 
291
- co.spawn(8, async (id) => {
292
- await throttle();
293
- console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
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 './fixed-queue.js';
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
- 0: QueueNode<T>;
13
+ 2: QueueNode<T>;
13
14
  /**
14
15
  * The tail of the value queue
15
16
  */
16
- 1: QueueNode<T>;
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 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]()};
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";
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @module Dropping queues
3
3
  */
4
- import type { FixedQueue } from './fixed-queue.js';
5
- export { init } from './fixed-queue.js';
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: FixedQueue<T>, item: T) => boolean;
12
+ export declare const push: <T extends {}>(q: Fixed<T>, item: T) => boolean;
package/dropping-queue.js CHANGED
@@ -1 +1 @@
1
- export{init}from"./fixed-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};
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 stopped: (t: Process) => boolean;
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
- * Stop the execution of a fiber
66
+ * Interrupt the execution of a fiber
67
67
  * @param t
68
68
  */
69
- export declare const stop: (t: Process) => void;
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 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}
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
- * Yield back to main thread.
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;while(cur>0){if(tail===head){scheduled=false;return}cur--;(tail=tail[0])[1]()}setTimeout(unlock,ms)};return()=>{if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r];return p}if(!scheduled){scheduled=true;setTimeout(unlock,ms)}cur--;return pause}};
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.2.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
- "./semaphore": "./semaphore.js",
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
- ".": "./index.js",
27
+ "./fiber": "./fiber.js",
28
+ "./queue": "./queue.js",
28
29
  "./topic": "./topic.js",
29
- "./latch": "./latch.js"
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 FixedQueue<T extends {}> {
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 type QueueNode<T> = [next: QueueNode<T> | null, value: T];
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 init: <T extends {}>(n: number) => FixedQueue<T>;
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 { QueueNode } from './fixed-queue.js';
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
- 0: number;
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 pause: (s: Semaphore) => Promise<void>;
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 signal: (s: Semaphore) => void;
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[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]++}};
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]++}};
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * @module Sliding queues
3
3
  */
4
- import type { FixedQueue } from './fixed-queue.js';
5
- export { init } from './fixed-queue.js';
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: FixedQueue<T>, item: T) => void;
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: FixedQueue<T>) => T | undefined;
16
+ export declare const pop: <T extends {}>(q: Fixed<T>) => T | undefined;
package/sliding-queue.js CHANGED
@@ -1 +1 @@
1
- export{init}from"./fixed-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}};
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 './fixed-queue.js';
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?: T) => void)[];
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],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)})};
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];