add locker. refactor.
This commit is contained in:
parent
3efdc904e7
commit
5023c7efe4
@ -83,6 +83,7 @@ bcoin.debug = function debug() {
|
|||||||
bcoin.utils = utils;
|
bcoin.utils = utils;
|
||||||
bcoin.utils.debug = bcoin.debug;
|
bcoin.utils.debug = bcoin.debug;
|
||||||
bcoin.utils.ensurePrefix = bcoin.ensurePrefix;
|
bcoin.utils.ensurePrefix = bcoin.ensurePrefix;
|
||||||
|
bcoin.locker = require('./bcoin/locker');
|
||||||
bcoin.reader = require('./bcoin/reader');
|
bcoin.reader = require('./bcoin/reader');
|
||||||
bcoin.writer = require('./bcoin/writer');
|
bcoin.writer = require('./bcoin/writer');
|
||||||
bcoin.profiler = require('./bcoin/profiler');
|
bcoin.profiler = require('./bcoin/profiler');
|
||||||
|
|||||||
@ -558,7 +558,6 @@ Address.prototype.toJSON = function toJSON(passphrase) {
|
|||||||
key: key.toJSON(passphrase),
|
key: key.toJSON(passphrase),
|
||||||
type: this.type,
|
type: this.type,
|
||||||
witness: this.witness,
|
witness: this.witness,
|
||||||
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
|
||||||
keys: this.keys.map(utils.toBase58),
|
keys: this.keys.map(utils.toBase58),
|
||||||
m: this.m,
|
m: this.m,
|
||||||
n: this.n
|
n: this.n
|
||||||
@ -583,7 +582,6 @@ Address.fromJSON = function fromJSON(json, passphrase) {
|
|||||||
key: bcoin.keypair.fromJSON(json.key, passphrase),
|
key: bcoin.keypair.fromJSON(json.key, passphrase),
|
||||||
type: json.type,
|
type: json.type,
|
||||||
witness: json.witness,
|
witness: json.witness,
|
||||||
redeem: json.redeem ? new Buffer(json.redeem, 'hex') : null,
|
|
||||||
keys: json.keys.map(utils.fromBase58),
|
keys: json.keys.map(utils.fromBase58),
|
||||||
m: json.m,
|
m: json.m,
|
||||||
n: json.n
|
n: json.n
|
||||||
|
|||||||
@ -33,14 +33,10 @@ function Chain(node, options) {
|
|||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this.mempool = node.mempool;
|
this.mempool = node.mempool;
|
||||||
this.db = new bcoin.chaindb(this, options);
|
this.db = new bcoin.chaindb(this, options);
|
||||||
this.busy = false;
|
|
||||||
this.jobs = [];
|
|
||||||
this.pending = [];
|
|
||||||
this.pendingBlocks = {};
|
|
||||||
this.pendingSize = 0;
|
|
||||||
this.total = 0;
|
this.total = 0;
|
||||||
this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024;
|
this.orphanLimit = options.orphanLimit || (20 << 20);
|
||||||
this.pendingLimit = options.pendingLimit || 20 * 1024 * 1024;
|
this.pendingLimit = options.pendingLimit || (20 << 20);
|
||||||
|
this.locker = new bcoin.locker(this, this.add, this.pendingLimit);
|
||||||
this.invalid = {};
|
this.invalid = {};
|
||||||
this.bestHeight = -1;
|
this.bestHeight = -1;
|
||||||
this.lastUpdate = utils.now();
|
this.lastUpdate = utils.now();
|
||||||
@ -181,61 +177,7 @@ Chain.prototype.open = function open(callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._lock = function _lock(func, args, force) {
|
Chain.prototype._lock = function _lock(func, args, force) {
|
||||||
var self = this;
|
return this.locker.lock(func, args, force);
|
||||||
var block, called;
|
|
||||||
|
|
||||||
if (force) {
|
|
||||||
assert(this.busy);
|
|
||||||
return function unlock() {
|
|
||||||
assert(!called);
|
|
||||||
called = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.busy) {
|
|
||||||
if (func === Chain.prototype.add) {
|
|
||||||
block = args[0];
|
|
||||||
this.pending.push(block);
|
|
||||||
this.pendingBlocks[block.hash('hex')] = true;
|
|
||||||
this.pendingSize += block.getSize();
|
|
||||||
if (this.pendingSize > this.pendingLimit) {
|
|
||||||
this.purgePending();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.jobs.push([func, args]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.busy = true;
|
|
||||||
|
|
||||||
return function unlock() {
|
|
||||||
var item, block;
|
|
||||||
|
|
||||||
assert(!called);
|
|
||||||
called = true;
|
|
||||||
|
|
||||||
self.busy = false;
|
|
||||||
|
|
||||||
if (func === Chain.prototype.add) {
|
|
||||||
if (self.pending.length === 0)
|
|
||||||
self.emit('flush');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.jobs.length === 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
item = self.jobs.shift();
|
|
||||||
|
|
||||||
if (item[0] === Chain.prototype.add) {
|
|
||||||
block = item[1][0];
|
|
||||||
assert(block === self.pending.shift());
|
|
||||||
delete self.pendingBlocks[block.hash('hex')];
|
|
||||||
self.pendingSize -= block.getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
item[0].apply(self, item[1]);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stream headers from electrum.org for quickly
|
// Stream headers from electrum.org for quickly
|
||||||
@ -962,10 +904,7 @@ Chain.prototype.resetTime = function resetTime(ts, callback, force) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.onFlush = function onFlush(callback) {
|
Chain.prototype.onFlush = function onFlush(callback) {
|
||||||
if (this.pending.length === 0)
|
return this.locker.onFlush(callback);
|
||||||
return callback();
|
|
||||||
|
|
||||||
this.once('flush', callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.add = function add(initial, peer, callback, force) {
|
Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||||
@ -1300,21 +1239,7 @@ Chain.prototype.pruneOrphans = function pruneOrphans(peer) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.purgePending = function purgePending() {
|
Chain.prototype.purgePending = function purgePending() {
|
||||||
var self = this;
|
return this.locker.purgePending();
|
||||||
|
|
||||||
utils.debug('Warning: %dmb of pending blocks. Purging.',
|
|
||||||
utils.mb(this.pendingSize));
|
|
||||||
|
|
||||||
this.pending.forEach(function(block) {
|
|
||||||
delete self.pendingBlocks[block.hash('hex')];
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pending.length = 0;
|
|
||||||
this.pendingSize = 0;
|
|
||||||
|
|
||||||
this.jobs = this.jobs.filter(function(item) {
|
|
||||||
return item[0] !== Chain.prototype.add;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.has = function has(hash, callback) {
|
Chain.prototype.has = function has(hash, callback) {
|
||||||
@ -1409,7 +1334,7 @@ Chain.prototype.hasPending = function hasPending(hash) {
|
|||||||
else if (hash.hash)
|
else if (hash.hash)
|
||||||
hash = hash.hash('hex');
|
hash = hash.hash('hex');
|
||||||
|
|
||||||
return !!this.pendingBlocks[hash];
|
return this.locker.hasPending(hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.getEntry = function getEntry(hash, callback) {
|
Chain.prototype.getEntry = function getEntry(hash, callback) {
|
||||||
|
|||||||
125
lib/bcoin/locker.js
Normal file
125
lib/bcoin/locker.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 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.purgePending = function purgePending() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Locker.prototype.onFlush = function onFlush(callback) {
|
||||||
|
if (this.pending.length === 0)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
this.once('flush', callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Locker;
|
||||||
@ -13,6 +13,7 @@ var utils = require('./utils');
|
|||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var BufferWriter = require('./writer');
|
var BufferWriter = require('./writer');
|
||||||
var BufferReader = require('./reader');
|
var BufferReader = require('./reader');
|
||||||
|
var DUMMY_PEER = { sendReject: function() {} };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mempool
|
* Mempool
|
||||||
@ -69,9 +70,12 @@ function Mempool(node, options) {
|
|||||||
|
|
||||||
utils.inherits(Mempool, EventEmitter);
|
utils.inherits(Mempool, EventEmitter);
|
||||||
|
|
||||||
|
Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||||
|
|
||||||
Mempool.prototype._lock = function _lock(func, args, force) {
|
Mempool.prototype._lock = function _lock(func, args, force) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var block, called;
|
var tx, called;
|
||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
assert(this.busy);
|
assert(this.busy);
|
||||||
@ -223,9 +227,6 @@ Mempool.prototype.hasTX = function hasTX(hash, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
|
||||||
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
|
||||||
|
|
||||||
Mempool.prototype.add =
|
Mempool.prototype.add =
|
||||||
Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -236,6 +237,14 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
|||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (typeof peer === 'function') {
|
||||||
|
callback = peer;
|
||||||
|
peer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!peer)
|
||||||
|
peer = DUMMY_PEER;
|
||||||
|
|
||||||
hash = tx.hash('hex');
|
hash = tx.hash('hex');
|
||||||
|
|
||||||
assert(tx.ts === 0);
|
assert(tx.ts === 0);
|
||||||
@ -321,18 +330,6 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, peer, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function VerifyError(reason, score) {
|
|
||||||
Error.call(this);
|
|
||||||
if (Error.captureStackTrace)
|
|
||||||
Error.captureStackTrace(this, VerifyError);
|
|
||||||
this.type = 'VerifyError';
|
|
||||||
this.message = reason;
|
|
||||||
this.reason = score === -1 ? null : reason;
|
|
||||||
this.score = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.inherits(VerifyError, Error);
|
|
||||||
|
|
||||||
Mempool.prototype.verify = function verify(tx, callback) {
|
Mempool.prototype.verify = function verify(tx, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var total, input, coin, i, fee, now;
|
var total, input, coin, i, fee, now;
|
||||||
@ -607,6 +604,9 @@ Mempool.prototype.checkTX = function checkTX(tx, peer) {
|
|||||||
var total = new bn(0);
|
var total = new bn(0);
|
||||||
var uniq = {};
|
var uniq = {};
|
||||||
|
|
||||||
|
if (!peer)
|
||||||
|
peer = DUMMY_PEER;
|
||||||
|
|
||||||
if (tx.inputs.length === 0)
|
if (tx.inputs.length === 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-vin-empty', 100);
|
return peer.sendReject(tx, 'bad-txns-vin-empty', 100);
|
||||||
|
|
||||||
@ -649,6 +649,18 @@ Mempool.prototype.checkTX = function checkTX(tx, peer) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function VerifyError(reason, score) {
|
||||||
|
Error.call(this);
|
||||||
|
if (Error.captureStackTrace)
|
||||||
|
Error.captureStackTrace(this, VerifyError);
|
||||||
|
this.type = 'VerifyError';
|
||||||
|
this.message = reason;
|
||||||
|
this.reason = score === -1 ? null : reason;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.inherits(VerifyError, Error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -754,9 +754,9 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
peer.queue.block.length,
|
peer.queue.block.length,
|
||||||
block.bits,
|
block.bits,
|
||||||
self.peers.all.length,
|
self.peers.all.length,
|
||||||
self.chain.pending.length,
|
self.chain.locker.pending.length,
|
||||||
self.chain.bestHeight,
|
self.chain.bestHeight,
|
||||||
self.chain.jobs.length);
|
self.chain.locker.jobs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
@ -1145,10 +1145,7 @@ Pool.prototype.watch = function watch(id) {
|
|||||||
else
|
else
|
||||||
this.watchMap[hid] = 1;
|
this.watchMap[hid] = 1;
|
||||||
|
|
||||||
if (this.bloom.test(id, 'hex'))
|
this.bloom.add(id);
|
||||||
return;
|
|
||||||
|
|
||||||
this.bloom.add(id, 'hex');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send it to peers
|
// Send it to peers
|
||||||
@ -1528,7 +1525,7 @@ Pool.prototype.scheduleRequests = function scheduleRequests(peer) {
|
|||||||
Pool.prototype._sendRequests = function _sendRequests(peer) {
|
Pool.prototype._sendRequests = function _sendRequests(peer) {
|
||||||
var size, items;
|
var size, items;
|
||||||
|
|
||||||
if (this.chain.pending.length > 0)
|
if (this.chain.locker.pending.length > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (peer.queue.block.length === 0)
|
if (peer.queue.block.length === 0)
|
||||||
|
|||||||
@ -1915,6 +1915,6 @@ utils.SyncBatch = SyncBatch;
|
|||||||
|
|
||||||
if (utils.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
bn.prototype.toBuffer = function toBuffer(order, size) {
|
bn.prototype.toBuffer = function toBuffer(order, size) {
|
||||||
return new Buffer(this.toArray(order, size));
|
return this.toArrayLike(Buffer, order, size);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ workers.spawn = function spawn(index) {
|
|||||||
child = cp.spawn(process.argv[0], [__filename], {
|
child = cp.spawn(process.argv[0], [__filename], {
|
||||||
stdio: ['pipe', 'pipe', 'inherit'],
|
stdio: ['pipe', 'pipe', 'inherit'],
|
||||||
env: utils.merge({}, process.env, {
|
env: utils.merge({}, process.env, {
|
||||||
BCOIN_WORKER: index + ''
|
BCOIN_WORKER_ID: index + ''
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ bcoin.tx.prototype.verifyAsync = function verifyAsync(index, force, flags, callb
|
|||||||
|
|
||||||
workers.listen = function listen() {
|
workers.listen = function listen() {
|
||||||
bcoin.debug = function debug() {
|
bcoin.debug = function debug() {
|
||||||
process.stderr.write('Worker %s: ', process.env.BCOIN_WORKER);
|
process.stderr.write('Worker ' + process.env.BCOIN_WORKER_ID + ':');
|
||||||
return console.error.apply(console.error, arguments);
|
return console.error.apply(console.error, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -372,5 +372,5 @@ function parser(onPacket) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.BCOIN_WORKER)
|
if (process.env.BCOIN_WORKER_ID)
|
||||||
workers.listen();
|
workers.listen();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user