├── .gitignore ├── .npmignore ├── .taprc ├── .taprc copy ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING ├── LICENSE ├── README.md ├── package.json ├── src ├── index.ts └── shuffled-priority-queue.d.ts ├── test ├── fixtures │ └── eval.js ├── simple-test.ts └── tsconfig.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | node_modules 3 | package-lock.json 4 | dist 5 | coverage 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | .nyc_output 3 | package-lock.json 4 | coverage 5 | -------------------------------------------------------------------------------- /.taprc: -------------------------------------------------------------------------------- 1 | check-coverage: true 2 | color: true 3 | coverage: true 4 | coverage-report: 5 | - html 6 | - text 7 | jobs: 2 8 | no-browser: true 9 | test-env: TS_NODE_PROJECT=test/tsconfig.json 10 | test-ignore: $. 11 | test-regex: ((\/|^)(tests?|__tests?__)\/.*|\.(tests?|spec)|^\/?tests?)\.([mc]js|[jt]sx?)$ 12 | timeout: 60 13 | ts: true 14 | -------------------------------------------------------------------------------- /.taprc copy: -------------------------------------------------------------------------------- 1 | check-coverage: true 2 | color: true 3 | coverage: true 4 | coverage-report: 5 | - html 6 | - text 7 | jobs: 2 8 | no-browser: true 9 | test-env: TS_NODE_PROJECT=test/tsconfig.json 10 | test-ignore: $. 11 | test-regex: ((\/|^)(tests?|__tests?__)\/.*|\.(tests?|spec)|^\/?tests?)\.([mc]js|[jt]sx?)$ 12 | timeout: 60 13 | ts: true 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | jasnell@gmail.com, anna@addaleax.net, or matteo.collina@gmail.com. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # Piscina is an OPEN Open Source Project 2 | 3 | ## What? 4 | 5 | Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 6 | 7 | ## Rules 8 | 9 | There are a few basic ground-rules for contributors: 10 | 11 | 1. **No `--force` pushes** on `master` or modifying the Git history in any way after a PR has been merged. 12 | 1. **Non-master branches** ought to be used for ongoing work. 13 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors. 14 | 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 15 | 1. Contributors should attempt to adhere to the prevailing code-style. 16 | 1. 100% code coverage 17 | 1. Semantic Versioning is used. 18 | 19 | ## Releases 20 | 21 | Declaring formal releases remains the prerogative of the project maintainer. 22 | 23 | ## Changes to this arrangement 24 | 25 | This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. 26 | 27 | ----------------------------------------- 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 James M Snell and the Piscina contributors 4 | 5 | Piscina contributors listed at https://github.com/jasnell/piscina#the-team and 6 | in the README file. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # piscina-priority-queue - A Priority Queue for Piscina 2 | 3 | ![CI](https://github.com/jasnell/piscina/workflows/CI/badge.svg) 4 | 5 | ## Example 6 | 7 | ```js 8 | const Piscina = require('piscina'); 9 | const PiscinaPriorityQueue = require('piscina-priority-queue'); 10 | const { resolve } = require('path'); 11 | 12 | const pool = new Piscina({ 13 | filename: resolve(__dirname, 'worker.js'), 14 | taskQueue: new PiscinaPriorityQueue() 15 | }); 16 | 17 | const priority_1_task = PiscinaPriorityQueue.makeTask({ a: 1 }, 1); 18 | const priority_2_task = PiscinaPriorityQueue.makeTask({ a: 1 }, 2); 19 | 20 | (async () => { 21 | await Promise.all([ 22 | pool.runTask(priority_1_task), 23 | pool.runTask(priority_2_task) 24 | ]); 25 | })(); 26 | ``` 27 | 28 | ## The Team 29 | 30 | * James M Snell 31 | * Anna Henningsen 32 | * Matteo Collina 33 | 34 | ## Acknowledgements 35 | 36 | Piscina development is sponsored by [NearForm Research][]. 37 | 38 | [NearForm Research]: https://www.nearform.com/research/ 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "piscina-priority-queue", 3 | "version": "3.0.0", 4 | "description": "A Priority TaskQueue implementation for Piscina", 5 | "main": "./dist/src/index.js", 6 | "exports": { 7 | "import": "./dist/esm-wrapper.mjs", 8 | "require": "./dist/src/index.js" 9 | }, 10 | "types": "./dist/src/index.d.ts", 11 | "scripts": { 12 | "build": "tsc && gen-esm-wrapper . dist/esm-wrapper.mjs", 13 | "lint": "standardx \"**/*.{ts,mjs,js,cjs}\" | snazzy", 14 | "test": "npm run lint && npm run build && npm run test-only", 15 | "test-only": "tap --ts", 16 | "prepack": "npm run build" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/jasnell/piscina-priority-queue" 21 | }, 22 | "keywords": [ 23 | "piscina" 24 | ], 25 | "author": "James M Snell ", 26 | "contributors": [ 27 | "Anna Henningsen ", 28 | "Matteo Collina " 29 | ], 30 | "license": "MIT", 31 | "devDependencies": { 32 | "@types/node": "^15.0.1", 33 | "@typescript-eslint/eslint-plugin": "^4.9.0", 34 | "@typescript-eslint/parser": "^4.9.0", 35 | "gen-esm-wrapper": "^1.1.1", 36 | "piscina": "3", 37 | "snazzy": "^9.0.0", 38 | "standardx": "^7.0.0", 39 | "tap": "^15.0.6", 40 | "ts-node": "^9.1.1", 41 | "typescript": "^4.1.2" 42 | }, 43 | "peerDependencies": { 44 | "piscina": "^3.0.0" 45 | }, 46 | "dependencies": { 47 | "shuffled-priority-queue": "^2.1.0" 48 | }, 49 | "eslintConfig": { 50 | "rules": { 51 | "semi": [ 52 | "error", 53 | "always" 54 | ], 55 | "no-unused-vars": "off", 56 | "@typescript-eslint/no-unused-vars": "error" 57 | }, 58 | "globals": { 59 | "SharedArrayBuffer": true, 60 | "Atomics": true 61 | } 62 | }, 63 | "standardx": { 64 | "parser": "@typescript-eslint/parser", 65 | "plugins": [ 66 | "@typescript-eslint/eslint-plugin" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Piscina from 'piscina'; 2 | import spq from 'shuffled-priority-queue'; 3 | 4 | type TaskQueue = Piscina extends { 5 | options: { 6 | taskQueue: infer T 7 | } 8 | } ? T : never; 9 | 10 | type Task = TaskQueue extends { 11 | shift() : infer T | null 12 | } ? T : never; 13 | 14 | const kEntry = Symbol('PiscinaPriorityQueue.Entry'); 15 | 16 | class PiscinaPriorityQueue implements TaskQueue { 17 | queue = spq(); 18 | 19 | get size () : number { return this.queue.length; } 20 | 21 | push (value : Task) { 22 | const queueOptions = (value as any)[Piscina.queueOptionsSymbol]; 23 | const priority = queueOptions ? (queueOptions.priority || 0) : 0; 24 | (value as any)[kEntry] = this.queue.add({ priority, value }); 25 | } 26 | 27 | remove (value : Task) { 28 | if (value == null) { 29 | return; 30 | } 31 | const entry = (value as any)[kEntry]; 32 | if (entry) { 33 | this.queue.remove(entry); 34 | } 35 | (value as any)[kEntry] = undefined; 36 | } 37 | 38 | shift () : Task | null { 39 | return this.queue.shift().value; 40 | } 41 | 42 | static makeTask (task : object, priority? : number) : Task { 43 | return ({ 44 | ...task, 45 | [Piscina.queueOptionsSymbol]: { priority } 46 | } as object) as Task; 47 | } 48 | } 49 | 50 | export = PiscinaPriorityQueue; 51 | -------------------------------------------------------------------------------- /src/shuffled-priority-queue.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'shuffled-priority-queue'; 2 | -------------------------------------------------------------------------------- /test/fixtures/eval.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-eval 2 | module.exports = function (code) { return eval(code); }; 3 | -------------------------------------------------------------------------------- /test/simple-test.ts: -------------------------------------------------------------------------------- 1 | import Piscina from 'piscina'; 2 | import PiscinaPriorityQueue from '..'; 3 | import { test } from 'tap'; 4 | import { resolve } from 'path'; 5 | 6 | test('PiscinaPriorityQueue works', async ({ equal }) => { 7 | const queue = new PiscinaPriorityQueue(); 8 | equal(queue.size, 0); 9 | const task = PiscinaPriorityQueue.makeTask({}, 1); 10 | queue.push(task); 11 | equal(queue.size, 1); 12 | queue.remove(null); 13 | queue.remove(({} as any)); 14 | equal(queue.size, 1); 15 | queue.remove(task); 16 | equal(queue.size, 0); 17 | }); 18 | 19 | test('PiscinaPriorityQueue works with Piscina', async () => { 20 | const queue = new PiscinaPriorityQueue(); 21 | const pool = new Piscina({ 22 | filename: resolve(__dirname, 'fixtures/eval.js'), 23 | taskQueue: queue, 24 | maxThreads: 1, 25 | minThreads: 1 26 | }); 27 | 28 | await Promise.all([ 29 | pool.runTask(PiscinaPriorityQueue.makeTask({}, 1)), 30 | pool.runTask(PiscinaPriorityQueue.makeTask({}, 2)), 31 | pool.runTask(PiscinaPriorityQueue.makeTask({})), 32 | pool.runTask(PiscinaPriorityQueue.makeTask({}, null)), 33 | pool.runTask({}) 34 | ]); 35 | }); 36 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | "strict": false, 5 | "noImplicitAny": false 6 | }, 7 | "include": [ 8 | "./**/*.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "lib": ["es2019"], 7 | "outDir": "dist", 8 | "rootDir": ".", 9 | "declaration": true, 10 | "sourceMap": true, 11 | 12 | "strict": true, 13 | 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "esModuleInterop": true, 19 | 20 | "resolveJsonModule": true, /* Include modules imported with '.json' extension */ 21 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 22 | }, 23 | "include": ["src"], 24 | } 25 | --------------------------------------------------------------------------------