UNPKG

@adpt/core

Version:
134 lines 4.49 kB
"use strict"; /* * Copyright 2019 Unbounded Systems, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const async_lock_1 = tslib_1.__importDefault(require("async-lock")); const p_defer_1 = tslib_1.__importDefault(require("p-defer")); const error_1 = require("../error"); const server_1 = require("./server"); class NoLocker { lock() { return Promise.resolve({ [server_1.$serverLock]: true }); } unlock(l) { return Promise.resolve(); } } exports.NoLocker = NoLocker; class ProcessLocker { constructor() { this.locks = new async_lock_1.default(); } lock() { return new Promise((resolve, reject) => { const releaseComplete = p_defer_1.default(); this.locks.acquire("thelock", (releaseLock) => { resolve({ release: async () => { releaseLock(); return releaseComplete.promise; }, [server_1.$serverLock]: true, }); }, (err) => { if (err) { reject(err); releaseComplete.reject(err); } else { releaseComplete.resolve(); } }); }); } async unlock(lock) { await lock.release(); } } exports.ProcessLocker = ProcessLocker; class ServerBase { constructor(worldLocker) { this.processLocker = new ProcessLocker(); this.worldLocker = worldLocker || (new NoLocker()); } async lock() { let pLock; try { pLock = await this.processLocker.lock(); if (this.currentProcessLock) { throw new error_1.InternalError(`AdaptServer: previous process lock not cleared`); } this.currentProcessLock = pLock; this.currentWorldLock = await this.worldLocker.lock(); return pLock; } catch (err) { if (pLock) { if (pLock === this.currentProcessLock) delete this.currentProcessLock; await this.processLocker.unlock(pLock); } throw err; } } async unlock(l) { if (!this.assertLock(l, "unlock")) throw new error_1.InternalError(`Bad lock`); const wLock = this.currentWorldLock; if (wLock) { delete this.currentWorldLock; await this.worldLocker.unlock(wLock); } delete this.currentProcessLock; await this.processLocker.unlock(l); } async withLock(options, f) { let release; if (options.lock) { // Requester thinks they already hold the lock. But do they? this.assertLock(options.lock, "use"); // They do hold the lock. So don't unlock when this // operation is complete; requester will call unlock later. release = async () => { }; } else { // Get a temporary per-operation lock const tempLock = await this.lock(); release = () => this.unlock(tempLock); } try { return await f(); } finally { await release(); } } // Confirm that a lock is the current lock assertLock(l, op) { if (!this.currentProcessLock) { throw new error_1.InternalError(`AdaptServer: Attempt to ${op} a stale lock. Server is not ` + `currently locked.`); } if (l !== this.currentProcessLock) { throw new error_1.InternalError(`AdaptServer: Attempt to ${op} a stale lock. Server is locked ` + `by another user.`); } return true; } } exports.ServerBase = ServerBase; //# sourceMappingURL=server_base.js.map