ciorent 0.1.3 → 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,7 +25,66 @@ 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
+ ```
30
+
31
+ ## Fibers
32
+ Virtual threads with more controlled execution.
33
+
34
+ ```ts
35
+ import * as co from 'ciorent';
36
+ import * as fiber from 'ciorent/fiber';
37
+
38
+ const f1 = fiber.fn(function* () {
39
+ console.log('Fiber 1 started');
40
+
41
+ // Wait for a promise
42
+ yield co.sleep(1000);
43
+
44
+ console.log('Fiber 1 done');
45
+ return Math.random();
46
+ });
47
+
48
+ fiber.spawn(function* (proc) {
49
+ console.log('Fiber 2 started');
50
+
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);
54
+
55
+ // Start f1 and make its lifetime depends on current fiber
56
+ fiber.mount(fiber.spawn(f1), proc);
57
+
58
+ // The runtime will interrupt f1
59
+ console.log('Fiber 2 done');
60
+ });
61
+ ```
62
+
63
+ ## Latch
64
+ Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
65
+
66
+ ```ts
67
+ import * as latch from 'ciorent/latch';
68
+
69
+ const startFetch = latch.init();
70
+
71
+ const task = async () => {
72
+ // Blocks until the latch is open
73
+ await latch.pause(startFetch);
74
+
75
+ console.log('Start fetching...');
76
+ const res = await fetch('http://example.com');
77
+ console.log('Fetch status:', res.status);
78
+ }
79
+
80
+ const prepare = () => {
81
+ // This always run first
82
+ console.log('Run before fetch:', performance.now().toFixed(2));
83
+ latch.open(startFetch);
84
+ }
85
+
86
+ task();
87
+ prepare();
29
88
  ```
30
89
 
31
90
  ## Pubsub
@@ -33,15 +92,15 @@ A fast, simple publish-subscribe API.
33
92
 
34
93
  ```ts
35
94
  import * as topic from 'ciorent/topic';
36
- import * as cio from 'ciorent';
95
+ import * as co from 'ciorent';
37
96
 
38
97
  const messages = topic.init<number>();
39
98
 
40
99
  // A task that publish messages
41
100
  const publisher = async () => {
42
101
  for (let i = 0; i < 3; i++) {
43
- await cio.sleep(100);
44
- topic.pub(messages, i);
102
+ await co.sleep(100);
103
+ topic.publish(messages, i);
45
104
  }
46
105
 
47
106
  // Resolve all waiting promises
@@ -50,8 +109,8 @@ const publisher = async () => {
50
109
  }
51
110
 
52
111
  // Spawn 3 tasks that recieve messages
53
- cio.concurrent(3, async (id: number) => {
54
- const sub = topic.sub(messages);
112
+ co.spawn(3, async (id: number) => {
113
+ const sub = topic.subscribe(messages);
55
114
 
56
115
  while (true) {
57
116
  // Block until the value is sent
@@ -69,13 +128,13 @@ Channel is a synchronization primitive via message passing. A message may be sen
69
128
 
70
129
  ```ts
71
130
  import * as channel from 'ciorent/channel';
72
- import * as cio from 'ciorent';
131
+ import * as co from 'ciorent';
73
132
 
74
133
  const c = channel.init<number>();
75
134
 
76
135
  const run = async () => {
77
136
  for (let i = 0; i < 5; i++) {
78
- await cio.sleep(100);
137
+ await co.sleep(100);
79
138
  channel.send(c, i);
80
139
  console.log('Sent', i);
81
140
  }
@@ -102,70 +161,11 @@ run();
102
161
  console.log('Starting...');
103
162
  ```
104
163
 
105
- ## Latch
106
- Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
107
-
108
- ```ts
109
- import * as latch from 'ciorent/latch';
110
-
111
- const startFetch = latch.init();
112
-
113
- const task = async () => {
114
- // Blocks until the latch is open
115
- await latch.pause(startFetch);
116
-
117
- console.log('Start fetching...');
118
- const res = await fetch('http://example.com');
119
- console.log('Fetch status:', res.status);
120
- }
121
-
122
- const prepare = () => {
123
- // This always run first
124
- console.log('Run before fetch:', performance.now().toFixed(2));
125
- latch.open(startFetch);
126
- }
127
-
128
- task();
129
- prepare();
130
- ```
131
-
132
- ## Fibers
133
- Virtual threads with more controlled execution.
134
-
135
- ```ts
136
- import * as cio from 'ciorent';
137
- import * as fiber from 'ciorent/fiber';
138
-
139
- const f1 = fiber.fn(function* () {
140
- console.log('Fiber 1 started');
141
-
142
- // Wait for a promise
143
- yield cio.sleep(1000);
144
-
145
- console.log('Fiber 1 done');
146
- return Math.random();
147
- });
148
-
149
- fiber.spawn(function* (proc) {
150
- console.log('Fiber 2 started');
151
-
152
- // Start f1, wait for it to finish and get the result
153
- const res = yield* fiber.join(fiber.spawn(f1));
154
- console.log('Fiber 1 result:', res);
155
-
156
- // Start f1 and make its lifetime depends on current fiber
157
- fiber.mount(fiber.spawn(f1), proc);
158
-
159
- // The runtime will interrupt f1
160
- console.log('Fiber 2 done');
161
- });
162
- ```
163
-
164
164
  ## Utilities
165
165
  ### Pausing
166
166
  Delay the execution of a function for other asynchronous tasks to run.
167
167
  ```ts
168
- import * as cio from 'ciorent';
168
+ import * as co from 'ciorent';
169
169
 
170
170
  // Expensive sync task
171
171
  const task1 = async () => {
@@ -173,7 +173,7 @@ const task1 = async () => {
173
173
 
174
174
  // Yield control back to the runtime, allowing it to
175
175
  // schedule other tasks
176
- await cio.pause;
176
+ await co.pause;
177
177
 
178
178
  // Simulate heavy operation
179
179
  for (let i = 0; i < (Math.random() + 15) * 1e6; i++)
@@ -197,82 +197,82 @@ task2();
197
197
  ### Sleep
198
198
  Cross-runtime synchronous and asynchronous sleep functions.
199
199
  ```ts
200
- import * as cio from 'ciorent';
200
+ import * as co from 'ciorent';
201
201
 
202
202
  const logTime = (label: string) => console.log(label + ':', Math.floor(performance.now()) + 'ms');
203
203
 
204
204
  logTime('Start');
205
205
 
206
206
  // Non-blocking
207
- await cio.sleep(500);
207
+ await co.sleep(500);
208
208
  logTime('After about 0.5s');
209
209
 
210
210
  // This blocks the event loop
211
211
  // On the browser this only works in workers and blocks the worker thread
212
- cio.sleepSync(500);
212
+ co.sleepSync(500);
213
213
  logTime('After another 0.5s');
214
214
  ```
215
215
 
216
216
  ### Spawning tasks
217
217
  Utilities to create and run tasks.
218
218
  ```ts
219
- import * as cio from 'ciorent';
219
+ import * as co from 'ciorent';
220
220
 
221
221
  const task = async (id: number) => {
222
- await cio.sleep((10 - id) * 20 + 50);
222
+ await co.sleep((10 - id) * 20 + 50);
223
223
  console.log('Task', id, 'done');
224
224
  }
225
225
 
226
226
  // Spawn and run 5 tasks sequentially
227
227
  console.log('Running 5 tasks sequentially:');
228
- await cio.sequential(5, task);
228
+ await co.sequential(5, task);
229
229
 
230
230
  // Spawn and run 5 tasks concurrently
231
231
  console.log('Running 5 tasks concurrently:');
232
- await cio.concurrent(5, task);
232
+ await Promise.all(co.spawn(5, task));
233
233
  ```
234
234
 
235
235
  ### Debounce
236
236
  Postpones execution until after an idle period.
237
237
  ```ts
238
- import * as cio from 'ciorent';
238
+ import * as co from 'ciorent';
239
239
 
240
- const fn = cio.debounce((id: number) => {
240
+ const fn = co.debounce((id: number) => {
241
241
  console.log('ID:', id);
242
242
  }, 500);
243
243
 
244
244
  fn(1); // fn(1) gets skipped
245
- await cio.sleep(100);
245
+ await co.sleep(100);
246
246
  fn(2); // fn(2) gets executed
247
247
  ```
248
248
 
249
249
  ### Rate Limit
250
250
  Limits the number of calls within a time window.
251
251
  ```ts
252
- import * as cio from 'ciorent';
252
+ import * as co from 'ciorent';
253
253
 
254
254
  // Allow 2 calls in 500ms, other calls are dropped
255
- const fn = cio.rateLimit((id: number) => {
255
+ const fn = co.rateLimit((id: number) => {
256
256
  console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
257
257
  }, 500, 2);
258
258
 
259
259
  // Some calls will be dropped
260
260
  for (let i = 0; i < 8; i++) {
261
261
  fn(i);
262
- await cio.sleep(400);
262
+ await co.sleep(400);
263
263
  }
264
264
  ```
265
265
 
266
266
  ### Throttle
267
267
  Executes a function at a regular interval.
268
268
  ```ts
269
- import * as cio from 'ciorent';
269
+ import * as co from 'ciorent';
270
270
 
271
271
  // Allow 2 calls in 500ms
272
- const fn = cio.throttle((id: number) => {
272
+ const fn = co.throttle((id: number) => {
273
273
  console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
274
274
  }, 500, 2);
275
275
 
276
- cio.concurrent(8, fn);
276
+ co.spawn(8, fn);
277
277
  ```
278
278
 
package/fiber.d.ts CHANGED
@@ -27,19 +27,19 @@ export interface Process<TReturn = unknown> {
27
27
  */
28
28
  export type Runtime = <const TReturn, const Args extends any[]>(gen: (proc: Process<TReturn>, ...args: Args) => Generator<any, TReturn>, ...args: Args) => Process<TReturn>;
29
29
  /**
30
- * Check whether the fiber is paused
30
+ * Check whether the fiber has been paused
31
31
  */
32
32
  export declare const paused: (t: Process) => boolean;
33
33
  /**
34
34
  * Check whether the fiber is running
35
35
  */
36
- export declare const running: (t: Process) => boolean;
36
+ export declare const resumed: (t: Process) => boolean;
37
37
  /**
38
- * Check whether the fiber is finished
38
+ * Check whether the fiber has finished
39
39
  */
40
40
  export declare const done: (t: Process) => boolean;
41
41
  /**
42
- * Check whether the fiber is interrupted
42
+ * Check whether the fiber has been interrupted
43
43
  */
44
44
  export declare const stopped: (t: Process) => boolean;
45
45
  /**
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;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}
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.3",
3
+ "version": "0.1.4",
4
4
  "description": "A lightweight, low-overhead concurrency library",
5
5
  "homepage": "https://ciorent.netlify.app",
6
6
  "repository": {
@@ -18,14 +18,14 @@
18
18
  "main": "./index.js",
19
19
  "types": "./index.d.ts",
20
20
  "exports": {
21
- "./sliding-queue": "./sliding-queue.js",
22
21
  "./fixed-queue": "./fixed-queue.js",
23
- "./fiber": "./fiber.js",
24
- ".": "./index.js",
25
- "./semaphore": "./semaphore.js",
26
22
  "./dropping-queue": "./dropping-queue.js",
23
+ "./sliding-queue": "./sliding-queue.js",
24
+ ".": "./index.js",
27
25
  "./topic": "./topic.js",
26
+ "./semaphore": "./semaphore.js",
28
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)})};