ciorent 0.0.7 → 0.0.9

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,44 +1,33 @@
1
1
  A low-overhead, cross-runtime concurrency library.
2
2
 
3
- # Usage
4
- Pausing to prioritize an asynchronous task:
3
+ # Examples
4
+ ## Semaphore
5
+ Semaphore is a concurrency primitive used to control access to a common resource by multiple processes.
6
+
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';
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.
36
30
 
37
- await cio.sleep(1000);
38
- console.log('Slept for about 1s');
39
- ```
40
-
41
- Go-like channels for synchronizations:
42
31
  ```ts
43
32
  import * as channel from 'ciorent/channel';
44
33
 
@@ -77,7 +66,7 @@ console.log('Starting...');
77
66
  ```
78
67
 
79
68
  ## Latch
80
- A latch is a synchronization tool that allows one or more threads to wait until a specific condition is met.
69
+ Latch is a synchronization primitive that allows one process to wait until another completes an operation before continuing execution.
81
70
 
82
71
  ```ts
83
72
  import * as latch from 'ciorent/latch';
@@ -95,9 +84,6 @@ const task = async () => {
95
84
 
96
85
  const prepare = () => {
97
86
  console.log('Run before fetch:', performance.now().toFixed(2));
98
-
99
- // Unblock the latch
100
- latch.open(fetchLatch);
101
87
  }
102
88
 
103
89
  const main = async () => {
@@ -105,76 +91,100 @@ const main = async () => {
105
91
  await cio.sleep(500);
106
92
  prepare();
107
93
 
94
+ // Allows all previously blocked tasks to run
95
+ latch.open(fetchLatch);
96
+
97
+ // Reclose the latch
98
+ // Tasks that aren't blocked yet will be blocked
99
+ latch.reset(fetchLatch);
100
+
108
101
  return p;
109
102
  }
110
103
 
111
104
  // Run fetch after 500ms
112
105
  await main();
113
106
 
114
- // Re-close the latch
115
- latch.close(fetchLatch);
116
-
117
107
  // Run fetch after another 500ms
118
108
  await main();
119
109
  ```
120
110
 
121
- If you don't need to close the latch again:
111
+ ## Utilities
112
+ ### Pausing
113
+ Delay the execution of a function for other asynchronous tasks to run.
122
114
  ```ts
123
- import * as latch from 'ciorent/latch';
124
115
  import * as cio from 'ciorent';
125
116
 
126
- const [pauseFetch, startFetch] = latch.init();
127
-
128
- const task = async () => {
129
- // Blocks until the latch is open
130
- await pauseFetch;
131
-
132
- const res = await fetch('http://example.com');
133
- console.log('Fetch status:', res.status);
134
- }
117
+ // Expensive sync task
118
+ const task1 = async () => {
119
+ let x = 0;
120
+ for (let i = 0; i < (Math.random() + 15) * 1e7; i++) {
121
+ // Frequent pausing
122
+ if (i % 2e6 === 0)
123
+ await cio.pause;
135
124
 
136
- const prepare = () => {
137
- console.log('Run before fetch:', performance.now().toFixed(2));
125
+ x += Math.random() * 32 + i * Math.round(Math.random() * 16);
126
+ }
127
+ console.log('Finish task 1:', x);
128
+ };
138
129
 
139
- // Unblock the latch
140
- startFetch();
141
- }
130
+ // Short async task
131
+ const task2 = async () => {
132
+ console.log('Fetch start', performance.now().toFixed(2) + 'ms');
133
+ const txt = await fetch('http://example.com');
134
+ console.log('Fetch status', txt.status);
135
+ };
142
136
 
143
- const main = async () => {
144
- const p = task();
145
- await cio.sleep(500);
146
- prepare();
137
+ // Task 2 will not get blocked by task 1
138
+ task1();
139
+ task2();
140
+ ```
147
141
 
148
- return p;
149
- }
142
+ ### Sleep
143
+ A cross-runtime sleep function.
144
+ ```ts
145
+ import { sleep } from 'ciorent';
150
146
 
151
- // Run fetch after 500ms
152
- await main();
147
+ await sleep(500);
148
+ console.log('Hi');
153
149
  ```
154
150
 
155
- ## Semaphore
156
- A semaphore is a synchronization tool to control access to shared resources.
151
+ ### Concurrency
152
+ Control how many tasks can be executed concurrently.
153
+ ```ts
154
+ import concurrent from 'ciorent/concurrent';
155
+ import * as cio from 'ciorent';
156
+
157
+ // Allow 3 tasks to run at the same time
158
+ const run = concurrent(3);
157
159
 
158
- It's essentially a counter that regulates how many threads or processes can access a particular resource or section of code.
160
+ for (let id = 1; id <= 6; id++)
161
+ run(async () => {
162
+ await cio.sleep(Math.random() * 20 + 50);
163
+ console.log('Task', id, 'done');
164
+ });
165
+ ```
159
166
 
167
+ ### Spawning tasks
168
+ Creating new tasks with controlled concurrency.
160
169
  ```ts
161
- import * as semaphore from 'ciorent/semaphore';
162
170
  import * as cio from 'ciorent';
163
171
 
164
- // Only allow 2 of these tasks to run concurrently
165
- const task = semaphore.task(
166
- semaphore.init(2),
167
- async (task: number) => {
168
- for (let i = 1; i <= 5; i++) {
169
- await cio.pause;
170
- console.log('Task', task, 'iteration', i);
171
- }
172
+ const task = async (id: number) => {
173
+ await cio.sleep(Math.random() * 20 + 50);
174
+ console.log('Task', id, 'done');
175
+ }
172
176
 
173
- await cio.sleep(500);
174
- console.log('Task', task, 'end');
175
- }
176
- );
177
+ // Spawn and run 5 tasks sequentially
178
+ console.log('Running sequentially:');
179
+ cio.sequential(5, task);
180
+
181
+ // Spawn and run 5 tasks concurrently
182
+ console.log('Running concurrently:');
183
+ cio.concurrent(5, task);
177
184
 
178
- // Try to run 5 tasks concurrently
179
- for (let i = 1; i <= 5; i++) task(i);
185
+ // Spawn and run 5 tasks, with the maximum
186
+ // tasks running concurrently set to 3
187
+ console.log('Running each 3 tasks concurrently:');
188
+ cio.concurrent(5, task, 3);
180
189
  ```
190
+
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @module
3
+ * Concurrency controls
4
+ */
5
+ /**
6
+ * Describe a concurrency controller
7
+ */
8
+ export type Controller = <T>(task: () => Promise<T>) => Promise<T>;
9
+ /**
10
+ * Create a concurrency controller, allow n tasks to run concurrently
11
+ * @param n
12
+ */
13
+ declare const _default: (n: number) => Controller;
14
+ export default _default;
package/concurrent.js ADDED
@@ -0,0 +1 @@
1
+ export default (n)=>{let pending=new Array(n);let cnt=0;return async(f)=>{if(cnt<n)return pending[cnt++]=f();await Promise.allSettled(pending);cnt=0;return pending[0]=f()}};
package/index.d.ts CHANGED
@@ -16,10 +16,15 @@ export declare const pause: Promise<void>;
16
16
  */
17
17
  export declare const sleep: (ms: number) => Promise<void>;
18
18
  /**
19
- * Spawn n tasks
19
+ * Spawn n tasks that runs sequentially
20
20
  * @param n
21
21
  * @param task - The function to run
22
- * @param args - The arguments to pass in the function
23
- * @returns
24
22
  */
25
- export declare const spawn: <Args extends any[], R extends Promise<any>>(n: number, task: (...args: Args) => R, ...args: Args) => R[];
23
+ export declare const sequential: (n: number, task: (id: number) => Promise<any>) => Promise<void>;
24
+ /**
25
+ * Spawn n tasks that runs concurrently
26
+ * @param n
27
+ * @param task - The function to run
28
+ * @param concurrency - The amount of task to run concurrently
29
+ */
30
+ export declare const concurrent: (n: number, task: (id: number) => Promise<any>, concurrency?: number) => Promise<any>;
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 spawn=(n,task,...args)=>{let a=new Array(n);while(n--!==0)a[n]=task(...args);return 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)}));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
@@ -22,6 +22,6 @@ export declare const pause: (latch: Latch) => Promise<void>;
22
22
  */
23
23
  export declare const open: (latch: Latch) => void;
24
24
  /**
25
- * Close a latch
25
+ * Re-close a latch
26
26
  */
27
- export declare const close: (latch: Latch) => void;
27
+ export declare const reset: (latch: Latch) => void;
package/latch.js CHANGED
@@ -1 +1 @@
1
- import{pause as endPromise}from".";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}};
1
+ import{pause as endPromise}from".";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}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ciorent",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A low-overhead, lightweight concurrency library",
5
5
  "homepage": "https://ciorent.netlify.app",
6
6
  "repository": {
package/semaphore.js CHANGED
@@ -1 +1 @@
1
- import{pause as resolvedPromise}from".";export let init=(n)=>{let root=[null,null];return[n,root,root]};export let pause=(s)=>{if(s[0]===0){let r;let p=new Promise((res)=>{r=res});s[1]=s[1][1]=[r,null];return p}s[0]--;return resolvedPromise};export let signal=(s)=>{if(s[1]===s[2])s[0]++;else(s[2]=s[2][1])[0]()};export let task=(s,f)=>async(...a)=>{try{await pause(s);return f(...a)}finally{signal(s)}};
1
+ import{pause as resolvedPromise}from".";export let init=(n)=>{let root=[null,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][1]=[r,null];return p}return resolvedPromise};export let signal=(s)=>{if(s[0]<0)(s[2]=s[2][1])[0]();s[0]++};export let task=(s,f)=>async(...a)=>{try{await pause(s);return await f(...a)}finally{signal(s)}};