UNPKG

@deepkit/core

Version:
154 lines 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Mutex = exports.ProcessLocker = exports.ProcessLock = void 0; function __assignType(fn, args) { fn.__type = args; return fn; } /* * Deepkit Framework * Copyright (C) 2021 Deepkit UG, Marc J. Schmidt * * This program is free software: you can redistribute it and/or modify * it under the terms of the MIT License. * * You should have received a copy of the MIT License along with this program. */ const array_js_1 = require("./array.js"); const LOCKS = {}; /** * This lock mechanism works only for one process (worker). * * live-mutex: has horrible API and doesn't allow to check if an key is currently locked. * proper-filelock: No way to do a correct mutex locking with event-driven blocking acquire() method. * redislock: Very bad performance on high-load (when multiple locks on the same key `wait`, since it loops) * mongodb lock: even worse performance than redis. Jesus. */ class ProcessLock { constructor(id) { this.id = id; this.holding = false; } async acquire(ttl = 0, timeout = 0) { if (this.holding) { throw new Error('Lock already acquired'); } return new Promise(__assignType((resolve, reject) => { const ourTake = () => { LOCKS[this.id].time = Date.now() / 1000; this.holding = true; resolve(); if (ttl) { this.ttlTimeout = setTimeout(() => { this.unlock(); }, ttl * 1000); } }; if (timeout > 0) { setTimeout(() => { if (LOCKS[this.id]) (0, array_js_1.arrayRemoveItem)(LOCKS[this.id].queue, ourTake); //reject is never handled when resolve is called first reject('Lock timed out ' + this.id); }, timeout * 1000); } if (LOCKS[this.id]) { LOCKS[this.id].queue.push(ourTake); } else { LOCKS[this.id] = { time: Date.now() / 1000, queue: [] }; this.holding = true; resolve(); if (ttl) { this.ttlTimeout = setTimeout(() => { this.unlock(); }, ttl * 1000); } } }, ['resolve', 'reject', '', 'P"2!"2""/#'])); } isLocked() { return this.holding; } tryLock(ttl = 0) { this.holding = false; if (!LOCKS[this.id]) { LOCKS[this.id] = { time: Date.now() / 1000, queue: [] }; this.holding = true; if (ttl) { this.ttlTimeout = setTimeout(() => { this.unlock(); }, ttl * 1000); } } return this.holding; } unlock() { clearTimeout(this.ttlTimeout); if (!this.holding) { return; } this.holding = false; if (LOCKS[this.id].queue.length) { //there are other locks waiting. //so we pick the next, and call it const next = LOCKS[this.id].queue.shift(); next(); } else { //nobody is waiting, so we just delete that lock delete LOCKS[this.id]; } } } exports.ProcessLock = ProcessLock; ProcessLock.__type = ['ttlTimeout', 'id', 'constructor', 'ttl', () => 0, 'timeout', () => 0, 'acquire', 'isLocked', () => 0, 'tryLock', 'unlock', '"3!<P&2":"0#P\'2$>%\'2&>\'"0(P"0)P\'2$>*"0+P"0,5']; class ProcessLocker { /** * * @param id * @param ttl optional defines when the times automatically unlocks. * @param timeout if after `timeout` seconds the lock isn't acquired, it throws an error. */ async acquireLock(id, ttl = 0, timeout = 0) { const lock = new ProcessLock(id); await lock.acquire(ttl, timeout); return lock; } async tryLock(id, ttl = 0) { const lock = new ProcessLock(id); if (lock.tryLock(ttl)) { return lock; } return; } isLocked(id) { return !!LOCKS[id]; } } exports.ProcessLocker = ProcessLocker; ProcessLocker.__type = ['id', 'ttl', () => 0, 'timeout', () => 0, () => ProcessLock, 'acquireLock', () => 0, () => ProcessLock, 'tryLock', 'isLocked', 'P&2!\'2">#\'2$>%P7&`0\'P&2!\'2">(PP7)-J`0*P&2!)0+5']; class Mutex { unlock() { if (this.resolver) this.resolver(); this.promise = undefined; } async lock() { while (this.promise) { await this.promise; } this.promise = new Promise(__assignType((resolver) => { this.resolver = resolver; }, ['resolver', '', 'P"2!"/"'])); } } exports.Mutex = Mutex; Mutex.__type = ['promise', '', 'resolver', 'unlock', 'lock', '$`3!8<P"/"3#8<P$0$P$`0%5']; //# sourceMappingURL=process-locker.js.map