vox-core
Version:
Runtime de aplicaciones multiplataforma
365 lines (294 loc) • 7.58 kB
JavaScript
/*
* Este archivo establece una comunicación IPC
* bidireccional del proceso a master a proceso hijo o viceversa
* Aún no estoy seguro si esto es más eficiente que utilizar sockets
* em este caso se utiliza el stdin y stdout del hijo
*/
var safeStringify= require("../../safe-json.js")
var oid=0
var util= require("util")
var cluster= require("cluster")
var child= require("child_process");
var Path= require("path");
var events= require("events").EventEmitter;
var Util= core.VW.Util;
var Comunication= module.exports= function(){
this.$buffer=[];
this.$buf='';
this.tasks=[]
this.otasks={}
}
Comunication.get_isMaster= function(){
if(core.VW._cluster){
return false;
}
return cluster.isMaster;
}
Comunication.get_isCluster= function(){
return !!core.VW._cluster || !cluster.isMaster;
}
Comunication.prototype.get_child= function(){
return this.cp;
}
Comunication.prototype.handleStdErr= true;
Comunication.prototype.handleStdOut= true;
Comunication.prototype.init= function(/* child_process */cp){
var self= this;
if(Comunication.isMaster){
// Cuando es master se debe especificar un child_process ...
/*
* Se comentarea esta parte pero se debe tener cuidado
* no crear canales de comunicación dos veces sobre proceso principal
if(!cp){
throw new core.System.ArgumentException("Este proceso es master, debe especificar el parámetro child_process cp");
}
*/
}
if(cp){
this.cp= cp;
this.cp.on("error", function(er){
this.lastError= er;
})
this.cp.stderr&&this.cp.stderr.on("error", function(er){
this.lastError= er;
})
this.cp.stdout&&this.cp.stdout.on("error", function(er){
this.lastError= er;
})
this.cp.stderr && this.cp.stderr.on("data", function(buf){
//vw.log("Data: ",buf.toString())
if(this.handleStdErr||true){
core.VW.Console.foregroundColor= core.System.ConsoleColor.Red;
core.VW.Console.write("Error no controlado child: ", buf.toString());
core.VW.Console.resetColors();
}
});
this.cp.stdout.on("data", function(buf){
//vw.log("Data: ",buf.toString())
self.handleResponse(buf);
});
this._inited=true;
}
else{
// La comunicación se hace con el master
var stdin;
if(!cluster.isMaster){
stdin= Comunication.processWorkerClient();
this.stdout=stdin;
}
else{
stdin= process.openStdin();
}
stdin.on("data", function(buf){
self.handleResponse(buf);
})
this.stdin=stdin;
this._inited=true;
}
return this;
}
Comunication.prototype.verifyInit= function(){
if(!this._inited){
throw new core.VW.IPC.NotInitializedException("Debe inicializar el flujo de comunicación con el método init()");
}
}
Comunication.uid=1;
var Stream= function(worker, error){
events.call(this);
this.worker= worker;
if(worker===false){
}
else if(worker){
var self= this;
this.worker.on(error? "error" : "message",function(buf){
self.emit("data",buf)
})
}
else{
var self= this;
process.on(error? "error" : "message",function(buf){
self.emit("data",buf)
})
}
}
Stream.prototype= Object.create(events.prototype)
Stream.prototype.constructor=Stream;
Stream.prototype.write= function(message){
if(this.worker){
this.worker.send(message);
}
else{
process.send(message);
}
}
Comunication.processWorker= function(worker){
var stdout=new Stream(worker);
var stdin=new Stream(false);
stdin.worker= worker;
var stderr=new Stream(worker,true);
worker.stdout= stdout;
worker.stdin= stdin;
worker.stderr=stderr;
}
Comunication.processWorkerClient= function(){
return new Stream();
}
Comunication.prototype.createChildProcess= function(options){
/*if(!Comunication.isMaster){
throw new core.VW.IPC.InvalidOperationException();
}*/
options=options||{};
var modulePath=options.modulePath;
if(options && options.fork){
var cp= cluster.fork();
Comunication.processWorker(cp)
return this.init(cp)
}
var arg1= process.argv[0];
var arg=[];
arg.push(Path.normalize(Path.join(core.VW.path, "cluster.js")));
for(var i=2;i<process.argv.length;i++){
arg.push(process.argv[i]);
}
return this.init(child.spawn(arg1,arg));
}
Comunication.prototype.write= function(){
throw new core.System.NotImplementedException("Utilice writeAsync");
}
Comunication.prototype.read= function(){
throw new core.System.NotImplementedException("Utilice readAsync");
}
Comunication.prototype.writeAsync= function(data, isCall){
var id= ((Math.random()*10000)|0) + "w" + (oid++)
var res= {
IPC:true,
data:data,
id: isCall ?id: undefined
};
var text= safeStringify(res);
var task= this.rawWrite(text)
var self= this
if(isCall){
setTimeout(function(){
if(task.isCompleted)
return
self.otasks[id]=undefined
task.exception= "Se acabó el tiempo de espera para escribir en el canal de comunicación"
task.finish()
}, 12000)
this.otasks[id]= task
}
return task
}
Comunication.prototype.rawWrite= function(text){
var task= new core.VW.Task();
var l= this.cp ? this.cp.stdin : (this.stdout||process.stdout);
if(!l._error){
l.on("error", function(er){
//vw.error("ERROR: ",er)
})
l._error= true
}
l.write(text+"\n", function(err, data){
if(task.isCompleted)
return
if(err){
task.exception=err;
task.finish();
}
//task.finish();
});
return task;
}
/* Método asíncrono para leer */
Comunication.prototype.readAsync= function(task){
//this.verifyInit();
//return this._readAsync(0, core.VW.Task.get(arguments));
var task= core.VW.Task.get(arguments);
this.tasks.push(task);
return task;
}
Comunication.prototype._readAsync= function(num, task){
if(this.$r){
var data= this.$buffer.shift();
this.$r=false;
if(num==0){
setImmediate(function(){
task.result= data;
task.finish();
});
}
else{
task.result= data;
task.finish();
}
}
else{
var self=this;
/*if(num<11){
return self._readAsync(num+1,task);
}
else{
num=1;
}*/
setTimeout(function(){
self._readAsync(num+1,task);
},0);
}
return task;
}
Comunication.prototype.handleNormal= function(str){
if(!this.handleStdOut){return;}
core.VW.Console.setColorLog().writeLine(str).resetColors();
}
Comunication.prototype.handleResponse= function(buf){
var self= this;
var str=buf.toString('utf8');
var p= self.p;
if(!p){
p= self.p=function(str){
if(!str){return;}
var res;
try{
res=JSON.parse(str);
}catch(e){
return self.handleNormal(str);
}
if(!res.IPC){
return;
}
if(res.oid){
if(self.otasks[res.oid]){
self.otasks[res.oid].finish()
self.otasks[res.oid]=undefined
}
}
else{
self.$buffer.push(res.data);
if(res.id)
self.rawWrite(JSON.stringify({IPC:true, oid: res.id})) // OK ...
for(var i=0;i<Math.min(self.$buffer.length, self.tasks.length);i++){
var task= self.tasks.shift();
task.result= self.$buffer.shift();
task.finish();
}
}
}
}
var pr=false;
if(str.indexOf("\n")<0){
self.$buf+= str;
pr=true;
}
else if(self.$buf.length>0){
str= self.$buf+str;
self.$buf='';
}
if(!pr){
var lines=str.split("\n");
for(var i=0;i<lines.length;i++){
p(lines[i]);
}
}
}
Util.createProperties(Comunication,Comunication.prototype);