workers: classify.
This commit is contained in:
parent
f313ca166d
commit
bad24a6f31
@ -10,86 +10,89 @@ const assert = require('assert');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* Child
|
||||
* Represents a child process.
|
||||
* @alias module:workers.Child
|
||||
* @constructor
|
||||
* @extends EventEmitter
|
||||
* @ignore
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
function Child(file) {
|
||||
if (!(this instanceof Child))
|
||||
return new Child(file);
|
||||
class Child extends EventEmitter {
|
||||
/**
|
||||
* Represents a child process.
|
||||
* @constructor
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor(file) {
|
||||
super();
|
||||
|
||||
this.init(file);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Child.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Test whether child process support is available.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Child.hasSupport = function hasSupport() {
|
||||
return typeof global.postMessage === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize child process. Bind to events.
|
||||
* @private
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
Child.prototype.init = function init(file) {
|
||||
this.child = new global.Worker(file);
|
||||
|
||||
this.child.onerror = (event) => {
|
||||
this.emit('error', new Error('Child error.'));
|
||||
this.emit('exit', 1, null);
|
||||
};
|
||||
|
||||
this.child.onmessage = (event) => {
|
||||
let data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = Buffer.from(event.data, 'hex');
|
||||
assert(data.length === event.data.length / 2);
|
||||
} else {
|
||||
assert(event.data && typeof event.data === 'object');
|
||||
assert(event.data.data && typeof event.data.data.length === 'number');
|
||||
data = event.data.data;
|
||||
data.__proto__ = Buffer.prototype;
|
||||
}
|
||||
this.emit('data', data);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to child process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Child.prototype.write = function write(data) {
|
||||
if (this.child.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
this.child.postMessage({ data }, [data]);
|
||||
} else {
|
||||
this.child.postMessage(data.toString('hex'));
|
||||
this.init(file);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the child process.
|
||||
*/
|
||||
/**
|
||||
* Test whether child process support is available.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Child.prototype.destroy = function destroy() {
|
||||
this.child.terminate();
|
||||
this.emit('exit', 15 | 0x80, 'SIGTERM');
|
||||
};
|
||||
static hasSupport() {
|
||||
return typeof global.postMessage === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize child process. Bind to events.
|
||||
* @private
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
init(file) {
|
||||
this.child = new global.Worker(file);
|
||||
|
||||
this.child.onerror = (event) => {
|
||||
this.emit('error', new Error('Child error.'));
|
||||
this.emit('exit', 1, null);
|
||||
};
|
||||
|
||||
this.child.onmessage = (event) => {
|
||||
let data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = Buffer.from(event.data, 'hex');
|
||||
assert(data.length === event.data.length / 2);
|
||||
} else {
|
||||
assert(event.data && typeof event.data === 'object');
|
||||
assert(event.data.data && typeof event.data.data.length === 'number');
|
||||
data = event.data.data;
|
||||
data.__proto__ = Buffer.prototype;
|
||||
}
|
||||
this.emit('data', data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to child process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
write(data) {
|
||||
if (this.child.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
this.child.postMessage({ data }, [data]);
|
||||
} else {
|
||||
this.child.postMessage(data.toString('hex'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the child process.
|
||||
*/
|
||||
|
||||
destroy() {
|
||||
this.child.terminate();
|
||||
this.emit('exit', 15 | 0x80, 'SIGTERM');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -14,97 +14,101 @@ const children = new Set();
|
||||
let exitBound = false;
|
||||
|
||||
/**
|
||||
* Child
|
||||
* Represents a child process.
|
||||
* @alias module:workers.Child
|
||||
* @constructor
|
||||
* @param {String} file
|
||||
* @extends EventEmitter
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
function Child(file) {
|
||||
if (!(this instanceof Child))
|
||||
return new Child(file);
|
||||
class Child extends EventEmitter {
|
||||
/**
|
||||
* Represents a child process.
|
||||
* @constructor
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor(file) {
|
||||
super();
|
||||
|
||||
bindExit();
|
||||
children.add(this);
|
||||
bindExit();
|
||||
children.add(this);
|
||||
|
||||
this.init(file);
|
||||
this.init(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether child process support is available.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
static hasSupport() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize child process (node.js).
|
||||
* @private
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
init(file) {
|
||||
const bin = process.argv[0];
|
||||
const filename = path.resolve(__dirname, file);
|
||||
const options = { stdio: 'pipe', env: process.env };
|
||||
|
||||
this.child = cp.spawn(bin, [filename], options);
|
||||
|
||||
this.child.unref();
|
||||
this.child.stdin.unref();
|
||||
this.child.stdout.unref();
|
||||
this.child.stderr.unref();
|
||||
|
||||
this.child.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.once('exit', (code, signal) => {
|
||||
children.delete(this);
|
||||
this.emit('exit', code == null ? -1 : code, signal);
|
||||
});
|
||||
|
||||
this.child.stdin.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stdout.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stderr.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stdout.on('data', (data) => {
|
||||
this.emit('data', data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to child process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
write(data) {
|
||||
return this.child.stdin.write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the child process.
|
||||
*/
|
||||
|
||||
destroy() {
|
||||
this.child.kill('SIGTERM');
|
||||
}
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Child.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Test whether child process support is available.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Child.hasSupport = function hasSupport() {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize child process (node.js).
|
||||
* @private
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
Child.prototype.init = function init(file) {
|
||||
const bin = process.argv[0];
|
||||
const filename = path.resolve(__dirname, file);
|
||||
const options = { stdio: 'pipe', env: process.env };
|
||||
|
||||
this.child = cp.spawn(bin, [filename], options);
|
||||
|
||||
this.child.unref();
|
||||
this.child.stdin.unref();
|
||||
this.child.stdout.unref();
|
||||
this.child.stderr.unref();
|
||||
|
||||
this.child.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.once('exit', (code, signal) => {
|
||||
children.delete(this);
|
||||
this.emit('exit', code == null ? -1 : code, signal);
|
||||
});
|
||||
|
||||
this.child.stdin.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stdout.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stderr.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.child.stdout.on('data', (data) => {
|
||||
this.emit('data', data);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to child process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Child.prototype.write = function write(data) {
|
||||
return this.child.stdin.write(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the child process.
|
||||
*/
|
||||
|
||||
Child.prototype.destroy = function destroy() {
|
||||
this.child.kill('SIGTERM');
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleanup all child processes.
|
||||
* @private
|
||||
|
||||
@ -12,32 +12,35 @@ const bio = require('bufio');
|
||||
/**
|
||||
* Framer
|
||||
* @alias module:workers.Framer
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function Framer() {
|
||||
if (!(this instanceof Framer))
|
||||
return new Framer();
|
||||
class Framer {
|
||||
/**
|
||||
* Create a framer.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
constructor() {}
|
||||
|
||||
packet(payload) {
|
||||
const size = 10 + payload.getSize();
|
||||
const bw = bio.write(size);
|
||||
|
||||
bw.writeU32(payload.id);
|
||||
bw.writeU8(payload.cmd);
|
||||
bw.seek(4);
|
||||
|
||||
payload.toWriter(bw);
|
||||
|
||||
bw.writeU8(0x0a);
|
||||
|
||||
const msg = bw.render();
|
||||
msg.writeUInt32LE(msg.length - 10, 5, true);
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
Framer.prototype.packet = function packet(payload) {
|
||||
const size = 10 + payload.getSize();
|
||||
const bw = bio.write(size);
|
||||
|
||||
bw.writeU32(payload.id);
|
||||
bw.writeU8(payload.cmd);
|
||||
bw.seek(4);
|
||||
|
||||
payload.toWriter(bw);
|
||||
|
||||
bw.writeU8(0x0a);
|
||||
|
||||
const msg = bw.render();
|
||||
msg.writeUInt32LE(msg.length - 10, 5, true);
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -18,174 +18,177 @@ const packets = require('./packets');
|
||||
const Parent = require('./parent');
|
||||
|
||||
/**
|
||||
* Master
|
||||
* Represents the master process.
|
||||
* @alias module:workers.Master
|
||||
* @constructor
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
|
||||
function Master() {
|
||||
if (!(this instanceof Master))
|
||||
return new Master();
|
||||
class Master extends EventEmitter {
|
||||
/**
|
||||
* Create the master process.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.parent = new Parent();
|
||||
this.framer = new Framer();
|
||||
this.parser = new Parser();
|
||||
this.listening = false;
|
||||
this.color = false;
|
||||
this.parent = new Parent();
|
||||
this.framer = new Framer();
|
||||
this.parser = new Parser();
|
||||
this.listening = false;
|
||||
this.color = false;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Master.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Initialize master. Bind events.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Master.prototype.init = function init() {
|
||||
this.parent.on('data', (data) => {
|
||||
this.parser.feed(data);
|
||||
});
|
||||
|
||||
this.parent.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.parent.on('exception', (err) => {
|
||||
this.send(new packets.ErrorPacket(err));
|
||||
setTimeout(() => this.destroy(), 1000);
|
||||
});
|
||||
|
||||
this.parser.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.parser.on('packet', (packet) => {
|
||||
this.emit('packet', packet);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set environment.
|
||||
* @param {Object} env
|
||||
*/
|
||||
|
||||
Master.prototype.setEnv = function setEnv(env) {
|
||||
this.color = env.BCOIN_WORKER_ISTTY === '1';
|
||||
this.set(env.BCOIN_WORKER_NETWORK);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set primary network.
|
||||
* @param {NetworkType|Network} network
|
||||
*/
|
||||
|
||||
Master.prototype.set = function set(network) {
|
||||
return Network.set(network);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to worker.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Master.prototype.write = function write(data) {
|
||||
return this.parent.write(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Frame and send a packet.
|
||||
* @param {Packet} packet
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Master.prototype.send = function send(packet) {
|
||||
return this.write(this.framer.packet(packet));
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit an event on the worker side.
|
||||
* @param {String} event
|
||||
* @param {...Object} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Master.prototype.sendEvent = function sendEvent(...items) {
|
||||
return this.send(new packets.EventPacket(items));
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the worker.
|
||||
*/
|
||||
|
||||
Master.prototype.destroy = function destroy() {
|
||||
return this.parent.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a message to stdout in the master process.
|
||||
* @param {Object|String} obj
|
||||
* @param {...String} args
|
||||
*/
|
||||
|
||||
Master.prototype.log = function log() {
|
||||
const text = format.apply(null, arguments);
|
||||
this.send(new packets.LogPacket(text));
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for messages from master process (only if worker).
|
||||
*/
|
||||
|
||||
Master.prototype.listen = function listen() {
|
||||
assert(!this.listening, 'Already listening.');
|
||||
|
||||
this.listening = true;
|
||||
|
||||
this.on('error', (err) => {
|
||||
this.send(new packets.ErrorPacket(err));
|
||||
});
|
||||
|
||||
this.on('packet', (packet) => {
|
||||
try {
|
||||
this.handlePacket(packet);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle packet.
|
||||
* @private
|
||||
* @param {Packet}
|
||||
*/
|
||||
|
||||
Master.prototype.handlePacket = function handlePacket(packet) {
|
||||
let result;
|
||||
|
||||
switch (packet.cmd) {
|
||||
case packets.types.ENV:
|
||||
this.setEnv(packet.env);
|
||||
break;
|
||||
case packets.types.EVENT:
|
||||
this.emit('event', packet.items);
|
||||
this.emit(...packet.items);
|
||||
break;
|
||||
case packets.types.ERROR:
|
||||
this.emit('error', packet.error);
|
||||
break;
|
||||
default:
|
||||
result = jobs.execute(packet);
|
||||
result.id = packet.id;
|
||||
this.send(result);
|
||||
break;
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize master. Bind events.
|
||||
* @private
|
||||
*/
|
||||
|
||||
init() {
|
||||
this.parent.on('data', (data) => {
|
||||
this.parser.feed(data);
|
||||
});
|
||||
|
||||
this.parent.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.parent.on('exception', (err) => {
|
||||
this.send(new packets.ErrorPacket(err));
|
||||
setTimeout(() => this.destroy(), 1000);
|
||||
});
|
||||
|
||||
this.parser.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.parser.on('packet', (packet) => {
|
||||
this.emit('packet', packet);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set environment.
|
||||
* @param {Object} env
|
||||
*/
|
||||
|
||||
setEnv(env) {
|
||||
this.color = env.BCOIN_WORKER_ISTTY === '1';
|
||||
this.set(env.BCOIN_WORKER_NETWORK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set primary network.
|
||||
* @param {NetworkType|Network} network
|
||||
*/
|
||||
|
||||
set(network) {
|
||||
return Network.set(network);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to worker.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
write(data) {
|
||||
return this.parent.write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame and send a packet.
|
||||
* @param {Packet} packet
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
send(packet) {
|
||||
return this.write(this.framer.packet(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event on the worker side.
|
||||
* @param {String} event
|
||||
* @param {...Object} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
sendEvent(...items) {
|
||||
return this.send(new packets.EventPacket(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the worker.
|
||||
*/
|
||||
|
||||
destroy() {
|
||||
return this.parent.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message to stdout in the master process.
|
||||
* @param {Object|String} obj
|
||||
* @param {...String} args
|
||||
*/
|
||||
|
||||
log() {
|
||||
const text = format.apply(null, arguments);
|
||||
this.send(new packets.LogPacket(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for messages from master process (only if worker).
|
||||
*/
|
||||
|
||||
listen() {
|
||||
assert(!this.listening, 'Already listening.');
|
||||
|
||||
this.listening = true;
|
||||
|
||||
this.on('error', (err) => {
|
||||
this.send(new packets.ErrorPacket(err));
|
||||
});
|
||||
|
||||
this.on('packet', (packet) => {
|
||||
try {
|
||||
this.handlePacket(packet);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle packet.
|
||||
* @private
|
||||
* @param {Packet}
|
||||
*/
|
||||
|
||||
handlePacket(packet) {
|
||||
let result;
|
||||
|
||||
switch (packet.cmd) {
|
||||
case packets.types.ENV:
|
||||
this.setEnv(packet.env);
|
||||
break;
|
||||
case packets.types.EVENT:
|
||||
this.emit('event', packet.items);
|
||||
this.emit(...packet.items);
|
||||
break;
|
||||
case packets.types.ERROR:
|
||||
this.emit('error', packet.error);
|
||||
break;
|
||||
default:
|
||||
result = jobs.execute(packet);
|
||||
result.id = packet.id;
|
||||
this.send(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,71 +10,74 @@ const assert = require('assert');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* Parent
|
||||
* Represents the parent process.
|
||||
* @alias module:workers.Parent
|
||||
* @constructor
|
||||
* @extends EventEmitter
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
function Parent() {
|
||||
if (!(this instanceof Parent))
|
||||
return new Parent();
|
||||
class Parent extends EventEmitter {
|
||||
/**
|
||||
* Create the parent process.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Parent.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Initialize master (web workers).
|
||||
* @private
|
||||
*/
|
||||
|
||||
Parent.prototype.init = function init() {
|
||||
global.onerror = (event) => {
|
||||
this.emit('error', new Error('Worker error.'));
|
||||
};
|
||||
|
||||
global.onmessage = (event) => {
|
||||
let data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = Buffer.from(event.data, 'hex');
|
||||
assert(data.length === event.data.length / 2);
|
||||
} else {
|
||||
assert(event.data && typeof event.data === 'object');
|
||||
assert(event.data.data && typeof event.data.data.length === 'number');
|
||||
data = event.data.data;
|
||||
data.__proto__ = Buffer.prototype;
|
||||
}
|
||||
this.emit('data', data);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to parent process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Parent.prototype.write = function write(data) {
|
||||
if (global.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
global.postMessage({ data }, [data]);
|
||||
} else {
|
||||
global.postMessage(data.toString('hex'));
|
||||
this.init();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the parent process.
|
||||
*/
|
||||
/**
|
||||
* Initialize master (web workers).
|
||||
* @private
|
||||
*/
|
||||
|
||||
Parent.prototype.destroy = function destroy() {
|
||||
global.close();
|
||||
};
|
||||
init() {
|
||||
global.onerror = (event) => {
|
||||
this.emit('error', new Error('Worker error.'));
|
||||
};
|
||||
|
||||
global.onmessage = (event) => {
|
||||
let data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = Buffer.from(event.data, 'hex');
|
||||
assert(data.length === event.data.length / 2);
|
||||
} else {
|
||||
assert(event.data && typeof event.data === 'object');
|
||||
assert(event.data.data && typeof event.data.data.length === 'number');
|
||||
data = event.data.data;
|
||||
data.__proto__ = Buffer.prototype;
|
||||
}
|
||||
this.emit('data', data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to parent process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
write(data) {
|
||||
if (global.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
global.postMessage({ data }, [data]);
|
||||
} else {
|
||||
global.postMessage(data.toString('hex'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the parent process.
|
||||
*/
|
||||
|
||||
destroy() {
|
||||
global.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -9,60 +9,63 @@
|
||||
const EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* Parent
|
||||
* Represents the parent process.
|
||||
* @alias module:workers.Parent
|
||||
* @constructor
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
|
||||
function Parent() {
|
||||
if (!(this instanceof Parent))
|
||||
return new Parent();
|
||||
class Parent extends EventEmitter {
|
||||
/**
|
||||
* Create the parent process.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.init();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize master (node.js).
|
||||
* @private
|
||||
*/
|
||||
|
||||
init() {
|
||||
process.stdin.on('data', (data) => {
|
||||
this.emit('data', data);
|
||||
});
|
||||
|
||||
// Nowhere to send these errors:
|
||||
process.stdin.on('error', () => {});
|
||||
process.stdout.on('error', () => {});
|
||||
process.stderr.on('error', () => {});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
this.emit('exception', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to parent process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
write(data) {
|
||||
return process.stdout.write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the parent process.
|
||||
*/
|
||||
|
||||
destroy() {
|
||||
return process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Parent.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Initialize master (node.js).
|
||||
* @private
|
||||
*/
|
||||
|
||||
Parent.prototype.init = function init() {
|
||||
process.stdin.on('data', (data) => {
|
||||
this.emit('data', data);
|
||||
});
|
||||
|
||||
// Nowhere to send these errors:
|
||||
process.stdin.on('error', () => {});
|
||||
process.stdout.on('error', () => {});
|
||||
process.stderr.on('error', () => {});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
this.emit('exception', err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to parent process.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Parent.prototype.write = function write(data) {
|
||||
return process.stdout.write(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the parent process.
|
||||
*/
|
||||
|
||||
Parent.prototype.destroy = function destroy() {
|
||||
return process.exit(0);
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* workers.js - worker processes for bcoin
|
||||
* parser.js - worker parser for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
@ -14,178 +14,186 @@ const packets = require('./packets');
|
||||
/**
|
||||
* Parser
|
||||
* @alias module:workers.Parser
|
||||
* @constructor
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
|
||||
function Parser() {
|
||||
if (!(this instanceof Parser))
|
||||
return new Parser();
|
||||
class Parser extends EventEmitter {
|
||||
/**
|
||||
* Create a parser.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
EventEmitter.call(this);
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.waiting = 9;
|
||||
this.header = null;
|
||||
this.pending = [];
|
||||
this.total = 0;
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Parser.prototype, EventEmitter.prototype);
|
||||
|
||||
Parser.prototype.feed = function feed(data) {
|
||||
this.total += data.length;
|
||||
this.pending.push(data);
|
||||
|
||||
while (this.total >= this.waiting) {
|
||||
const chunk = this.read(this.waiting);
|
||||
this.parse(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
Parser.prototype.read = function read(size) {
|
||||
assert(this.total >= size, 'Reading too much.');
|
||||
|
||||
if (size === 0)
|
||||
return Buffer.alloc(0);
|
||||
|
||||
const pending = this.pending[0];
|
||||
|
||||
if (pending.length > size) {
|
||||
const chunk = pending.slice(0, size);
|
||||
this.pending[0] = pending.slice(size);
|
||||
this.total -= chunk.length;
|
||||
return chunk;
|
||||
this.waiting = 9;
|
||||
this.header = null;
|
||||
this.pending = [];
|
||||
this.total = 0;
|
||||
}
|
||||
|
||||
if (pending.length === size) {
|
||||
const chunk = this.pending.shift();
|
||||
this.total -= chunk.length;
|
||||
return chunk;
|
||||
feed(data) {
|
||||
this.total += data.length;
|
||||
this.pending.push(data);
|
||||
|
||||
while (this.total >= this.waiting) {
|
||||
const chunk = this.read(this.waiting);
|
||||
this.parse(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
const chunk = Buffer.allocUnsafe(size);
|
||||
let off = 0;
|
||||
read(size) {
|
||||
assert(this.total >= size, 'Reading too much.');
|
||||
|
||||
if (size === 0)
|
||||
return Buffer.alloc(0);
|
||||
|
||||
while (off < chunk.length) {
|
||||
const pending = this.pending[0];
|
||||
const len = pending.copy(chunk, off);
|
||||
if (len === pending.length)
|
||||
this.pending.shift();
|
||||
else
|
||||
this.pending[0] = pending.slice(len);
|
||||
off += len;
|
||||
|
||||
if (pending.length > size) {
|
||||
const chunk = pending.slice(0, size);
|
||||
this.pending[0] = pending.slice(size);
|
||||
this.total -= chunk.length;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
if (pending.length === size) {
|
||||
const chunk = this.pending.shift();
|
||||
this.total -= chunk.length;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
const chunk = Buffer.allocUnsafe(size);
|
||||
let off = 0;
|
||||
|
||||
while (off < chunk.length) {
|
||||
const pending = this.pending[0];
|
||||
const len = pending.copy(chunk, off);
|
||||
if (len === pending.length)
|
||||
this.pending.shift();
|
||||
else
|
||||
this.pending[0] = pending.slice(len);
|
||||
off += len;
|
||||
}
|
||||
|
||||
assert.strictEqual(off, chunk.length);
|
||||
|
||||
this.total -= chunk.length;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
assert.strictEqual(off, chunk.length);
|
||||
parse(data) {
|
||||
let header = this.header;
|
||||
|
||||
this.total -= chunk.length;
|
||||
if (!header) {
|
||||
try {
|
||||
header = this.parseHeader(data);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
};
|
||||
this.header = header;
|
||||
this.waiting = header.size + 1;
|
||||
|
||||
Parser.prototype.parse = function parse(data) {
|
||||
let header = this.header;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!header) {
|
||||
this.waiting = 9;
|
||||
this.header = null;
|
||||
|
||||
let packet;
|
||||
try {
|
||||
header = this.parseHeader(data);
|
||||
packet = this.parsePacket(header, data);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this.header = header;
|
||||
this.waiting = header.size + 1;
|
||||
if (data[data.length - 1] !== 0x0a) {
|
||||
this.emit('error', new Error('No trailing newline.'));
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
packet.id = header.id;
|
||||
|
||||
this.emit('packet', packet);
|
||||
}
|
||||
|
||||
this.waiting = 9;
|
||||
this.header = null;
|
||||
|
||||
let packet;
|
||||
try {
|
||||
packet = this.parsePacket(header, data);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
parseHeader(data) {
|
||||
const id = data.readUInt32LE(0, true);
|
||||
const cmd = data.readUInt8(4, true);
|
||||
const size = data.readUInt32LE(5, true);
|
||||
return new Header(id, cmd, size);
|
||||
}
|
||||
|
||||
if (data[data.length - 1] !== 0x0a) {
|
||||
this.emit('error', new Error('No trailing newline.'));
|
||||
return;
|
||||
parsePacket(header, data) {
|
||||
switch (header.cmd) {
|
||||
case packets.types.ENV:
|
||||
return packets.EnvPacket.fromRaw(data);
|
||||
case packets.types.EVENT:
|
||||
return packets.EventPacket.fromRaw(data);
|
||||
case packets.types.LOG:
|
||||
return packets.LogPacket.fromRaw(data);
|
||||
case packets.types.ERROR:
|
||||
return packets.ErrorPacket.fromRaw(data);
|
||||
case packets.types.ERRORRESULT:
|
||||
return packets.ErrorResultPacket.fromRaw(data);
|
||||
case packets.types.CHECK:
|
||||
return packets.CheckPacket.fromRaw(data);
|
||||
case packets.types.CHECKRESULT:
|
||||
return packets.CheckResultPacket.fromRaw(data);
|
||||
case packets.types.SIGN:
|
||||
return packets.SignPacket.fromRaw(data);
|
||||
case packets.types.SIGNRESULT:
|
||||
return packets.SignResultPacket.fromRaw(data);
|
||||
case packets.types.CHECKINPUT:
|
||||
return packets.CheckInputPacket.fromRaw(data);
|
||||
case packets.types.CHECKINPUTRESULT:
|
||||
return packets.CheckInputResultPacket.fromRaw(data);
|
||||
case packets.types.SIGNINPUT:
|
||||
return packets.SignInputPacket.fromRaw(data);
|
||||
case packets.types.SIGNINPUTRESULT:
|
||||
return packets.SignInputResultPacket.fromRaw(data);
|
||||
case packets.types.ECVERIFY:
|
||||
return packets.ECVerifyPacket.fromRaw(data);
|
||||
case packets.types.ECVERIFYRESULT:
|
||||
return packets.ECVerifyResultPacket.fromRaw(data);
|
||||
case packets.types.ECSIGN:
|
||||
return packets.ECSignPacket.fromRaw(data);
|
||||
case packets.types.ECSIGNRESULT:
|
||||
return packets.ECSignResultPacket.fromRaw(data);
|
||||
case packets.types.MINE:
|
||||
return packets.MinePacket.fromRaw(data);
|
||||
case packets.types.MINERESULT:
|
||||
return packets.MineResultPacket.fromRaw(data);
|
||||
case packets.types.SCRYPT:
|
||||
return packets.ScryptPacket.fromRaw(data);
|
||||
case packets.types.SCRYPTRESULT:
|
||||
return packets.ScryptResultPacket.fromRaw(data);
|
||||
default:
|
||||
throw new Error('Unknown packet.');
|
||||
}
|
||||
}
|
||||
|
||||
packet.id = header.id;
|
||||
|
||||
this.emit('packet', packet);
|
||||
};
|
||||
|
||||
Parser.prototype.parseHeader = function parseHeader(data) {
|
||||
const id = data.readUInt32LE(0, true);
|
||||
const cmd = data.readUInt8(4, true);
|
||||
const size = data.readUInt32LE(5, true);
|
||||
return new Header(id, cmd, size);
|
||||
};
|
||||
|
||||
Parser.prototype.parsePacket = function parsePacket(header, data) {
|
||||
switch (header.cmd) {
|
||||
case packets.types.ENV:
|
||||
return packets.EnvPacket.fromRaw(data);
|
||||
case packets.types.EVENT:
|
||||
return packets.EventPacket.fromRaw(data);
|
||||
case packets.types.LOG:
|
||||
return packets.LogPacket.fromRaw(data);
|
||||
case packets.types.ERROR:
|
||||
return packets.ErrorPacket.fromRaw(data);
|
||||
case packets.types.ERRORRESULT:
|
||||
return packets.ErrorResultPacket.fromRaw(data);
|
||||
case packets.types.CHECK:
|
||||
return packets.CheckPacket.fromRaw(data);
|
||||
case packets.types.CHECKRESULT:
|
||||
return packets.CheckResultPacket.fromRaw(data);
|
||||
case packets.types.SIGN:
|
||||
return packets.SignPacket.fromRaw(data);
|
||||
case packets.types.SIGNRESULT:
|
||||
return packets.SignResultPacket.fromRaw(data);
|
||||
case packets.types.CHECKINPUT:
|
||||
return packets.CheckInputPacket.fromRaw(data);
|
||||
case packets.types.CHECKINPUTRESULT:
|
||||
return packets.CheckInputResultPacket.fromRaw(data);
|
||||
case packets.types.SIGNINPUT:
|
||||
return packets.SignInputPacket.fromRaw(data);
|
||||
case packets.types.SIGNINPUTRESULT:
|
||||
return packets.SignInputResultPacket.fromRaw(data);
|
||||
case packets.types.ECVERIFY:
|
||||
return packets.ECVerifyPacket.fromRaw(data);
|
||||
case packets.types.ECVERIFYRESULT:
|
||||
return packets.ECVerifyResultPacket.fromRaw(data);
|
||||
case packets.types.ECSIGN:
|
||||
return packets.ECSignPacket.fromRaw(data);
|
||||
case packets.types.ECSIGNRESULT:
|
||||
return packets.ECSignResultPacket.fromRaw(data);
|
||||
case packets.types.MINE:
|
||||
return packets.MinePacket.fromRaw(data);
|
||||
case packets.types.MINERESULT:
|
||||
return packets.MineResultPacket.fromRaw(data);
|
||||
case packets.types.SCRYPT:
|
||||
return packets.ScryptPacket.fromRaw(data);
|
||||
case packets.types.SCRYPTRESULT:
|
||||
return packets.ScryptResultPacket.fromRaw(data);
|
||||
default:
|
||||
throw new Error('Unknown packet.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Header
|
||||
* @constructor
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
function Header(id, cmd, size) {
|
||||
this.id = id;
|
||||
this.cmd = cmd;
|
||||
this.size = size;
|
||||
class Header {
|
||||
/**
|
||||
* Create a header.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
constructor(id, cmd, size) {
|
||||
this.id = id;
|
||||
this.cmd = cmd;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user