add a read lock for walletdb.

This commit is contained in:
Christopher Jeffrey 2016-06-30 21:58:50 -07:00
parent e3f023142f
commit 59cdc96c9f
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 77 additions and 10 deletions

View File

@ -442,7 +442,6 @@ Input.prototype.inspect = function inspect() {
type: this.getType(),
subtype: this.getSubtype(),
address: this.getAddress(),
value: utils.btc(coin.value),
script: this.script,
witness: this.witness,
redeem: this.getRedeem(),

View File

@ -21,7 +21,9 @@ var assert = utils.assert;
function Locker(parent, add) {
if (!(this instanceof Locker))
return Locker(parent, add);
return new Locker(parent, add);
EventEmitter.call(this);
this.parent = parent;
this.jobs = [];

View File

@ -50,6 +50,10 @@ function WalletDB(options) {
this.options = options;
this.network = bcoin.network.get(options.network);
// We need one read lock for `get` and `create`.
// It will hold locks specific to wallet ids.
this.readLock = new ReadLock(this);
this.db = bcoin.ldb({
network: this.network,
name: this.options.name || 'wallet',
@ -156,6 +160,15 @@ WalletDB.prototype._close = function close(callback) {
});
};
/**
* Invoke mutex lock.
* @returns {Function} unlock
*/
WalletDB.prototype._lock = function lock(id, func, args, force) {
return this.readLock.lock(id, func, args, force);
};
/**
* Emit balance events after a tx is saved.
* @private
@ -398,7 +411,14 @@ WalletDB.prototype.hasListener = function hasListener(id, event) {
WalletDB.prototype.get = function get(id, callback) {
var self = this;
var watcher, wallet;
var unlock, watcher, wallet;
unlock = this._lock(id, get, [id, callback]);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
if (!id)
return callback();
@ -459,13 +479,20 @@ WalletDB.prototype.save = function save(wallet, callback) {
WalletDB.prototype.create = function create(options, callback) {
var self = this;
var wallet;
var wallet, unlock;
if (typeof options === 'function') {
callback = options;
options = {};
}
unlock = this._lock(options.id, create, [options, callback]);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
this.has(options.id, function(err, exists) {
if (err)
return callback(err);
@ -1049,9 +1076,6 @@ WalletDB.prototype._getKey = function _getKey(id, account, errback, callback) {
WalletDB.prototype.removeBlockSPV = function removeBlockSPV(block, callback) {
var self = this;
callback = utils.ensure(callback);
this.tx.getHeightHashes(block.height, function(err, hashes) {
if (err)
return callback(err);
@ -1071,9 +1095,6 @@ WalletDB.prototype.removeBlockSPV = function removeBlockSPV(block, callback) {
WalletDB.prototype.removeBlock = function removeBlock(block, callback) {
var self = this;
callback = utils.ensure(callback);
utils.forEachSerial(block.txs, function(tx, next) {
self.tx.unconfirm(tx.hash('hex'), next);
}, callback);
@ -1438,6 +1459,51 @@ function isAlpha(key) {
return /^[\u0030-\u007d]+$/.test(key);
}
function ReadLock(parent) {
if (!(this instanceof ReadLock))
return new ReadLock(parent);
this.parent = parent;
this.jobs = [];
this.busy = {};
}
ReadLock.prototype.lock = function lock(id, func, args, force) {
var self = this;
var called;
if (force || !id) {
assert(!id || this.busy[id]);
return function unlock() {
assert(!called);
called = true;
};
}
if (this.busy[id]) {
this.jobs.push([func, args]);
return;
}
this.busy[id] = true;
return function unlock() {
var item;
assert(!called);
called = true;
delete self.busy[id];
if (self.jobs.length === 0)
return;
item = self.jobs.shift();
item[0].apply(self.parent, item[1]);
};
};
/*
* Expose
*/