'use strict'; const assert = require('assert'), BoxError = require('./boxerror.js'), debug = require('debug')('box:locker'), EventEmitter = require('events').EventEmitter, util = require('util'); function Locker() { this._operation = null; this._timestamp = null; this._watcherId = -1; this._lockDepth = 0; // recursive locks } util.inherits(Locker, EventEmitter); // these are mutually exclusive operations Locker.prototype.OP_BOX_UPDATE = 'box_update'; Locker.prototype.OP_INFRA_START = 'infra_start'; Locker.prototype.OP_FULL_BACKUP = 'full_backup'; Locker.prototype.OP_APPTASK = 'apptask'; Locker.prototype.lock = function (operation) { assert.strictEqual(typeof operation, 'string'); if (this._operation !== null) { let error = new BoxError(BoxError.CONFLICT, `Locked for ${this._operation}`); error.operation = this._operation; return error; } this._operation = operation; ++this._lockDepth; this._timestamp = new Date(); this._watcherId = setInterval(() => { debug('Lock unreleased %s', this._operation); }, 1000 * 60 * 5); debug('Acquired : %s', this._operation); this.emit('locked', this._operation); return null; }; Locker.prototype.recursiveLock = function (operation) { if (this._operation === operation) { ++this._lockDepth; debug('Re-acquired : %s Depth : %s', this._operation, this._lockDepth); return null; } return this.lock(operation); }; Locker.prototype.unlock = function (operation) { assert.strictEqual(typeof operation, 'string'); if (this._operation !== operation) throw new BoxError(BoxError.BAD_STATE, 'Mismatched unlock. Current lock is for ' + this._operation); // throw because this is a programming error if (--this._lockDepth === 0) { debug('Released : %s', this._operation); this._operation = null; this._timestamp = null; clearInterval(this._watcherId); this._watcherId = -1; } else { debug('Recursive lock released : %s. Depth : %s', this._operation, this._lockDepth); } this.emit('unlocked', operation); return null; }; exports = module.exports = new Locker();