From: Jérôme Benoit Date: Tue, 28 May 2024 17:42:19 +0000 (+0200) Subject: fix: fix task dequeuing from the last bucket X-Git-Tag: v4.0.13~9 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=e75373e6b8ed2882c1b8dd7eedc8ca9217582121;p=poolifier.git fix: fix task dequeuing from the last bucket Signed-off-by: Jérôme Benoit --- diff --git a/src/fixed-priority-queue.ts b/src/fixed-priority-queue.ts index 53ef6edb..2424b652 100644 --- a/src/fixed-priority-queue.ts +++ b/src/fixed-priority-queue.ts @@ -40,14 +40,31 @@ export class FixedPriorityQueue { this.clear() } + /** + * Checks if the fixed priority queue is empty. + * + * @returns `true` if the fixed priority queue is empty, `false` otherwise. + */ public empty (): boolean { return this.size === 0 } + /** + * Checks if the fixed priority queue is full. + * + * @returns `true` if the fixed priority queue is full, `false` otherwise. + */ public full (): boolean { return this.size === this.capacity } + /** + * Enqueue data into the fixed priority queue. + * + * @param data - Data to enqueue. + * @param priority - Priority of the data. Lower values have higher priority. + * @returns The new size of the priority queue. + */ public enqueue (data: T, priority?: number): number { if (this.full()) { throw new Error('Priority queue is full') @@ -78,6 +95,11 @@ export class FixedPriorityQueue { return this.incrementSize() } + /** + * Dequeue data from the fixed priority queue. + * + * @returns The dequeued data or `undefined` if the priority queue is empty. + */ public dequeue (): T | undefined { if (this.empty()) { return undefined @@ -144,6 +166,11 @@ export class FixedPriorityQueue { return this.size } + /** + * Checks the size. + * + * @param size - The size to check. + */ private checkSize (size: number): void { if (!Number.isSafeInteger(size)) { throw new TypeError( diff --git a/src/pools/worker-node.ts b/src/pools/worker-node.ts index 7ea617fb..07955701 100644 --- a/src/pools/worker-node.ts +++ b/src/pools/worker-node.ts @@ -114,7 +114,7 @@ export class WorkerNode /** @inheritdoc */ public dequeueLastPrioritizedTask (): Task | undefined { // Start from the last empty or partially filled bucket - return this.dequeueTask() + return this.dequeueTask(this.tasksQueue.buckets + 1) } /** @inheritdoc */ diff --git a/src/priority-queue.ts b/src/priority-queue.ts index d1d93078..ed4d63f5 100644 --- a/src/priority-queue.ts +++ b/src/priority-queue.ts @@ -36,11 +36,19 @@ export class PriorityQueue { * @returns PriorityQueue. */ public constructor (bucketSize: number = defaultBucketSize) { + if (!Number.isSafeInteger(bucketSize)) { + throw new TypeError('bucketSize must be an integer') + } + if (bucketSize < 1) { + throw new RangeError('bucketSize must be greater than or equal to 1') + } this.bucketSize = bucketSize this.clear() } - /** The size of the priority queue. */ + /** + * The size of the priority queue. + */ public get size (): number { let node: PriorityQueueNode | undefined = this.tail let size = 0 @@ -51,6 +59,13 @@ export class PriorityQueue { return size } + /** + * The number of filled prioritized buckets. + */ + public get buckets (): number { + return Math.trunc(this.size / this.bucketSize) + } + /** * Enqueue data into the priority queue. * diff --git a/tests/priority-queue.test.mjs b/tests/priority-queue.test.mjs index f366dc0f..bcd3cf0a 100644 --- a/tests/priority-queue.test.mjs +++ b/tests/priority-queue.test.mjs @@ -5,7 +5,18 @@ import { defaultBucketSize, PriorityQueue } from '../lib/priority-queue.cjs' describe('Priority queue test suite', () => { it('Verify constructor() behavior', () => { - const priorityQueue = new PriorityQueue() + expect(() => new PriorityQueue('')).toThrow( + new TypeError('bucketSize must be an integer') + ) + expect(() => new PriorityQueue(-1)).toThrow( + new RangeError('bucketSize must be greater than or equal to 1') + ) + expect(() => new PriorityQueue(0)).toThrow( + new RangeError('bucketSize must be greater than or equal to 1') + ) + let priorityQueue = new PriorityQueue() + expect(priorityQueue.bucketSize).toBe(defaultBucketSize) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(0) expect(priorityQueue.maxSize).toBe(0) expect(priorityQueue.head).toBeInstanceOf(FixedPriorityQueue) @@ -13,11 +24,23 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.capacity).toBe(defaultBucketSize) expect(priorityQueue.tail).toBeInstanceOf(FixedPriorityQueue) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) + const bucketSize = 2 + priorityQueue = new PriorityQueue(bucketSize) + expect(priorityQueue.bucketSize).toBe(bucketSize) + expect(priorityQueue.buckets).toBe(0) + expect(priorityQueue.size).toBe(0) + expect(priorityQueue.maxSize).toBe(0) + expect(priorityQueue.head).toBeInstanceOf(FixedPriorityQueue) + expect(priorityQueue.head.next).toBe(undefined) + expect(priorityQueue.head.capacity).toBe(bucketSize) + expect(priorityQueue.tail).toBeInstanceOf(FixedPriorityQueue) + expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) }) it('Verify default bucket size enqueue() behavior', () => { const priorityQueue = new PriorityQueue() let rtSize = priorityQueue.enqueue(1) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(1) expect(priorityQueue.maxSize).toBe(1) expect(rtSize).toBe(priorityQueue.size) @@ -27,6 +50,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(2) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(2) expect(priorityQueue.maxSize).toBe(2) expect(rtSize).toBe(priorityQueue.size) @@ -37,6 +61,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(3) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(3) expect(priorityQueue.maxSize).toBe(3) expect(rtSize).toBe(priorityQueue.size) @@ -48,6 +73,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(3, -1) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(4) expect(priorityQueue.maxSize).toBe(4) expect(rtSize).toBe(priorityQueue.size) @@ -60,6 +86,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(1, 1) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(5) expect(priorityQueue.maxSize).toBe(5) expect(rtSize).toBe(priorityQueue.size) @@ -77,6 +104,7 @@ describe('Priority queue test suite', () => { it('Verify bucketSize=2 enqueue() behavior', () => { const priorityQueue = new PriorityQueue(2) let rtSize = priorityQueue.enqueue(1) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(1) expect(priorityQueue.maxSize).toBe(1) expect(rtSize).toBe(priorityQueue.size) @@ -86,6 +114,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(2) + expect(priorityQueue.buckets).toBe(1) expect(priorityQueue.size).toBe(2) expect(priorityQueue.maxSize).toBe(2) expect(rtSize).toBe(priorityQueue.size) @@ -96,6 +125,7 @@ describe('Priority queue test suite', () => { expect(priorityQueue.head.next).toBe(undefined) expect(priorityQueue.tail).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(3) + expect(priorityQueue.buckets).toBe(1) expect(priorityQueue.size).toBe(3) expect(priorityQueue.maxSize).toBe(3) expect(rtSize).toBe(priorityQueue.size) @@ -109,6 +139,7 @@ describe('Priority queue test suite', () => { ]) expect(priorityQueue.tail.next).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(3, -1) + expect(priorityQueue.buckets).toBe(2) expect(priorityQueue.size).toBe(4) expect(priorityQueue.maxSize).toBe(4) expect(rtSize).toBe(priorityQueue.size) @@ -123,6 +154,7 @@ describe('Priority queue test suite', () => { ]) expect(priorityQueue.tail.next).toStrictEqual(priorityQueue.head) rtSize = priorityQueue.enqueue(1, 1) + expect(priorityQueue.buckets).toBe(2) expect(priorityQueue.size).toBe(5) expect(priorityQueue.maxSize).toBe(5) expect(rtSize).toBe(priorityQueue.size) @@ -140,6 +172,7 @@ describe('Priority queue test suite', () => { { data: 3, priority: 0 } ]) rtSize = priorityQueue.enqueue(3, -2) + expect(priorityQueue.buckets).toBe(3) expect(priorityQueue.size).toBe(6) expect(priorityQueue.maxSize).toBe(6) expect(rtSize).toBe(priorityQueue.size) @@ -164,23 +197,27 @@ describe('Priority queue test suite', () => { priorityQueue.enqueue(1) priorityQueue.enqueue(2, -1) priorityQueue.enqueue(3) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(3) expect(priorityQueue.maxSize).toBe(3) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBe(undefined) let rtItem = priorityQueue.dequeue() + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(2) expect(priorityQueue.maxSize).toBe(3) expect(rtItem).toBe(2) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBe(undefined) rtItem = priorityQueue.dequeue() + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(1) expect(priorityQueue.maxSize).toBe(3) expect(rtItem).toBe(1) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBe(undefined) rtItem = priorityQueue.dequeue() + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(0) expect(priorityQueue.maxSize).toBe(3) expect(rtItem).toBe(3) @@ -196,41 +233,48 @@ describe('Priority queue test suite', () => { priorityQueue.enqueue(3, -1) priorityQueue.enqueue(1, 1) priorityQueue.enqueue(3, -2) + expect(priorityQueue.buckets).toBe(3) expect(priorityQueue.size).toBe(6) expect(priorityQueue.maxSize).toBe(6) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) let rtItem = priorityQueue.dequeue(3) + expect(priorityQueue.buckets).toBe(2) expect(priorityQueue.size).toBe(5) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(3) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) rtItem = priorityQueue.dequeue() + expect(priorityQueue.buckets).toBe(2) expect(priorityQueue.size).toBe(4) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(1) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) rtItem = priorityQueue.dequeue(2) + expect(priorityQueue.buckets).toBe(1) expect(priorityQueue.size).toBe(3) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(3) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) rtItem = priorityQueue.dequeue(2) + expect(priorityQueue.buckets).toBe(1) expect(priorityQueue.size).toBe(2) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(3) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) rtItem = priorityQueue.dequeue(2) + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(1) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(1) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail.next).toBeInstanceOf(FixedPriorityQueue) rtItem = priorityQueue.dequeue() + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(0) expect(priorityQueue.maxSize).toBe(6) expect(rtItem).toBe(2) @@ -255,12 +299,14 @@ describe('Priority queue test suite', () => { priorityQueue.enqueue(1) priorityQueue.enqueue(2) priorityQueue.enqueue(3) + expect(priorityQueue.buckets).toBe(1) expect(priorityQueue.size).toBe(3) expect(priorityQueue.maxSize).toBe(3) expect(priorityQueue.head.empty()).toBe(false) expect(priorityQueue.tail.empty()).toBe(false) expect(priorityQueue.tail).not.toStrictEqual(priorityQueue.head) priorityQueue.clear() + expect(priorityQueue.buckets).toBe(0) expect(priorityQueue.size).toBe(0) expect(priorityQueue.maxSize).toBe(0) expect(priorityQueue.head.empty()).toBe(true)