net: use linked lists.

This commit is contained in:
Christopher Jeffrey 2016-12-15 21:54:36 -08:00
parent 67ffecc989
commit 69a9b5873f
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 406 additions and 207 deletions

View File

@ -327,7 +327,7 @@ RPC.prototype.getinfo = co(function* getinfo(args) {
balance: Amount.btc(balance.unconfirmed, true), balance: Amount.btc(balance.unconfirmed, true),
blocks: this.chain.height, blocks: this.chain.height,
timeoffset: this.network.time.offset, timeoffset: this.network.time.offset,
connections: this.pool.peers.all.length, connections: this.pool.peers.size(),
proxy: '', proxy: '',
difficulty: this._getDifficulty(), difficulty: this._getDifficulty(),
testnet: this.network.type !== Network.main, testnet: this.network.type !== Network.main,
@ -379,7 +379,7 @@ RPC.prototype.getnetworkinfo = co(function* getnetworkinfo(args) {
protocolversion: constants.VERSION, protocolversion: constants.VERSION,
localservices: this.pool.services, localservices: this.pool.services,
timeoffset: this.network.time.offset, timeoffset: this.network.time.offset,
connections: this.pool.peers.all.length, connections: this.pool.peers.size(),
networks: [], networks: [],
relayfee: Amount.btc(this.network.getMinRelay(), true), relayfee: Amount.btc(this.network.getMinRelay(), true),
localaddresses: [], localaddresses: [],
@ -413,7 +413,7 @@ RPC.prototype.addnode = co(function* addnode(args) {
case 'onetry': case 'onetry':
if (!this.pool.peers.get(addr)) { if (!this.pool.peers.get(addr)) {
peer = this.pool.createPeer(addr); peer = this.pool.createPeer(addr);
this.pool.peers.addPending(peer); this.pool.peers.addOutbound(peer);
} }
break; break;
} }
@ -450,47 +450,46 @@ RPC.prototype.getaddednodeinfo = co(function* getaddednodeinfo(args) {
peer = this.pool.peers.get(addr); peer = this.pool.peers.get(addr);
if (!peer) if (!peer)
throw new RPCError('Node has not been added.'); throw new RPCError('Node has not been added.');
peers = [peer]; return [this._toAddedNode(peer)];
} else {
peers = this.pool.peers.all;
} }
for (i = 0; i < peers.length; i++) { for (peer = this.pool.peers.head(); peer; peer = peer.next)
peer = peers[i]; out.push(this._toAddedNode(peer));
out.push({
addednode: peer.hostname,
connected: peer.connected,
addresses: [
{
address: peer.hostname,
connected: peer.outbound
? 'outbound'
: 'inbound'
}
]
});
}
return out; return out;
}); });
RPC.prototype._toAddedNode = function _toAddedNode(peer) {
return {
addednode: peer.hostname,
connected: peer.connected,
addresses: [
{
address: peer.hostname,
connected: peer.outbound
? 'outbound'
: 'inbound'
}
]
};
};
RPC.prototype.getconnectioncount = co(function* getconnectioncount(args) { RPC.prototype.getconnectioncount = co(function* getconnectioncount(args) {
if (args.help || args.length !== 0) if (args.help || args.length !== 0)
throw new RPCError('getconnectioncount'); throw new RPCError('getconnectioncount');
return this.pool.peers.all.length; return this.pool.peers.size();
}); });
RPC.prototype.getnettotals = co(function* getnettotals(args) { RPC.prototype.getnettotals = co(function* getnettotals(args) {
var sent = 0; var sent = 0;
var recv = 0; var recv = 0;
var i, peer; var peer;
if (args.help || args.length > 0) if (args.help || args.length > 0)
throw new RPCError('getnettotals'); throw new RPCError('getnettotals');
for (i = 0; i < this.pool.peers.all.length; i++) { for (peer = this.pool.peers.head(); peer; peer = peer.next) {
peer = this.pool.peers.all[i];
sent += peer.socket.bytesWritten; sent += peer.socket.bytesWritten;
recv += peer.socket.bytesRead; recv += peer.socket.bytesRead;
} }
@ -504,13 +503,12 @@ RPC.prototype.getnettotals = co(function* getnettotals(args) {
RPC.prototype.getpeerinfo = co(function* getpeerinfo(args) { RPC.prototype.getpeerinfo = co(function* getpeerinfo(args) {
var peers = []; var peers = [];
var i, peer; var peer;
if (args.help || args.length !== 0) if (args.help || args.length !== 0)
throw new RPCError('getpeerinfo'); throw new RPCError('getpeerinfo');
for (i = 0; i < this.pool.peers.all.length; i++) { for (peer = this.pool.peers.head(); peer; peer = peer.next) {
peer = this.pool.peers.all[i];
peers.push({ peers.push({
id: peer.id, id: peer.id,
addr: peer.hostname, addr: peer.hostname,
@ -538,13 +536,11 @@ RPC.prototype.getpeerinfo = co(function* getpeerinfo(args) {
}); });
RPC.prototype.ping = co(function* ping(args) { RPC.prototype.ping = co(function* ping(args) {
var i;
if (args.help || args.length !== 0) if (args.help || args.length !== 0)
throw new RPCError('ping'); throw new RPCError('ping');
for (i = 0; i < this.pool.peers.all.length; i++) for (peer = this.pool.peers.head(); peer; peer = peer.next)
this.pool.peers.all[i].sendPing(); peer.sendPing();
return null; return null;
}); });
@ -1538,7 +1534,7 @@ RPC.prototype.getblocktemplate = co(function* getblocktemplate(args) {
} }
if (!this.network.selfConnect) { if (!this.network.selfConnect) {
if (this.pool.peers.all.length === 0) if (this.pool.peers.size() === 0)
throw new RPCError('Bitcoin is not connected!'); throw new RPCError('Bitcoin is not connected!');
if (!this.chain.isFull()) if (!this.chain.isFull())

View File

@ -631,7 +631,6 @@ HTTPServer.prototype._init = function _init() {
this.get('/', function(req, res, send, next) { this.get('/', function(req, res, send, next) {
var totalTX = this.mempool ? this.mempool.totalTX : 0; var totalTX = this.mempool ? this.mempool.totalTX : 0;
var size = this.mempool ? this.mempool.getSize() : 0; var size = this.mempool ? this.mempool.getSize() : 0;
var loader = this.pool.peers.load ? 1 : 0;
send(200, { send(200, {
version: constants.USER_VERSION, version: constants.USER_VERSION,
@ -644,9 +643,9 @@ HTTPServer.prototype._init = function _init() {
}, },
pool: { pool: {
services: this.pool.services.toString(2), services: this.pool.services.toString(2),
outbound: this.pool.peers.outbound.length + loader, outbound: this.pool.peers.outbound,
pending: this.pool.peers.pending.length, pending: this.pool.peers.pending,
inbound: this.pool.peers.inbound.length inbound: this.pool.peers.inbound
}, },
mempool: { mempool: {
tx: totalTX, tx: totalTX,

View File

@ -26,6 +26,7 @@ var BIP152 = require('./bip152');
var Block = require('../primitives/block'); var Block = require('../primitives/block');
var TX = require('../primitives/tx'); var TX = require('../primitives/tx');
var errors = require('../btc/errors'); var errors = require('../btc/errors');
var List = require('../utils/list');
var packetTypes = packets.types; var packetTypes = packets.types;
var VerifyResult = errors.VerifyResult; var VerifyResult = errors.VerifyResult;
@ -93,6 +94,7 @@ function Peer(pool, addr, socket) {
this.version = null; this.version = null;
this.destroyed = false; this.destroyed = false;
this.ack = false; this.ack = false;
this.pending = true;
this.connected = false; this.connected = false;
this.ts = 0; this.ts = 0;
this.preferHeaders = false; this.preferHeaders = false;
@ -119,6 +121,9 @@ function Peer(pool, addr, socket) {
this.drainSize = 0; this.drainSize = 0;
this.drainQueue = []; this.drainQueue = [];
this.next = null;
this.prev = null;
this.challenge = null; this.challenge = null;
this.lastPong = -1; this.lastPong = -1;
this.lastPing = -1; this.lastPing = -1;
@ -134,8 +139,8 @@ function Peer(pool, addr, socket) {
this.requestTimeout = 10000; this.requestTimeout = 10000;
this.requestMap = {}; this.requestMap = {};
this.queueBlock = []; this.queueBlock = new List();
this.queueTX = []; this.queueTX = new List();
this.uid = 0; this.uid = 0;
this.id = Peer.uid++; this.id = Peer.uid++;
@ -154,6 +159,7 @@ function Peer(pool, addr, socket) {
} else { } else {
this.socket = socket; this.socket = socket;
this.connected = true; this.connected = true;
this.pending = false;
} }
if (this.options.bip151) { if (this.options.bip151) {
@ -491,7 +497,7 @@ Peer.prototype._finalize = co(function* _finalize() {
yield this.updateWatch(); yield this.updateWatch();
// Announce our currently broadcasted items. // Announce our currently broadcasted items.
yield this.announce(this.pool.invItems); yield this.announce(this.pool.invItems.toArray());
// Set a fee rate filter. // Set a fee rate filter.
if (this.pool.feeRate !== -1) if (this.pool.feeRate !== -1)
@ -1883,7 +1889,7 @@ Peer.prototype._handleAddr = function _handleAddr(packet) {
'Received %d addrs (hosts=%d, peers=%d) (%s).', 'Received %d addrs (hosts=%d, peers=%d) (%s).',
addrs.length, addrs.length,
this.pool.hosts.items.length, this.pool.hosts.items.length,
this.pool.peers.all.length, this.pool.peers.size(),
this.hostname); this.hostname);
this.fire('addr', addrs); this.fire('addr', addrs);

View File

@ -29,6 +29,7 @@ var tcp = require('./tcp');
var request = require('../http/request'); var request = require('../http/request');
var VerifyError = errors.VerifyError; var VerifyError = errors.VerifyError;
var VerifyResult = errors.VerifyResult; var VerifyResult = errors.VerifyResult;
var List = require('../utils/list');
/** /**
* A pool of peers for handling all network activity. * A pool of peers for handling all network activity.
@ -145,7 +146,7 @@ function Pool(options) {
// Currently broadcasted objects. // Currently broadcasted objects.
this.invMap = {}; this.invMap = {};
this.invItems = []; this.invItems = new List();
this.invTimeout = 60000; this.invTimeout = 60000;
this.scheduled = false; this.scheduled = false;
@ -327,14 +328,14 @@ Pool.prototype._open = co(function* _open() {
*/ */
Pool.prototype._close = co(function* close() { Pool.prototype._close = co(function* close() {
var i, items, hashes, hash; var i, next, item, hashes, hash;
this.stopSync(); this.stopSync();
items = this.invItems.slice(); for (item = this.invItems.head; item; item = next) {
next = item.next;
for (i = 0; i < items.length; i++) item.finish();
items[i].finish(); }
hashes = Object.keys(this.requestMap); hashes = Object.keys(this.requestMap);
@ -467,7 +468,7 @@ Pool.prototype._handleLeech = function _handleLeech(socket) {
addr = NetworkAddress.fromSocket(socket, this.network); addr = NetworkAddress.fromSocket(socket, this.network);
if (this.peers.inbound.length >= this.maxInbound) { if (this.peers.inbound >= this.maxInbound) {
this.logger.debug('Ignoring leech: too many inbound (%s).', addr.hostname); this.logger.debug('Ignoring leech: too many inbound (%s).', addr.hostname);
socket.destroy(); socket.destroy();
return; return;
@ -613,7 +614,9 @@ Pool.prototype.addLoader = function addLoader() {
this.logger.info('Added loader peer (%s).', peer.hostname); this.logger.info('Added loader peer (%s).', peer.hostname);
this.peers.addLoader(peer); assert(!this.peers.load);
this.peers.load = peer;
this.peers.add(peer);
this.fillPeers(); this.fillPeers();
util.nextTick(function() { util.nextTick(function() {
@ -675,13 +678,13 @@ Pool.prototype.startSync = function startSync() {
*/ */
Pool.prototype.sync = function sync() { Pool.prototype.sync = function sync() {
var i; var peer;
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.trySync(); if (!peer.outbound || peer.pending)
continue;
for (i = 0; i < this.peers.outbound.length; i++) peer.trySync();
this.peers.outbound[i].trySync(); }
}; };
/** /**
@ -690,15 +693,11 @@ Pool.prototype.sync = function sync() {
*/ */
Pool.prototype.forceSync = function forceSync() { Pool.prototype.forceSync = function forceSync() {
var i, peer; var peer;
if (this.peers.load) { for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.syncSent = false; if (!peer.outbound || peer.pending)
this.peers.load.trySync(); continue;
}
for (i = 0; i < this.peers.outbound.length; i++) {
peer = this.peers.outbound[i];
peer.syncSent = false; peer.syncSent = false;
peer.trySync(); peer.trySync();
} }
@ -709,7 +708,7 @@ Pool.prototype.forceSync = function forceSync() {
*/ */
Pool.prototype.stopSync = function stopSync() { Pool.prototype.stopSync = function stopSync() {
var i; var peer;
if (!this.syncing) if (!this.syncing)
return; return;
@ -722,11 +721,11 @@ Pool.prototype.stopSync = function stopSync() {
this.stopInterval(); this.stopInterval();
this.stopTimeout(); this.stopTimeout();
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.syncSent = false; if (!peer.outbound || peer.pending)
continue;
for (i = 0; i < this.peers.outbound.length; i++) peer.syncSent = false;
this.peers.outbound[i].syncSent = false; }
}; };
/** /**
@ -991,9 +990,9 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
this.chain.total, this.chain.total,
this.chain.orphan.count, this.chain.orphan.count,
this.activeBlocks, this.activeBlocks,
peer.queueBlock.length, peer.queueBlock.size,
block.bits, block.bits,
this.peers.all.length, this.peers.size(),
this.chain.locker.pending.length, this.chain.locker.pending.length,
this.chain.locker.jobs.length); this.chain.locker.jobs.length);
} }
@ -1011,13 +1010,13 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
*/ */
Pool.prototype.sendMempool = function sendMempool() { Pool.prototype.sendMempool = function sendMempool() {
var i; var peer;
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.sendMempool(); if (!peer.outbound || peer.pending)
continue;
for (i = 0; i < this.peers.outbound.length; i++) peer.sendMempool();
this.peers.outbound[i].sendMempool(); }
}; };
/** /**
@ -1026,16 +1025,10 @@ Pool.prototype.sendMempool = function sendMempool() {
*/ */
Pool.prototype.sendAlert = function sendAlert(alert) { Pool.prototype.sendAlert = function sendAlert(alert) {
var i; var peer;
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next)
this.peers.load.sendAlert(alert); peer.sendAlert(alert);
for (i = 0; i < this.peers.outbound.length; i++)
this.peers.outbound[i].sendAlert(alert);
for (i = 0; i < this.peers.inbound.length; i++)
this.peers.inbound[i].sendAlert(alert);
}; };
/** /**
@ -1077,7 +1070,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
self.stopInterval(); self.stopInterval();
self.stopTimeout(); self.stopTimeout();
if (self.peers.size() === 0) { if (self.peers.outbound === 0) {
self.logger.warning('%s %s %s', self.logger.warning('%s %s %s',
'Could not connect to any peers.', 'Could not connect to any peers.',
'Do you have a network connection?', 'Do you have a network connection?',
@ -1415,7 +1408,7 @@ Pool.prototype.addLeech = function addLeech(addr, socket) {
this.logger.info('Added leech peer (%s).', peer.hostname); this.logger.info('Added leech peer (%s).', peer.hostname);
this.peers.addLeech(peer); this.peers.add(peer);
util.nextTick(function() { util.nextTick(function() {
self.emit('leech', peer); self.emit('leech', peer);
@ -1435,7 +1428,7 @@ Pool.prototype.addPeer = function addPeer() {
if (!this.loaded) if (!this.loaded)
return; return;
if (this.peers.isFull()) if (this.peers.outbound >= this.maxOutbound)
return; return;
// Hang back if we don't have a loader peer yet. // Hang back if we don't have a loader peer yet.
@ -1449,7 +1442,7 @@ Pool.prototype.addPeer = function addPeer() {
peer = this.createPeer(addr); peer = this.createPeer(addr);
this.peers.addPending(peer); this.peers.add(peer);
util.nextTick(function() { util.nextTick(function() {
self.emit('peer', peer); self.emit('peer', peer);
@ -1465,7 +1458,7 @@ Pool.prototype.fillPeers = function fillPeers() {
var i; var i;
this.logger.debug('Refilling peers (%d/%d).', this.logger.debug('Refilling peers (%d/%d).',
this.peers.all.length - this.peers.inbound.length, this.peers.outbound,
this.maxOutbound); this.maxOutbound);
for (i = 0; i < this.maxOutbound - 1; i++) for (i = 0; i < this.maxOutbound - 1; i++)
@ -1541,19 +1534,18 @@ Pool.prototype.unwatch = function unwatch() {
Pool.prototype.updateWatch = function updateWatch() { Pool.prototype.updateWatch = function updateWatch() {
var self = this; var self = this;
var i; var peer;
if (this.pendingWatch != null) if (this.pendingWatch != null)
return; return;
this.pendingWatch = setTimeout(function() { this.pendingWatch = setTimeout(function() {
self.pendingWatch = null; self.pendingWatch = null;
for (peer = self.peers.head(); peer; peer = peer.next) {
if (self.peers.load) if (!peer.outbound || peer.pending)
self.peers.load.updateWatch(); continue;
peer.updateWatch();
for (i = 0; i < self.peers.outbound.length; i++) }
self.peers.outbound[i].updateWatch();
}, 50); }, 50);
}; };
@ -1634,16 +1626,15 @@ Pool.prototype.getTX = function getTX(peer, hash) {
item = new LoadRequest(this, peer, this.txType, hash); item = new LoadRequest(this, peer, this.txType, hash);
if (peer.queueTX.length === 0) { if (peer.queueTX.size === 0) {
util.nextTick(function() { util.nextTick(function() {
self.logger.debug( self.logger.debug(
'Requesting %d/%d txs from peer with getdata (%s).', 'Requesting %d/%d txs from peer with getdata (%s).',
peer.queueTX.length, peer.queueTX.size,
self.activeTX, self.activeTX,
peer.hostname); peer.hostname);
peer.getData(peer.queueTX); peer.getData(peer.queueTX.slice());
peer.queueTX.length = 0;
}); });
} }
@ -1709,29 +1700,29 @@ Pool.prototype.scheduleRequests = co(function* scheduleRequests(peer) {
*/ */
Pool.prototype.sendRequests = function sendRequests(peer) { Pool.prototype.sendRequests = function sendRequests(peer) {
var i, size, items; var i, size, items, item;
if (peer.queueBlock.length === 0) if (peer.queueBlock.size === 0)
return; return;
if (this.options.spv) { if (this.options.spv) {
if (this.activeBlocks >= 500) if (this.activeBlocks >= 2000)
return; return;
items = peer.queueBlock.slice(); size = peer.queueBlock.size;
peer.queueBlock.length = 0;
} else { } else {
size = this.network.getBatchSize(this.chain.height); size = this.network.getBatchSize(this.chain.height);
if (this.activeBlocks >= size) if (this.activeBlocks >= size)
return; return;
items = peer.queueBlock.slice(0, size);
peer.queueBlock = peer.queueBlock.slice(size);
} }
for (i = 0; i < items.length; i++) items = peer.queueBlock.slice(size);
items[i] = items[i].start();
for (i = 0; i < items.length; i++) {
item = items[i];
item.start();
}
this.logger.debug( this.logger.debug(
'Requesting %d/%d blocks from peer with getdata (%s).', 'Requesting %d/%d blocks from peer with getdata (%s).',
@ -1799,13 +1790,13 @@ Pool.prototype.broadcast = function broadcast(msg) {
*/ */
Pool.prototype.announce = function announce(msg) { Pool.prototype.announce = function announce(msg) {
var i; var peer;
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.tryAnnounce(msg); if (!peer.outbound || peer.pending)
continue;
for (i = 0; i < this.peers.outbound.length; i++) peer.tryAnnounce(msg);
this.peers.outbound[i].tryAnnounce(msg); }
}; };
/** /**
@ -1814,15 +1805,15 @@ Pool.prototype.announce = function announce(msg) {
*/ */
Pool.prototype.setFeeRate = function setFeeRate(rate) { Pool.prototype.setFeeRate = function setFeeRate(rate) {
var i; var peer;
this.feeRate = rate; this.feeRate = rate;
if (this.peers.load) for (peer = this.peers.head(); peer; peer = peer.next) {
this.peers.load.sendFeeRate(rate); if (!peer.outbound || peer.pending)
continue;
for (i = 0; i < this.peers.outbound.length; i++) peer.sendFeeRate(rate);
this.peers.outbound[i].sendFeeRate(rate); }
}; };
/** /**
@ -1978,52 +1969,48 @@ Pool.prototype.getIP2 = co(function* getIP2() {
function PeerList(pool) { function PeerList(pool) {
this.pool = pool; this.pool = pool;
// Peers that are loading blocks themselves
this.outbound = [];
// Peers that are still connecting
this.pending = [];
// Peers that connected to us
this.inbound = [];
// Peers that are loading block ids
this.load = null;
// All peers
this.all = [];
// Map of hostnames
this.map = {}; this.map = {};
this.list = new List();
this.load = null;
this.inbound = 0;
this.outbound = 0;
this.pending = 0;
} }
PeerList.prototype.addLoader = function addLoader(peer) { PeerList.prototype.head = function head() {
this.load = peer; return this.list.head;
this.all.push(peer);
assert(!this.map[peer.hostname]);
this.map[peer.hostname] = peer;
}; };
PeerList.prototype.addPending = function addPending(peer) { PeerList.prototype.tail = function tail() {
this.pending.push(peer); return this.list.tail;
this.all.push(peer);
assert(!this.map[peer.hostname]);
this.map[peer.hostname] = peer;
}; };
PeerList.prototype.addLeech = function addLeech(peer) { PeerList.prototype.size = function size() {
this.inbound.push(peer); return this.list.size;
this.all.push(peer);
assert(!this.map[peer.hostname]);
this.map[peer.hostname] = peer;
}; };
PeerList.prototype.promote = function promote(peer) { PeerList.prototype.promote = function promote(peer) {
if (util.binaryRemove(this.pending, peer, compare)) assert(peer.outbound);
util.binaryInsert(this.outbound, peer, compare); assert(peer.pending);
peer.pending = false;
this.pending--;
};
PeerList.prototype.add = function add(peer) {
assert(this.list.push(peer));
assert(!this.map[peer.hostname]);
this.map[peer.hostname] = peer;
if (peer.outbound) {
this.outbound++;
if (peer.pending)
this.pending++;
} else {
this.inbound++;
}
}; };
PeerList.prototype.remove = function remove(peer) { PeerList.prototype.remove = function remove(peer) {
util.binaryRemove(this.pending, peer, compare); assert(this.list.remove(peer));
util.binaryRemove(this.outbound, peer, compare);
util.binaryRemove(this.inbound, peer, compare);
util.binaryRemove(this.all, peer, compare);
assert(this.map[peer.hostname]); assert(this.map[peer.hostname]);
delete this.map[peer.hostname]; delete this.map[peer.hostname];
@ -2031,66 +2018,38 @@ PeerList.prototype.remove = function remove(peer) {
this.pool.logger.info('Removed loader peer (%s).', peer.hostname); this.pool.logger.info('Removed loader peer (%s).', peer.hostname);
this.load = null; this.load = null;
} }
};
PeerList.prototype.demoteLoader = function demoteLoader() { if (peer.outbound) {
var peer = this.load; this.outbound--;
assert(peer); if (peer.pending)
this.load = null; this.pending--;
if (peer.ack) } else {
util.binaryInsert(this.outbound, peer, compare); this.inbound--;
else }
util.binaryInsert(this.pending, peer, compare);
}; };
PeerList.prototype.repurpose = function repurpose(peer) { PeerList.prototype.repurpose = function repurpose(peer) {
var r1, r2;
assert(peer.outbound); assert(peer.outbound);
if (this.load)
this.demoteLoader();
r1 = util.binaryRemove(this.pending, peer, compare);
r2 = util.binaryRemove(this.outbound, peer, compare);
assert(r1 || r2);
this.load = peer; this.load = peer;
}; };
PeerList.prototype.isFull = function isFull() {
return this.size() >= this.pool.maxOutbound - 1;
};
PeerList.prototype.size = function size() {
return this.outbound.length + this.pending.length;
};
PeerList.prototype.get = function get(addr) { PeerList.prototype.get = function get(addr) {
return this.map[addr.hostname]; return this.map[addr.hostname];
}; };
PeerList.prototype.destroy = function destroy() { PeerList.prototype.destroy = function destroy() {
var i, peers; var peer, next;
if (this.load) this.map = {};
this.load.destroy(); this.load = null;
this.inbound = 0;
this.outbound = 0;
this.pending = 0;
peers = this.outbound.slice(); for (peer = this.list.head; peer; peer = next) {
next = peer.next;
for (i = 0; i < peers.length; i++) peer.destroy();
peers[i].destroy(); }
peers = this.pending.slice();
for (i = 0; i < peers.length; i++)
peers[i].destroy();
peers = this.inbound.slice();
for (i = 0; i < peers.length; i++)
peers[i].destroy();
}; };
/** /**
@ -2341,10 +2300,12 @@ function LoadRequest(pool, peer, type, hash) {
this.type = type; this.type = type;
this.hash = hash; this.hash = hash;
this.active = false; this.active = false;
this.id = this.pool.uid++;
this.timeout = null; this.timeout = null;
this.onTimeout = this._onTimeout.bind(this); this.onTimeout = this._onTimeout.bind(this);
this.next = null;
this.prev = null;
assert(!this.pool.requestMap[this.hash]); assert(!this.pool.requestMap[this.hash]);
this.pool.requestMap[this.hash] = this; this.pool.requestMap[this.hash] = this;
} }
@ -2408,9 +2369,9 @@ LoadRequest.prototype.finish = function finish() {
} }
if (this.type === this.pool.txType) if (this.type === this.pool.txType)
util.binaryRemove(this.peer.queueTX, this, compare); this.peer.queueTX.remove(this);
else else
util.binaryRemove(this.peer.queueBlock, this, compare); this.peer.queueBlock.remove(this);
if (this.timeout != null) { if (this.timeout != null) {
clearTimeout(this.timeout); clearTimeout(this.timeout);
@ -2502,7 +2463,7 @@ BroadcastItem.prototype.start = function start() {
assert(!this.pool.invMap[this.hash], 'Already started.'); assert(!this.pool.invMap[this.hash], 'Already started.');
this.pool.invMap[this.hash] = this; this.pool.invMap[this.hash] = this;
util.binaryInsert(this.pool.invItems, this, compare); assert(this.pool.invItems.push(this));
this.refresh(); this.refresh();
@ -2550,7 +2511,7 @@ BroadcastItem.prototype.finish = function finish(err) {
this.timeout = null; this.timeout = null;
delete this.pool.invMap[this.hash]; delete this.pool.invMap[this.hash];
util.binaryRemove(this.pool.invItems, this, compare); assert(this.pool.invItems.remove(this));
for (i = 0; i < this.callback.length; i++) for (i = 0; i < this.callback.length; i++)
this.callback[i](err); this.callback[i](err);

237
lib/utils/list.js Normal file
View File

@ -0,0 +1,237 @@
'use strict';
var assert = require('assert');
/**
* A linked list.
* @exports List
* @constructor
*/
function List() {
if (!(this instanceof List))
return new List();
this.head = null;
this.tail = null;
this.size = 0;
}
/**
* Reset the cache. Clear all items.
*/
List.prototype.reset = function reset() {
var item, next;
for (item = this.head; item; item = next) {
next = item.next;
item.prev = null;
item.next = null;
}
assert(!item);
this.head = null;
this.tail = null;
this.size = 0;
};
/**
* Remove the first item in the list.
*/
List.prototype.shift = function shift() {
var item = this.head;
if (!item)
return;
this.remove(item);
return item;
};
/**
* Prepend an item to the linked list (sets new head).
* @private
* @param {ListItem}
*/
List.prototype.unshift = function unshift(item) {
return this.insert(null, item);
};
/**
* Append an item to the linked list (sets new tail).
* @private
* @param {ListItem}
*/
List.prototype.push = function push(item) {
return this.insert(this.tail, item);
};
/**
* Remove the last item in the list.
*/
List.prototype.pop = function pop() {
var item = this.tail;
if (!item)
return;
this.remove(item);
return item;
};
/**
* Insert item into the linked list.
* @private
* @param {ListItem|null} ref
* @param {ListItem} item
*/
List.prototype.insert = function insert(ref, item) {
if (item.prev || item.next || item === this.head)
return false;
assert(!item.prev);
assert(!item.next);
if (ref == null) {
if (!this.head) {
this.head = item;
this.tail = item;
} else {
this.head.prev = item;
item.next = this.head;
this.head = item;
}
this.size++;
return true;
}
item.next = ref.next;
item.prev = ref;
ref.next = item;
if (ref === this.tail)
this.tail = item;
this.size++;
return true;
};
/**
* Remove item from the linked list.
* @private
* @param {ListItem}
*/
List.prototype.remove = function remove(item) {
if (!item.prev && !item.next && item !== this.head)
return false;
if (item.prev)
item.prev.next = item.next;
if (item.next)
item.next.prev = item.prev;
if (item === this.head)
this.head = item.next;
if (item === this.tail)
this.tail = item.prev || this.head;
if (!this.head)
assert(!this.tail);
if (!this.tail)
assert(!this.head);
item.prev = null;
item.next = null;
this.size--;
return true;
};
/**
* Slice the list to an array of items.
* @param {Number} total
* @returns {Object[]}
*/
List.prototype.slice = function slice(total) {
var items = [];
var item, next;
if (total == null)
total = Infinity;
for (item = this.head; item; item = next) {
next = item.next;
item.prev = null;
item.next = null;
this.size--;
items.push(item);
if (items.length === total)
break;
}
if (next) {
this.head = next;
next.prev = null;
} else {
this.head = null;
this.tail = null;
}
return items;
};
/**
* Convert the list to an array of items.
* @returns {Object[]}
*/
List.prototype.toArray = function toArray() {
var items = [];
var item;
for (item = this.head; item; item = item.next)
items.push(item);
return items;
};
/**
* Represents an LRU item.
* @constructor
* @private
* @param {String} key
* @param {Object} value
*/
function ListItem(value) {
this.next = null;
this.prev = null;
}
/*
* Expose
*/
exports = List;
exports.Item = ListItem;
module.exports = exports;