UNPKG

vox-core

Version:

Runtime de aplicaciones multiplataforma

365 lines (294 loc) 7.58 kB
/* * 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);