- Add FIFO semaphore for per-hoster concurrency control - Add token-bucket speed limiter with abort signal support - Rewrite upload-manager with retry loop, speed monitoring, and rich progress events - Add per-hoster settings: retries, max speed, parallel count, restart below speed, time interval, max size - Add context menu with shutdown-after-finish (sleep/shutdown/restart), always-on-top - Add z-o-o-m-style queue table with 8 columns, status-colored rows, progress bars - Add debounced queue rendering with scroll position preservation - Add statusbar with global speed, total bytes, elapsed time - Fix speedMonitor interval leak on error and scoping bug - Fix throttle not respecting abort signal during cancellation - Fix combined signal listener cleanup - Bump version to 1.1.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
49 lines
1.0 KiB
JavaScript
49 lines
1.0 KiB
JavaScript
/**
|
|
* FIFO Semaphore for per-hoster concurrency control.
|
|
* acquire() blocks until a slot is available, release() frees it.
|
|
*/
|
|
class Semaphore {
|
|
constructor(limit) {
|
|
this.limit = Math.max(1, limit || 1);
|
|
this.active = 0;
|
|
this.queue = [];
|
|
}
|
|
|
|
acquire() {
|
|
return new Promise((resolve) => {
|
|
if (this.active < this.limit) {
|
|
this.active++;
|
|
resolve();
|
|
} else {
|
|
this.queue.push(resolve);
|
|
}
|
|
});
|
|
}
|
|
|
|
release() {
|
|
if (this.queue.length > 0) {
|
|
// Don't decrement active — hand slot directly to next waiter
|
|
const next = this.queue.shift();
|
|
next();
|
|
} else {
|
|
this.active = Math.max(0, this.active - 1);
|
|
}
|
|
}
|
|
|
|
updateLimit(newLimit) {
|
|
this.limit = Math.max(1, newLimit || 1);
|
|
// If new limit is higher, wake up waiting tasks
|
|
while (this.active < this.limit && this.queue.length > 0) {
|
|
this.active++;
|
|
const next = this.queue.shift();
|
|
next();
|
|
}
|
|
}
|
|
|
|
get pending() {
|
|
return this.queue.length;
|
|
}
|
|
}
|
|
|
|
module.exports = Semaphore;
|