ciorent 0.1.1 → 0.1.3

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
@@ -1,63 +1,31 @@
1
1
  A lightweight, low-overhead concurrency library.
2
- ## Fibers
3
- Virtual threads with more controlled execution.
2
+ ## Semaphore
3
+ Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
4
4
 
5
5
  ```ts
6
+ import * as semaphore from 'ciorent/semaphore';
6
7
  import * as cio from 'ciorent';
7
- import * as fiber from 'ciorent/fiber';
8
-
9
- const thread1 = fiber.fn(function* () {
10
- console.log('Fiber 1 started');
11
-
12
- // Thread1 will be interrupted by thread2
13
- // As thread2 will end first
14
- yield cio.sleep(1000);
15
-
16
- console.log('Fiber 1 done');
17
- });
18
-
19
- const thread2 = fiber.fn(function* (thread) {
20
- console.log('Fiber 2 started');
21
-
22
- yield;
23
- console.log('Fiber 2 resumed');
24
-
25
- // Start thread 1 and make thread1
26
- // lifetime depends on thread2
27
- fiber.mount(fiber.spawn(thread1), thread);
28
-
29
- console.log('Fiber 2 done');
30
- });
31
-
32
- // Start running the thread
33
- fiber.spawn(thread2);
34
- ```
35
8
 
36
- ## Latch
37
- Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
9
+ // Only allow 2 task to run concurrently
10
+ const sem = semaphore.init(2);
38
11
 
39
- ```ts
40
- import * as latch from 'ciorent/latch';
12
+ const task = async (id: number) => {
13
+ // Acquire the semaphore or wait for the semaphore to be available
14
+ await semaphore.pause(sem);
41
15
 
42
- const startFetch = latch.init();
16
+ console.log('Task', id, 'started');
43
17
 
44
- const task = async () => {
45
- // Blocks until the latch is open
46
- await latch.pause(startFetch);
18
+ // Let the main thread schedules other tasks
19
+ for (let i = 1; i <= 5; i++) await cio.pause;
47
20
 
48
- console.log('Start fetching...');
49
- const res = await fetch('http://example.com');
50
- console.log('Fetch status:', res.status);
51
- }
21
+ console.log('Task', id, 'end');
52
22
 
53
- const prepare = () => {
54
- // This always run first
55
- console.log('Run before fetch:', performance.now().toFixed(2));
56
- latch.open(startFetch);
23
+ // Release the semaphore
24
+ semaphore.signal(sem);
57
25
  }
58
26
 
59
- task();
60
- prepare();
27
+ // Try to run 5 tasks concurrently
28
+ cio.concurrent(5, task);
61
29
  ```
62
30
 
63
31
  ## Pubsub
@@ -134,33 +102,63 @@ run();
134
102
  console.log('Starting...');
135
103
  ```
136
104
 
137
- ## Semaphore
138
- Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
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.
139
134
 
140
135
  ```ts
141
- import * as semaphore from 'ciorent/semaphore';
142
136
  import * as cio from 'ciorent';
137
+ import * as fiber from 'ciorent/fiber';
143
138
 
144
- // Only allow 2 task to run concurrently
145
- const sem = semaphore.init(2);
139
+ const f1 = fiber.fn(function* () {
140
+ console.log('Fiber 1 started');
146
141
 
147
- const task = async (id: number) => {
148
- // Acquire the semaphore or wait for the semaphore to be available
149
- await semaphore.pause(sem);
142
+ // Wait for a promise
143
+ yield cio.sleep(1000);
150
144
 
151
- console.log('Task', id, 'started');
145
+ console.log('Fiber 1 done');
146
+ return Math.random();
147
+ });
152
148
 
153
- // Let the main thread schedules other tasks
154
- for (let i = 1; i <= 5; i++) await cio.pause;
149
+ fiber.spawn(function* (proc) {
150
+ console.log('Fiber 2 started');
155
151
 
156
- console.log('Task', id, 'end');
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);
157
155
 
158
- // Release the semaphore
159
- semaphore.signal(sem);
160
- }
156
+ // Start f1 and make its lifetime depends on current fiber
157
+ fiber.mount(fiber.spawn(f1), proc);
161
158
 
162
- // Try to run 5 tasks concurrently
163
- cio.concurrent(5, task);
159
+ // The runtime will interrupt f1
160
+ console.log('Fiber 2 done');
161
+ });
164
162
  ```
165
163
 
166
164
  ## Utilities
@@ -253,16 +251,16 @@ Limits the number of calls within a time window.
253
251
  ```ts
254
252
  import * as cio from 'ciorent';
255
253
 
256
- // Allow 1 call in 500ms
254
+ // Allow 2 calls in 500ms, other calls are dropped
257
255
  const fn = cio.rateLimit((id: number) => {
258
256
  console.log('Call ' + id + ':', Math.floor(performance.now()) + 'ms');
259
- }, 500, 1);
257
+ }, 500, 2);
260
258
 
261
- fn(1); // fn(1) gets executed
262
- await cio.sleep(100);
263
- fn(2); // fn(2) gets skipped
264
- await cio.sleep(500);
265
- fn(3); // fn(3) gets executed
259
+ // Some calls will be dropped
260
+ for (let i = 0; i < 8; i++) {
261
+ fn(i);
262
+ await cio.sleep(400);
263
+ }
266
264
  ```
267
265
 
268
266
  ### Throttle
@@ -275,6 +273,6 @@ const fn = cio.throttle((id: number) => {
275
273
  console.log(id + ': ' + Math.floor(performance.now()) + 'ms');
276
274
  }, 500, 2);
277
275
 
278
- cio.concurrent(8, (id) => fn(id));
276
+ cio.concurrent(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 is paused
31
+ */
32
+ export declare const paused: (t: Process) => boolean;
33
+ /**
34
+ * Check whether the fiber is running
35
+ */
36
+ export declare const running: (t: Process) => boolean;
37
+ /**
38
+ * Check whether the fiber is finished
39
+ */
40
+ export declare const done: (t: Process) => boolean;
41
+ /**
42
+ * Check whether the fiber is 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){thread[3].forEach(stop);return v}t=g.next(v)}thread[1]=2;return t.value}finally{thread[3].forEach(stop)}};export let fn=(f)=>f;export let spawn=(f,...args)=>{let thread=[null,1,null,[]];thread[0]=invoke(f(thread,...args),thread);return thread};export let pause=(t)=>{if(t[1]===1)t[1]=0};export let resume=(t)=>{if(t[1]===0){t[1]=1;t[2]?.()}};export let stop=(t)=>{if(t[1]===0){t[1]=2;t[2]?.()}else t[1]=2};export function*join(t){return yield t[1]}export let finish=(t)=>t[1];export let mount=(child,parent)=>{parent[3].push(child)};export let control=(t,signal)=>{signal.addEventListener("abort",()=>{stop(t)})};export function*unwrap(t){return yield t}
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}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ciorent",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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
  "./sliding-queue": "./sliding-queue.js",
22
+ "./fixed-queue": "./fixed-queue.js",
23
+ "./fiber": "./fiber.js",
24
+ ".": "./index.js",
22
25
  "./semaphore": "./semaphore.js",
26
+ "./dropping-queue": "./dropping-queue.js",
23
27
  "./topic": "./topic.js",
24
- ".": "./index.js",
25
- "./latch": "./latch.js",
26
28
  "./channel": "./channel.js",
27
- "./fixed-queue": "./fixed-queue.js",
28
- "./dropping-queue": "./dropping-queue.js",
29
- "./fiber": "./fiber.js"
29
+ "./latch": "./latch.js"
30
30
  }
31
31
  }
package/semaphore.d.ts CHANGED
@@ -32,6 +32,6 @@ export declare const pause: (s: Semaphore) => Promise<void>;
32
32
  */
33
33
  export declare const signal: (s: Semaphore) => void;
34
34
  /**
35
- * Wrap a task to bind to a custom semaphore later
35
+ * Bind a task to a semaphore
36
36
  */
37
- export declare const wrap: <Args extends any[], Return extends Promise<any>>(f: (...args: Args) => Return) => ((s: Semaphore, ...a: Args) => Return);
37
+ export declare const bind: <T extends (...args: any[]) => Promise<any>>(f: T, s: Semaphore) => T;
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 wrap=(f)=>async(s,...a)=>{s[0]--;if(s[0]<0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][0]=[null,r];await p}try{return await f(...a)}finally{signal(s)}};
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]++}};