svmq
Version:
Native System V message queues in Node.js
180 lines (137 loc) • 4.21 kB
JavaScript
;
let EventEmitter = require('events').EventEmitter;
let inherits = require('util').inherits;
let msg = require('../build/Release/msg');
let MessageQueue = function(key, perms, options) {
if (typeof key !== 'number') {
throw new Error('Key must be a number. If you\'re providing a path, use ftok first.')
}
if (typeof perms === 'undefined') {
perms = 0x666;
} else if (typeof perms !== 'number') {
throw new Error('Permissions must be in number form, i.e. 0x666');
}
this.key = key;
this.perms = perms;
this.flags = formatFlags(perms);
this.options = options || { };
this.closed = false;
this.receiveBuffer = [];
this.on('newListener', (event) => {
if (event === 'data') {
setImmediate(() => this._beginReceive());
}
});
this._open();
};
inherits(MessageQueue, EventEmitter);
MessageQueue.open = (key, perms) => new MessageQueue(key, perms);
MessageQueue.prototype._open = function() {
if (this.id >= 0) throw new Error('Queue already open');
this.id = msg.get(this.key, this.flags);
};
MessageQueue.prototype.push = function(data, options, callback) {
if (!Buffer.isBuffer(data)) {
data = new Buffer(data);
}
if (typeof options == 'function') {
callback = options;
options = { };
}
if (typeof callback === 'undefined') {
callback = Function.prototype;
}
options = options || { };
if (typeof options.type === 'undefined') {
options.type = 1;
} else if (typeof options.type !== 'number' || options.type <= 0) {
return callback(new TypeError('Message type must be a positive nonzero integer or undefined'));
}
if (typeof options.flags === 'undefined') {
options.flags = 0;
} else if (typeof options.flags !== 'number') {
return callback(new Error('Message flags must be a number or undefined'));
}
msg.snd(this.id, data, options.type, options.flags, callback);
};
MessageQueue.prototype._beginReceive = function() {
if (this.listenerCount('data') === 0) return;
// TODO
let options = { };
this._receive(options, (err, data) => {
if (err) {
this.emit('error', err);
return;
}
if (this.listenerCount('data') > 0) {
this.emit('data', data);
} else {
this.receiveBuffer.push(data);
}
this._beginReceive();
});
};
MessageQueue.prototype._receive = function(options, callback) {
options = options || { };
if (typeof options.type === 'undefined') {
options.type = 0; // pop
} else if (typeof options.type !== 'number') {
return callback(new TypeError('Message type must be a positive nonzero integer or undefined'));
}
if (typeof options.flags === 'undefined') {
options.flags = 0;
}
if (this.receiveBuffer.length > 0) {
return callback(null, this.receiveBuffer.shift());
}
// TODO: handle buffered messages with different types
msg.rcv(this.id, MSGMAX, options.type, options.flags, callback);
};
MessageQueue.prototype.pop = function(options, callback) {
if (this.listenerCount('data') > 0) {
return callback(new Error('Do not use \'data\' event with pop()'));
}
if (typeof options === 'function') {
callback = options;
options = { };
}
if (typeof callback === 'undefined') {
callback = (err) => {
if (err) throw err;
};
}
options = options || { };
this._receive(options, callback);
};
MessageQueue.prototype.close = function(callback) {
callback = callback || Function.prototype;
if (this.closed) {
callback(null, true);
return true;
}
try {
this.closed = msg.close(this.id)
} catch (err) {
callback(err, false);
return false;
}
callback(null, true);
return true;
};
function formatFlags(perms) {
let uPerms = (perms & 0x006);
let gPerms = (perms & 0x060) >> 1;
let oPerms = (perms & 0x600) >> 2;
perms = uPerms | gPerms | oPerms;
let flags = 0;
flags |= MessageQueue.IPC_CREATE;
flags |= perms;
return flags;
}
// Static members and constants
const IPC_CREATE = MessageQueue.IPC_CREATE = 512;
const MSGMAX = MessageQueue.MSGMAX = 4052;
MessageQueue.formatFlags = formatFlags;
// Export native bindings
MessageQueue.msg = msg;
module.exports = MessageQueue;