136 lines
2.6 KiB
JavaScript
136 lines
2.6 KiB
JavaScript
/**
|
|
* locker.js - lock and queue for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
|
* https://github.com/indutny/bcoin
|
|
*/
|
|
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var utils = require('./utils');
|
|
var assert = utils.assert;
|
|
|
|
/**
|
|
* Locker
|
|
*/
|
|
|
|
function Locker(parent, add, limit) {
|
|
if (!(this instanceof Locker))
|
|
return Locker(parent, add, limit);
|
|
|
|
this.parent = parent;
|
|
this.jobs = [];
|
|
this.busy = false;
|
|
|
|
this.pending = [];
|
|
this.pendingMap = {};
|
|
this.pendingSize = 0;
|
|
this.pendingLimit = limit || (20 << 20);
|
|
this.add = add;
|
|
}
|
|
|
|
utils.inherits(Locker, EventEmitter);
|
|
|
|
Locker.prototype.hasPending = function hasPending(key) {
|
|
return this.pendingMap[key] === true;
|
|
};
|
|
|
|
Locker.prototype.lock = function lock(func, args, force) {
|
|
var self = this;
|
|
var obj, called;
|
|
|
|
if (force) {
|
|
assert(this.busy);
|
|
return function unlock() {
|
|
assert(!called);
|
|
called = true;
|
|
};
|
|
}
|
|
|
|
if (this.busy) {
|
|
if (this.add && func === this.add) {
|
|
obj = args[0];
|
|
this.pending.push(obj);
|
|
this.pendingMap[obj.hash('hex')] = true;
|
|
this.pendingSize += obj.getSize();
|
|
if (this.pendingSize > this.pendingLimit) {
|
|
this.purgePending();
|
|
return;
|
|
}
|
|
}
|
|
this.jobs.push([func, args]);
|
|
return;
|
|
}
|
|
|
|
this.busy = true;
|
|
|
|
return function unlock() {
|
|
var item, obj;
|
|
|
|
assert(!called);
|
|
called = true;
|
|
|
|
self.busy = false;
|
|
|
|
if (self.add && func === self.add) {
|
|
if (self.pending.length === 0)
|
|
self.emit('flush');
|
|
}
|
|
|
|
if (self.jobs.length === 0)
|
|
return;
|
|
|
|
item = self.jobs.shift();
|
|
|
|
if (self.add && item[0] === self.add) {
|
|
obj = item[1][0];
|
|
assert(obj === self.pending.shift());
|
|
delete self.pendingMap[obj.hash('hex')];
|
|
self.pendingSize -= obj.getSize();
|
|
}
|
|
|
|
item[0].apply(self.parent, item[1]);
|
|
};
|
|
};
|
|
|
|
Locker.prototype.destroy = function destroy() {
|
|
if (this.add)
|
|
this.purgePending();
|
|
this.jobs.length = 0;
|
|
};
|
|
|
|
Locker.prototype.purgePending = function purgePending() {
|
|
var self = this;
|
|
var total = this.pending.length;
|
|
|
|
assert(this.add);
|
|
|
|
utils.debug('Warning: %dmb of pending objects. Purging.',
|
|
utils.mb(this.pendingSize));
|
|
|
|
this.pending.forEach(function(obj) {
|
|
delete self.pendingMap[obj.hash('hex')];
|
|
});
|
|
|
|
this.pending.length = 0;
|
|
this.pendingSize = 0;
|
|
|
|
this.jobs = this.jobs.filter(function(item) {
|
|
return item[0] !== self.add;
|
|
});
|
|
|
|
if (total !== 0)
|
|
this.emit('flush');
|
|
};
|
|
|
|
Locker.prototype.onFlush = function onFlush(callback) {
|
|
if (this.pending.length === 0)
|
|
return callback();
|
|
|
|
this.once('flush', callback);
|
|
};
|
|
|
|
/**
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = Locker;
|