ciorent 0.1.2 → 0.1.4

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
@@ -4,7 +4,7 @@ Semaphore is a concurrency primitive used to control access to a common resource
4
4
 
5
5
  ```ts
6
6
  import * as semaphore from 'ciorent/semaphore';
7
- import * as cio from 'ciorent';
7
+ import * as co from 'ciorent';
8
8
 
9
9
  // Only allow 2 task to run concurrently
10
10
  const sem = semaphore.init(2);
@@ -16,7 +16,7 @@ const task = async (id: number) => {
16
16
  console.log('Task', id, 'started');
17
17
 
18
18
  // Let the main thread schedules other tasks
19
- for (let i = 1; i <= 5; i++) await cio.pause;
19
+ for (let i = 1; i <= 5; i++) await co.pause;
20
20
 
21
21
  console.log('Task', id, 'end');
22
22
 
@@ -25,41 +25,39 @@ const task = async (id: number) => {
25
25
  }
26
26
 
27
27
  // Try to run 5 tasks concurrently
28
- cio.concurrent(5, task);
28
+ co.spawn(5, task);
29
29
  ```
30
30
 
31
31
  ## Fibers
32
32
  Virtual threads with more controlled execution.
33
33
 
34
34
  ```ts
35
- import * as cio from 'ciorent';
35
+ import * as co from 'ciorent';
36
36
  import * as fiber from 'ciorent/fiber';
37
37
 
38
- const thread1 = fiber.fn(function* () {
38
+ const f1 = fiber.fn(function* () {
39
39
  console.log('Fiber 1 started');
40
40
 
41
- // Thread1 will be interrupted by thread2
42
- // As thread2 will end first
43
- yield cio.sleep(1000);
41
+ // Wait for a promise
42
+ yield co.sleep(1000);
44
43
 
45
44
  console.log('Fiber 1 done');
45
+ return Math.random();
46
46
  });
47
47
 
48
- const thread2 = fiber.fn(function* (thread) {
48
+ fiber.spawn(function* (proc) {
49
49
  console.log('Fiber 2 started');
50
50
 
51
- yield;
52
- console.log('Fiber 2 resumed');
51
+ // Start f1, wait for it to finish and get the result
52
+ const res = yield* fiber.join(fiber.spawn(f1));
53
+ console.log('Fiber 1 result:', res);
53
54
 
54
- // Start thread 1 and make thread1
55
- // lifetime depends on thread2
56
- fiber.mount(fiber.spawn(thread1), thread);
55
+ // Start f1 and make its lifetime depends on current fiber
56
+ fiber.mount(fiber.spawn(f1), proc);
57
57
 
58
+ // The runtime will interrupt f1
58
59
  console.log('Fiber 2 done');
59
60
  });
60
-
61
- // Start running the thread
62
- fiber.spawn(thread2);
63
61
  ```
64
62
 
65
63
  ## Latch
@@ -94,15 +92,15 @@ A fast, simple publish-subscribe API.
94
92
 
95
93
  ```ts
96
94
  import * as topic from 'ciorent/topic';
97
- import * as cio from 'ciorent';
95
+ import * as co from 'ciorent';
98
96
 
99
97
  const messages = topic.init<number>();
100
98
 
101
99
  // A task that publish messages
102
100
  const publisher = async () => {
103
101
  for (let i = 0; i < 3; i++) {
104
- await cio.sleep(100);
105
- topic.pub(messages, i);
102
+ await co.sleep(100);
103
+ topic.publish(messages, i);
106
104
  }
107
105
 
108
106
  // Resolve all waiting promises
@@ -111,8 +109,8 @@ const publisher = async () => {
111
109
  }
112
110
 
113
111
  // Spawn 3 tasks that recieve messages
114
- cio.concurrent(3, async (id: number) => {
115
- const sub = topic.sub(messages);
112
+ co.spawn(3, async (id: number) => {
113
+ const sub = topic.subscribe(messages);
116
114
 
117
115
  while (true) {
118
116
  // Block until the value is sent
@@ -130,13 +128,13 @@ Channel is a synchronization primitive via message passing. A message may be sen
130
128
 
131
129
  ```ts
132
130
  import * as channel from 'ciorent/channel';
133
- import * as cio from 'ciorent';
131
+ import * as co from 'ciorent';
134
132
 
135
133
  const c = channel.init<number>();
136
134
 
137
135
  const run = async () => {
138
136
  for (let i = 0; i < 5; i++) {
139
- await cio.sleep(100);
137
+ await co.sleep(100);
140
138
  channel.send(c, i);
141
139
  console.log('Sent', i);
142
140
  }
@@ -167,7 +165,7 @@ console.log('Starting...');
167
165
  ### Pausing
168
166
  Delay the execution of a function for other asynchronous tasks to run.
169
167
  ```ts
170
- import * as cio from 'ciorent';
168
+ import * as co from 'ciorent';
171
169
 
172
170
  // Expensive sync task
173
171
  const task1 = async () => {
@@ -175,7 +173,7 @@ const task1 = async () => {
175
173
 
176
174
  // Yield control back to the runtime, allowing it to
177
175
  // schedule other tasks
178
- await cio.pause;
176
+ await co.pause;
179
177
 
180
178
  // Simulate heavy operation
181
179
  for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
@@ -199,82 +197,82 @@ task2();
199
197
  ### Sleep
200
198
  Cross-runtime synchronous and asynchronous sleep functions.
201
199
  ```ts
202
- import * as cio from 'ciorent';
200
+ import * as co from 'ciorent';
203
201
 
204
202
  const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
205
203
 
206
204
  logTime('Start');
207
205
 
208
206
  // Non-blocking
209
- await cio.sleep(500);
207
+ await co.sleep(500);
210
208
  logTime('After about 0.5s');
211
209
 
212
210
  // This blocks the event loop
213
211
  // On the browser this only works in workers and blocks the worker thread
214
- cio.sleepSync(500);
212
+ co.sleepSync(500);
215
213
  logTime('After another 0.5s');
216
214
  ```
217
215
 
218
216
  ### Spawning tasks
219
217
  Utilities to create and run tasks.
220
218
  ```ts
221
- import * as cio from 'ciorent';
219
+ import * as co from 'ciorent';
222
220
 
223
221
  const task = async (id: number) => {
224
- await cio.sleep((10 - id) * 20 + 50);
222
+ await co.sleep((10 - id) * 20 + 50);
225
223
  console.log('Task', id, 'done');
226
224
  }
227
225
 
228
226
  // Spawn and run 5 tasks sequentially
229
227
  console.log('Running 5 tasks sequentially:');
230
- await cio.sequential(5, task);
228
+ await co.sequential(5, task);
231
229
 
232
230
  // Spawn and run 5 tasks concurrently
233
231
  console.log('Running 5 tasks concurrently:');
234
- await cio.concurrent(5, task);
232
+ await Promise.all(co.spawn(5, task));
235
233
  ```
236
234
 
237
235
  ### Debounce
238
236
  Postpones execution until after an idle period.
239
237
  ```ts
240
- import * as cio from 'ciorent';
238
+ import * as co from 'ciorent';
241
239
 
242
- const fn = cio.debounce((id: number) => {
240
+ const fn = co.debounce((id: number) => {
243
241
  console.log('ID:', id);
244
242
  }, 500);
245
243
 
246
244
  fn(1); // fn(1) gets skipped
247
- await cio.sleep(100);
245
+ await co.sleep(100);
248
246
  fn(2); // fn(2) gets executed
249
247
  ```
250
248
 
251
249
  ### Rate Limit
252
250
  Limits the number of calls within a time window.
253
251
  ```ts
254
- import * as cio from 'ciorent';
252
+ import * as co from 'ciorent';
255
253
 
256
254
  // Allow 2 calls in 500ms, other calls are dropped
257
- const fn = cio.rateLimit((id: number) => {
255
+ const fn = co.rateLimit((id: number) => {
258
256
  console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
259
257
  }, 500, 2);
260
258
 
261
259
  // Some calls will be dropped
262
260
  for (let i = 0; i < 8; i++) {
263
261
  fn(i);
264
- await cio.sleep(400);
262
+ await co.sleep(400);
265
263
  }
266
264
  ```
267
265
 
268
266
  ### Throttle
269
267
  Executes a function at a regular interval.
270
268
  ```ts
271
- import * as cio from 'ciorent';
269
+ import * as co from 'ciorent';
272
270
 
273
271
  // Allow 2 calls in 500ms
274
- const fn = cio.throttle((id: number) => {
272
+ const fn = co.throttle((id: number) => {
275
273
  console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
276
274
  }, 500, 2);
277
275
 
278
- cio.concurrent(8, fn);
276
+ co.spawn(8, fn);
279
277
  ```
280
278
 
package/fiber.d.ts CHANGED
@@ -2,47 +2,51 @@
2
2
  * @module Fibers
3
3
  */
4
4
  /**
5
- * Check whether the fiber is paused
5
+ * Describe a fiber process
6
6
  */
7
- export declare const paused: (t: Thread) => boolean;
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<T | TReturn>;
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 threads
21
+ * Bounded fibers
34
22
  */
35
- 3: Thread[];
23
+ 3: Process[];
36
24
  }
37
25
  /**
38
26
  * Describe a fiber runtime
39
27
  */
40
- export type Runtime = <const T, const TReturn, const Args extends any[]>(gen: (thread: Thread<T, TReturn>, ...args: Args) => Generator<T, TReturn>, ...args: Args) => Thread<T, TReturn>;
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 has been paused
31
+ */
32
+ export declare const paused: (t: Process) => boolean;
33
+ /**
34
+ * Check whether the fiber is running
35
+ */
36
+ export declare const resumed: (t: Process) => boolean;
37
+ /**
38
+ * Check whether the fiber has finished
39
+ */
40
+ export declare const done: (t: Process) => boolean;
41
+ /**
42
+ * Check whether the fiber has been 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: Thread, ...args: any[]) => Generator>(f: Fn) => Fn;
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: Thread) => void;
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: Thread) => void;
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: Thread) => void;
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 Thread>(t: T): Generator<Awaited<T[1]>, Awaited<T[1]>>;
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 Thread>(t: T) => T[1];
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: Thread, parent: Thread) => void;
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: Thread, signal: AbortSignal) => void;
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]===2)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}
1
+ export let paused=(t)=>t[1]===0;export let resumed=(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/index.d.ts CHANGED
@@ -30,11 +30,11 @@ export declare const sleepSync: (ms: number) => void;
30
30
  */
31
31
  export declare const sequential: <const T extends any[]>(n: number, task: (...args: [...T, id: number]) => Promise<any>, ...args: T) => Promise<void>;
32
32
  /**
33
- * Spawn n tasks that runs concurrently
33
+ * Spawn n concurrent tasks
34
34
  * @param n
35
35
  * @param task - The function to run
36
36
  */
37
- export declare const concurrent: <const T extends any[], const R>(n: number, task: (...args: [...T, id: number]) => Promise<R>, ...args: T) => Promise<R[]>;
37
+ export declare const spawn: <const T extends any[], const R>(n: number, task: (...args: [...T, id: number]) => Promise<R>, ...args: T) => Promise<R>[];
38
38
  /**
39
39
  * Drop function calls until it doesn't get called for a specific period.
40
40
  * @param f - The target function to debounce (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 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 cur=limit;let unlock=()=>{cur=limit};return(...a)=>{if(cur>0){if(cur===1)setTimeout(unlock,ms);cur--;f(...a)}}};export let throttle=(f,ms,limit)=>{let head=[null];let tail=head;let cur=limit;let unlock=()=>{cur=limit;while(cur>0){if(tail===head)return;cur--;tail=tail[0];tail[1](f(...tail[2]))}setTimeout(unlock,ms)};return(...a)=>{if(cur===1){setTimeout(unlock,ms)}else if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r,a];return p}cur--;return f(...a)}};
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 rateLimit=(f,ms,limit)=>{let cur=limit;let unlock=()=>{cur=limit};return(...a)=>{if(cur>0){if(cur===1)setTimeout(unlock,ms);cur--;f(...a)}}};export let throttle=(f,ms,limit)=>{let head=[null];let tail=head;let cur=limit;let unlock=()=>{cur=limit;while(cur>0){if(tail===head)return;cur--;tail=tail[0];tail[1](f(...tail[2]))}setTimeout(unlock,ms)};return(...a)=>{if(cur===1){setTimeout(unlock,ms)}else if(cur===0){let r;let p=new Promise((res)=>{r=res});head=head[0]=[null,r,a];return p}cur--;return f(...a)}};
package/latch.d.ts CHANGED
@@ -18,6 +18,6 @@ export declare const pause: (latch: Latch) => Promise<void>;
18
18
  */
19
19
  export declare const open: (latch: Latch) => void;
20
20
  /**
21
- * Re-close a latch
21
+ * Close a latch
22
22
  */
23
- export declare const reset: (latch: Latch) => void;
23
+ export declare const close: (latch: Latch) => void;
package/latch.js CHANGED
@@ -1 +1 @@
1
- import{pause as endPromise}from"./index.js";export let init=()=>{let r;return[new Promise((res)=>{r=res}),r]};export let pause=(latch)=>latch[0];export let open=(latch)=>{latch[1]();latch[0]=endPromise};export let reset=(latch)=>{if(latch[0]===endPromise){let r;latch[0]=new Promise((res)=>{r=res});latch[1]=r}};
1
+ import{pause as endPromise}from"./index.js";export let init=()=>{let r;return[new Promise((res)=>{r=res}),r]};export let pause=(latch)=>latch[0];export let open=(latch)=>{latch[1]();latch[0]=endPromise};export let close=(latch)=>{if(latch[0]===endPromise){let r;latch[0]=new Promise((res)=>{r=res});latch[1]=r}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ciorent",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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
- "./sliding-queue": "./sliding-queue.js",
23
22
  "./dropping-queue": "./dropping-queue.js",
24
- "./fiber": "./fiber.js",
25
- "./channel": "./channel.js",
23
+ "./sliding-queue": "./sliding-queue.js",
26
24
  ".": "./index.js",
27
25
  "./topic": "./topic.js",
28
26
  "./semaphore": "./semaphore.js",
27
+ "./channel": "./channel.js",
28
+ "./fiber": "./fiber.js",
29
29
  "./latch": "./latch.js"
30
30
  }
31
31
  }
package/topic.d.ts CHANGED
@@ -34,12 +34,12 @@ export interface Subscriber<T extends {}> {
34
34
  * Subscribe to a topic
35
35
  * @param t
36
36
  */
37
- export declare const sub: <T extends {}>(t: Topic<T>) => Subscriber<T>;
37
+ export declare const subscribe: <T extends {}>(t: Topic<T>) => Subscriber<T>;
38
38
  /**
39
- * Subscribe to a topic
39
+ * Publish to a topic
40
40
  * @param t
41
41
  */
42
- export declare const pub: <T extends {}>(t: Topic<T>, value: T) => void;
42
+ export declare const publish: <T extends {}>(t: Topic<T>, value: T) => void;
43
43
  /**
44
44
  * Resolve all waiting promises and clear all pending values
45
45
  * @param t
package/topic.js CHANGED
@@ -1 +1 @@
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)})};
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)})};