'use strict';
const deprecated = require('./deprecated');
const isLocked = Symbol('IsLocked');
const readyPromise = Symbol('ReadyPromise');
/**
* This base class provides an lock interface to execute exclusive operations
* @alias util.Lockable
*/
class Lockable {
constructor() {
/**
* Indicates if there is currently an onging exclusive operation
* @type boolean
* @private
*/
this[isLocked] = false;
/**
* A promise which represents the state of the least exclusive operation
* @type Promise
* @private
*/
this[readyPromise] = Promise.resolve(this);
}
/**
* Indicates if there is currently no exclusive operation executed
* <code>true</code> If no exclusive lock is hold
* @type {boolean}
*/
get isReady() {
return !this[isLocked];
}
/**
* Waits on the previously requested operation and calls the doneCallback if the operation is fulfilled
* @param {util.Lockable~doneCallback=} doneCallback The callback which will be invoked when the previously
* operations on this object is completed.
* @param {util.Lockable~failCallback=} failCallback When the lock can't be released caused by a none
* recoverable error
* @return {Promise<this>} A promise which completes successfully, when the previously requested
* operation completes
*/
ready(doneCallback, failCallback) {
return this[readyPromise].then(doneCallback, failCallback);
}
/**
* Try to aquire an exclusive lock and executes the given callback.
* @param {util.Lockable~callback} callback The exclusive operation to execute
* @param {boolean} [critical=false] Indicates if the operation is critical. If the operation is critical and the
* operation fails, then the lock will not be released
* @return {Promise<T>} A promise
* @throws {Error} If the lock can't be aquired
* @alias util.Lockable.prototype.withLock<T>
* @protected
*/
withLock(callback, critical) {
if (this[isLocked]) {
throw new Error('Current operation has not been finished.');
}
try {
this[isLocked] = true;
const result = callback().then((res) => {
this[isLocked] = false;
return res;
}, (e) => {
if (!critical) {
this[isLocked] = false;
}
throw e;
});
this[readyPromise] = result.then(() => this, (e) => {
if (!critical) {
return this;
}
throw e;
});
return result;
} catch (e) {
if (critical) {
this[readyPromise] = Promise.reject(e);
} else {
this[isLocked] = false;
}
throw e;
}
}
}
deprecated(Lockable.prototype, '_isLocked', isLocked);
deprecated(Lockable.prototype, '_readyPromise', readyPromise);
module.exports = Lockable;
/**
* The operation callback is used by the {@link util.Lockable#withLock} method,
* to perform an exclusive operation on the
* @callback util.Lockable~callback
* @return {Promise<T>} A Promise, which reflects the result of the operation
*/
/**
* The done callback is called, when the last operation on this object completes
* @callback util.Lockable~doneCallback
* @param {this} entity This entity instance
* @return {Promise<*>|*} A Promise, result or undefined
*/
/**
* The fail callback is called, when the last critical operation on this object fails
* @callback util.Lockable~failCallback
* @param {Error} error The error which reject the operation
* @return {Promise<*>|*} A Promise, result or undefined
*/