-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix EMFILE too many open files, added maxConcurrency option (#8)
- Loading branch information
Showing
10 changed files
with
146 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* Run async tasks in queue with a maximum number of threads. | ||
* Works like Promise.all, but with a maximum number of threads. | ||
* - The order of the results is guaranteed to be the same as the order of the input queue. | ||
* - If any task fails, the whole queue is rejected. | ||
* - If the queue is empty, the result is an empty array. | ||
* - If the queue has only one task, the result is an array with one element. | ||
* | ||
* @example | ||
* // Before | ||
* const packFiles = await Promise.all( | ||
* packFilenames.map((filename) => | ||
* readPackFile(gitdir, `${PACKDIR}/${filename}`, readObjectHeaderByHash, readObjectByHash) | ||
* ) | ||
* ); | ||
* | ||
* // After | ||
* const packFiles = await promiseAllThreaded(50, packFilenames, async (filename) => | ||
* readPackFile(gitdir, `${PACKDIR}/${filename}`, readObjectHeaderByHash, readObjectByHash) | ||
* ); | ||
*/ | ||
export async function promiseAllThreaded<T, R>( | ||
maxThreadCount: number, | ||
queue: T[], | ||
asyncFn: (task: T, taskIdx: number) => Promise<R> | ||
): Promise<R[]> { | ||
const result = Array(queue.length); | ||
let taskProcessed = 0; | ||
let queueSnapshot = [...queue]; | ||
const thread = async () => { | ||
while (taskProcessed < queueSnapshot.length) { | ||
const taskIdx = taskProcessed++; | ||
const task = queueSnapshot[taskIdx]; | ||
result[taskIdx] = await asyncFn(task, taskIdx); | ||
} | ||
}; | ||
|
||
await Promise.all( | ||
Array.from({ length: Math.min(maxThreadCount, queueSnapshot.length) }, () => thread()) | ||
).catch((err) => { | ||
// remove all pending tasks | ||
queueSnapshot = []; | ||
throw err; | ||
}); | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,38 @@ | ||
import assert from 'assert'; | ||
import { readEncodedOffset, BufferCursor } from '../src/utils/buffer.js'; | ||
import { promiseAllThreaded } from '../src/utils/threads.js'; | ||
|
||
it('readEncodedOffset', () => { | ||
const buffer = Buffer.from([142, 254, 254, 254, 254, 254, 254, 127]); | ||
const cursor = new BufferCursor(buffer); | ||
|
||
assert.strictEqual(readEncodedOffset(cursor), Number.MAX_SAFE_INTEGER); | ||
}); | ||
|
||
it('promiseAllThreaded', async () => { | ||
const maxThreadCount = 2; | ||
const queue = [1, 2, 3, 4, 5]; | ||
const asyncFn = async (task: number) => task * 2; | ||
|
||
const result = await promiseAllThreaded(maxThreadCount, queue, asyncFn); | ||
|
||
assert.deepStrictEqual(result, [2, 4, 6, 8, 10]); | ||
}); | ||
|
||
it('promiseAllThreaded with error', async () => { | ||
const maxThreadCount = 2; | ||
const queue = [1, 2, 3, 4, 5]; | ||
const asyncFn = async (task: number) => { | ||
if (task === 3) { | ||
throw new Error('Task failed'); | ||
} | ||
return task * 2; | ||
}; | ||
|
||
try { | ||
await promiseAllThreaded(maxThreadCount, queue, asyncFn); | ||
assert.fail('Expected an error'); | ||
} catch (err) { | ||
assert.strictEqual(err.message, 'Task failed'); | ||
} | ||
}); |