ciorent 0.0.8 → 0.0.11

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,53 +1,42 @@
1
- A low-overhead, cross-runtime concurrency library.
1
+ A lightweight, low-overhead concurrency library
2
+
3
+ # Examples
4
+ ## Semaphore
5
+ Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
2
6
 
3
- # Usage
4
- Pausing to prioritize an asynchronous task:
5
7
  ```ts
8
+ import * as semaphore from 'ciorent/semaphore';
6
9
  import * as cio from 'ciorent';
7
10
 
8
- // Expensive sync task
9
- const task1 = async () => {
10
- let x = 0;
11
- for (let i = 0; i < (Math.random() + 15) * 1e7; i++) {
12
- // Occasional pausing
13
- if (i % 2e6 === 0)
11
+ // Only allow 2 of these tasks to run concurrently
12
+ const task = semaphore.task(
13
+ semaphore.init(2),
14
+ async (task: number) => {
15
+ for (let i = 1; i <= 5; i++) {
16
+ console.log('Task', task, 'iteration', i);
14
17
  await cio.pause;
18
+ }
15
19
 
16
- x += Math.random() * 32 + i * Math.round(Math.random() * 16);
20
+ console.log('Task', task, 'end');
17
21
  }
18
- console.log('Finish task 1:', x);
19
- };
20
-
21
- // Short async task
22
- const task2 = async () => {
23
- console.log('Fetch start', performance.now().toFixed(2) + 'ms');
24
- const txt = await fetch('http://example.com');
25
- console.log('Fetch status', txt.status);
26
- };
22
+ );
27
23
 
28
- // Task 2 will not get blocked by task 1
29
- task1();
30
- task2();
24
+ // Try to run 6 tasks with 4 tasks running concurrently
25
+ cio.concurrent(6, task, 4);
31
26
  ```
32
27
 
33
- Sleep for a duration:
34
- ```ts
35
- import * as cio from 'ciorent';
36
-
37
- await cio.sleep(1000);
38
- console.log('Slept for about 1s');
39
- ```
28
+ ## Channel
29
+ Channel is a synchronization primitive via message passing. A message may be sent over a channel, and another process is able to receive messages sent over a channel it has a reference to.
40
30
 
41
- Go-like channels for synchronizations:
42
31
  ```ts
43
32
  import * as channel from 'ciorent/channel';
44
- import * as cio from 'ciorent';
45
33
 
46
34
  const c = channel.init<number>();
35
+ const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms));
47
36
 
48
37
  const run = async () => {
49
38
  for (let i = 0; i < 10; i++) {
50
- await cio.sleep(10);
39
+ await sleep(10);
51
40
  channel.send(c, i);
52
41
  console.log('Sent', i);
53
42
  }
@@ -62,8 +51,9 @@ const log = async () => {
62
51
  // Non-blocking
63
52
  const x = await channel.recieve(c);
64
53
 
65
- // If the channel has been closed
66
- if (x === null) break;
54
+ // 'recieve' returns undefined if
55
+ // The channel has been closed
56
+ if (x == null) break;
67
57
 
68
58
  console.log('Recieved', x);
69
59
  };
@@ -77,7 +67,7 @@ console.log('Starting...');
77
67
  ```
78
68
 
79
69
  ## Latch
80
- A latch is a synchronization tool that allows one or more threads to wait until a specific condition is met.
70
+ Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
81
71
 
82
72
  ```ts
83
73
  import * as latch from 'ciorent/latch';
@@ -119,62 +109,83 @@ await main();
119
109
  await main();
120
110
  ```
121
111
 
122
- If you don't need to close the latch again:
112
+ ## Utilities
113
+ ### Pausing
114
+ Delay the execution of a function for other asynchronous tasks to run.
123
115
  ```ts
124
- import * as latch from 'ciorent/latch';
125
116
  import * as cio from 'ciorent';
126
117
 
127
- const [pauseFetch, startFetch] = latch.init();
128
-
129
- const task = async () => {
130
- // Blocks until the latch is open
131
- await pauseFetch;
132
-
133
- const res = await fetch('http://example.com');
134
- console.log('Fetch status:', res.status);
135
- }
118
+ // Expensive sync task
119
+ const task1 = async () => {
120
+ let x = 0;
121
+ for (let i = 0; i < (Math.random() + 15) * 1e7; i++) {
122
+ // Frequent pausing
123
+ if (i % 2e6 === 0)
124
+ await cio.pause;
136
125
 
137
- const prepare = () => {
138
- console.log('Run before fetch:', performance.now().toFixed(2));
126
+ x += Math.random() * 32 + i * Math.round(Math.random() * 16);
127
+ }
128
+ console.log('Finish task 1:', x);
129
+ };
139
130
 
140
- // Unblock the latch
141
- startFetch();
142
- }
131
+ // Short async task
132
+ const task2 = async () => {
133
+ console.log('Fetch start', performance.now().toFixed(2) + 'ms');
134
+ const txt = await fetch('http://example.com');
135
+ console.log('Fetch status', txt.status);
136
+ };
143
137
 
144
- const main = async () => {
145
- const p = task();
146
- await cio.sleep(500);
147
- prepare();
138
+ // Task 2 will not get blocked by task 1
139
+ task1();
140
+ task2();
141
+ ```
148
142
 
149
- return p;
150
- }
143
+ ### Sleep
144
+ A cross-runtime sleep function.
145
+ ```ts
146
+ import { sleep } from 'ciorent';
151
147
 
152
- // Run fetch after 500ms
153
- await main();
148
+ await sleep(500);
149
+ console.log('Hi');
154
150
  ```
155
151
 
156
- ## Semaphore
157
- A semaphore is a synchronization tool to control access to shared resources.
152
+ ### Concurrency
153
+ Control how many tasks can be executed concurrently.
154
+ ```ts
155
+ import concurrent from 'ciorent/concurrent';
156
+ import * as cio from 'ciorent';
157
+
158
+ // Allow 3 tasks to run at the same time
159
+ const run = concurrent(3);
158
160
 
159
- It's essentially a counter that regulates how many threads or processes can access a particular resource or section of code.
161
+ for (let id = 1; id <= 6; id++)
162
+ run(async () => {
163
+ await cio.sleep(Math.random() * 20 + 50);
164
+ console.log('Task', id, 'done');
165
+ });
166
+ ```
160
167
 
168
+ ### Spawning tasks
169
+ Creating new tasks with controlled concurrency.
161
170
  ```ts
162
- import * as semaphore from 'ciorent/semaphore';
163
171
  import * as cio from 'ciorent';
164
172
 
165
- // Only allow 2 of these tasks to run concurrently
166
- const task = semaphore.task(
167
- semaphore.init(2),
168
- async (task: number) => {
169
- for (let i = 1; i <= 5; i++) {
170
- console.log('Task', task, 'iteration', i);
171
- await cio.pause;
172
- }
173
+ const task = async (id: number) => {
174
+ await cio.sleep(Math.random() * 20 + 50);
175
+ console.log('Task', id, 'done');
176
+ }
173
177
 
174
- console.log('Task', task, 'end');
175
- }
176
- );
178
+ // Spawn and run 5 tasks sequentially
179
+ console.log('Running sequentially:');
180
+ cio.sequential(5, task);
177
181
 
178
- // Try to run 6 tasks with 4 tasks running concurrently
179
- cio.concurrent(6, task, 4);
182
+ // Spawn and run 5 tasks concurrently
183
+ console.log('Running 5 tasks concurrently:');
184
+ cio.concurrent(5, task);
185
+
186
+ // Spawn and run 5 tasks, with the maximum
187
+ // tasks running concurrently set to 3
188
+ console.log('Running each 3 tasks concurrently:');
189
+ cio.concurrent(5, task, 3);
180
190
  ```
191
+
package/channel.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
- * @module
3
- * Channels
2
+ * @module Channels
4
3
  */
5
- import type { QueueNode } from './queue';
4
+ import type { QueueNode } from './fixed-queue';
6
5
  /**
7
6
  * Describe a channel
8
7
  */
@@ -22,11 +21,11 @@ export interface Channel<T> {
22
21
  /**
23
22
  * The head of the Promise resolve queue
24
23
  */
25
- 3: QueueNode<(value: T | null) => void>;
24
+ 3: QueueNode<(value?: T) => void>;
26
25
  /**
27
26
  * The tail of the Promise resolve queue
28
27
  */
29
- 4: QueueNode<(value: T | null) => void>;
28
+ 4: QueueNode<(value?: T) => void>;
30
29
  }
31
30
  /**
32
31
  * Create a channel
@@ -42,12 +41,12 @@ export declare const send: <T>(c: Channel<T>, t: T) => void;
42
41
  * Recieve a message from a channel, return null if the channel is closed
43
42
  * @param c
44
43
  */
45
- export declare const recieve: <T>(c: Channel<T>) => Promise<T | null>;
44
+ export declare const recieve: <T>(c: Channel<T>) => Promise<T | undefined>;
46
45
  /**
47
46
  * Recieve a message from a channel, return null if no message is currently in queue
48
47
  * @param c
49
48
  */
50
- export declare const poll: <T>(c: Channel<T>) => T | null;
49
+ export declare const poll: <T>(c: Channel<T>) => T | undefined;
51
50
  /**
52
51
  * Close a channel
53
52
  * @param c
package/channel.js CHANGED
@@ -1 +1 @@
1
- let resolvedNull=Promise.resolve(null);export let init=()=>{let qu=[null,null];let resolveQu=[null,null];return[true,qu,qu,resolveQu,resolveQu]};export let send=(c,t)=>{if(c[0]){if(c[4][1]!==null)(c[4]=c[4][1])[0](t);else c[1]=c[1][1]=[t,null]}};export let recieve=(c)=>c[2][1]!==null?Promise.resolve((c[2]=c[2][1])[0]):c[0]?new Promise((res)=>{c[3]=c[3][1]=[res,null]}):resolvedNull;export let poll=(c)=>c[2][1]!==null?(c[2]=c[2][1])[0]:null;export let close=(c)=>{c[0]=false;while(c[4][1]!==null)(c[4]=c[4][1])[0](null)};export let active=(c)=>c[0];
1
+ import{pause as resolvedPromise}from".";export let init=()=>{let qu=[null,null];let resolveQu=[null,null];return[true,qu,qu,resolveQu,resolveQu]};export let send=(c,t)=>{if(c[0]){if(c[4][1]!==null)(c[4]=c[4][1])[0](t);else c[1]=c[1][1]=[t,null]}};export let recieve=(c)=>c[2][1]!==null?Promise.resolve((c[2]=c[2][1])[0]):c[0]?new Promise((res)=>{c[3]=c[3][1]=[res,null]}):resolvedPromise;export let poll=(c)=>c[2][1]!==null?(c[2]=c[2][1])[0]:undefined;export let close=(c)=>{c[0]=false;while(c[4][1]!==null)(c[4]=c[4][1])[0]()};export let active=(c)=>c[0];
package/concurrent.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * @module
3
- * Concurrency controls
2
+ * @module Concurrency controls
4
3
  */
5
4
  /**
6
5
  * Describe a concurrency controller
@@ -1,10 +1,9 @@
1
1
  /**
2
- * @module
3
- * Dropping queues
2
+ * @module Dropping queues
4
3
  */
5
- import type { FixedQueue } from '.';
6
- export { default as init } from '.';
7
- export { pop } from './sliding';
4
+ import type { FixedQueue } from './fixed-queue';
5
+ export { default as init } from './fixed-queue';
6
+ export { pop } from './sliding-queue';
8
7
  /**
9
8
  * Push an item to a dropping queue
10
9
  * @param q - The queue to push to
@@ -0,0 +1 @@
1
+ export{default as init}from"./fixed-queue";export{pop}from"./sliding-queue";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,7 +1,6 @@
1
1
  /**
2
2
  * @private
3
- * @module
4
- * Queue utilities
3
+ * @module Queue utilities
5
4
  */
6
5
  /**
7
6
  * Describe a fixed-sized queue
package/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * @module
3
- * Other utilities
2
+ * @module Other utilities
4
3
  */
5
4
  /**
6
5
  * Continue running the function on next microtask.
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)}));export let sequential=async(n,task)=>{for(let i=1;i<=n;i++)await task(i)};export let concurrent=async(n,task,concurrency)=>{if(concurrency==null){let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(i+1);return Promise.all(arr)}let arr=new Array(concurrency);let pre=1;for(let block=n/concurrency>>>0;block>0;block--){for(let j=0;j<concurrency;j++)arr[j]=task(pre+j);await Promise.all(arr);pre+=concurrency}n-=pre;for(let i=0;i<=n;i++)arr[i]=task(pre+i);return Promise.all(arr)};
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)}));export let sequential=async(n,task)=>{for(let i=0;i<n;i++)await task(i)};export let concurrent=async(n,task,concurrency)=>{if(concurrency==null){let arr=new Array(n);for(let i=0;i<n;i++)arr[i]=task(i);return Promise.all(arr)}let arr=new Array(concurrency);let pre=0;for(let block=n/concurrency>>>0;block>0;block--){for(let j=0;j<concurrency;j++)arr[j]=task(pre+j);await Promise.all(arr);pre+=concurrency}n-=pre;for(let i=0;i<n;i++)arr[i]=task(pre+i);return Promise.all(arr)};
package/latch.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * @module
3
- * Latches
2
+ * @module Latches
4
3
  */
5
4
  /**
6
5
  * Describe a latch
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ciorent",
3
- "version": "0.0.8",
4
- "description": "A low-overhead, lightweight concurrency library",
3
+ "version": "0.0.11",
4
+ "description": "A lightweight, low-overhead concurrency library",
5
5
  "homepage": "https://ciorent.netlify.app",
6
6
  "repository": {
7
7
  "type": "github",
@@ -10,7 +10,8 @@
10
10
  "keywords": [
11
11
  "low-overhead",
12
12
  "lightweight",
13
- "concurrency"
13
+ "concurrency",
14
+ "cross-runtime"
14
15
  ],
15
16
  "license": "MIT",
16
17
  "type": "module",
@@ -22,7 +23,7 @@
22
23
  "build:publish": "bun build:test && bun task report-size && bun task publish",
23
24
  "lint": "eslint ./src",
24
25
  "lint:fix": "eslint ./src --fix",
25
- "docs": "typedoc ./src/** --entryPointStrategy Expand --plugin typedoc-material-theme --skipErrorChecking"
26
+ "docs": "typedoc"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@stylistic/eslint-plugin": "latest",
@@ -35,6 +36,7 @@
35
36
  "tsx": "latest",
36
37
  "typedoc": "^0.27.9",
37
38
  "typedoc-material-theme": "^1.3.0",
39
+ "typedoc-plugin-inline-sources": "^1.2.1",
38
40
  "typescript": "latest",
39
41
  "typescript-eslint": "latest"
40
42
  }
package/semaphore.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
- * @module
3
- * Semaphores
2
+ * @module Semaphores
4
3
  */
5
- import type { QueueNode } from './queue';
4
+ import type { QueueNode } from './fixed-queue';
6
5
  /**
7
6
  * Describe a semaphore
8
7
  */
@@ -1,9 +1,8 @@
1
1
  /**
2
- * @module
3
- * Sliding queues
2
+ * @module Sliding queues
4
3
  */
5
- import type { FixedQueue } from '.';
6
- export { default as init } from '.';
4
+ import type { FixedQueue } from './fixed-queue';
5
+ export { default as init } from './fixed-queue';
7
6
  /**
8
7
  * Push an item to a sliding queue
9
8
  * @param q - The queue to push to
@@ -0,0 +1 @@
1
+ export{default as init}from"./fixed-queue";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/queue/dropping.js DELETED
@@ -1 +0,0 @@
1
- export{default as init}from".";export{pop}from"./sliding";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/queue/sliding.js DELETED
@@ -1 +0,0 @@
1
- export{default as init}from".";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}};
File without changes