运行的进程状态维护

This commit is contained in:
Liang Ding 2014-09-16 17:32:55 +08:00
parent 75ee2b9c89
commit c516a7e289
6 changed files with 150 additions and 59 deletions

View File

@ -24,7 +24,7 @@ import (
func init() { func init() {
// 默认启动参数 // 默认启动参数
flag.Set("logtostderr", "true") flag.Set("logtostderr", "true")
flag.Set("v", "1") flag.Set("v", "3")
flag.Parse() flag.Parse()
// 加载事件处理 // 加载事件处理

View File

@ -32,7 +32,7 @@ type Notification struct {
} }
// 通知通道. // 通知通道.
// <sid, util.WSChannel> // <sid, *util.WSChannel>
var notificationWSs = map[string]*util.WSChannel{} var notificationWSs = map[string]*util.WSChannel{}
// 用户事件处理:将事件转为通知,并通过通知通道推送给前端. // 用户事件处理:将事件转为通知,并通过通知通道推送给前端.

View File

@ -21,7 +21,7 @@ import (
) )
// 输出通道. // 输出通道.
// <sid, util.WSChannel> // <sid, *util.WSChannel>
var outputWS = map[string]*util.WSChannel{} var outputWS = map[string]*util.WSChannel{}
// 建立输出通道. // 建立输出通道.
@ -83,9 +83,18 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
reader := io.MultiReader(stdout, stderr) reader := io.MultiReader(stdout, stderr)
cmd.Start() if err := cmd.Start(); nil != err {
glog.Error(err)
data["succ"] = false
return
}
// 添加到用户进程集中
processes.add(sid, cmd.Process)
channelRet := map[string]interface{}{} channelRet := map[string]interface{}{}
channelRet["pid"] = cmd.Process.Pid
go func(runningId int) { go func(runningId int) {
glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath) glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
@ -95,16 +104,33 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
count, err := reader.Read(buf) count, err := reader.Read(buf)
if nil != err || 0 == count { if nil != err || 0 == count {
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath) // 从用户进程集中移除这个执行完毕的进程
processes.remove(sid, cmd.Process)
break glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath)
} else {
channelRet["output"] = string(buf[:count])
channelRet["cmd"] = "run"
if nil != outputWS[sid] { if nil != outputWS[sid] {
wsChannel := outputWS[sid] wsChannel := outputWS[sid]
channelRet["cmd"] = "run-done"
channelRet["output"] = string(buf[:count])
err := wsChannel.Conn.WriteJSON(&channelRet)
if nil != err {
glog.Error(err)
break
}
// 更新通道最近使用时间
wsChannel.Time = time.Now()
}
break
} else {
if nil != outputWS[sid] {
wsChannel := outputWS[sid]
channelRet["cmd"] = "run"
channelRet["output"] = string(buf[:count])
err := wsChannel.Conn.WriteJSON(&channelRet) err := wsChannel.Conn.WriteJSON(&channelRet)
if nil != err { if nil != err {
glog.Error(err) glog.Error(err)
@ -206,7 +232,12 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
if data["succ"].(bool) { if data["succ"].(bool) {
reader := io.MultiReader(stdout, stderr) reader := io.MultiReader(stdout, stderr)
cmd.Start() if err := cmd.Start(); nil != err {
glog.Error(err)
data["succ"] = false
return
}
go func(runningId int) { go func(runningId int) {
glog.V(3).Infof("Session [%s] is building [id=%d, file=%s]", sid, runningId, filePath) glog.V(3).Infof("Session [%s] is building [id=%d, file=%s]", sid, runningId, filePath)

55
output/processes.go Normal file
View File

@ -0,0 +1,55 @@
package output
import (
"os"
"github.com/golang/glog"
)
// 所有用户正在运行的程序进程集.
// <sid, []*os.Process>
type procs map[string][]*os.Process
var processes = procs{}
// 添加用户执行进程.
func (procs *procs) add(sid string, proc *os.Process) {
userProcesses := (*procs)[sid]
userProcesses = append(userProcesses, proc)
(*procs)[sid] = userProcesses
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
}
// 移除用户执行进程.
func (procs *procs) remove(sid string, proc *os.Process) {
userProcesses := (*procs)[sid]
var newProcesses []*os.Process
for i, p := range userProcesses {
if p.Pid == proc.Pid {
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
(*procs)[sid] = newProcesses
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
return
}
}
}
// 结束用户正在执行的进程.
func (procs *procs) kill(sid string, pid int) {
pros := (*procs)[sid]
for _, p := range pros {
if p.Pid == pid {
if err := p.Kill(); nil != err {
glog.Error("Kill a process [pid=%d] of session [%s] failed [error=%v]", pid, sid, err)
} else {
glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
}
}
}
}

View File

@ -21,7 +21,7 @@ import (
) )
// Shell 通道. // Shell 通道.
// <sid, util.WSChannel>> // <sid, *util.WSChannel>>
var shellWS = map[string]*util.WSChannel{} var shellWS = map[string]*util.WSChannel{}
// Shell 首页. // Shell 首页.

View File

@ -1,9 +1,9 @@
var outputWS = new WebSocket(config.channel.output + '/output/ws'); var outputWS = new WebSocket(config.channel.output + '/output/ws');
outputWS.onopen = function() { outputWS.onopen = function () {
console.log('[output onopen] connected'); console.log('[output onopen] connected');
}; };
outputWS.onmessage = function(e) { outputWS.onmessage = function (e) {
console.log('[output onmessage]' + e.data); console.log('[output onmessage]' + e.data);
var data = JSON.parse(e.data); var data = JSON.parse(e.data);
@ -11,8 +11,29 @@ outputWS.onmessage = function(e) {
goLintFound = []; goLintFound = [];
} }
if ('run' === data.cmd) { if ('run' === data.nextCmd) {
var request = {
executable: data.executable
};
$.ajax({
type: 'POST',
url: '/run',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function (data) {
$('#output').text('');
},
success: function (data) {
}
});
}
if ('run' === data.cmd) { // 正在运行
$('#output').text($('#output').text() + data.output); $('#output').text($('#output').text() + data.output);
} else if ('run-done' === data.cmd) { // 运行结束
// TODO: 运行结束后修改 [构建&运行] 图标状态为可用状态
} else if ('build' === data.cmd || 'go install' === data.cmd) { } else if ('build' === data.cmd || 'go install' === data.cmd) {
$('#output').text(data.output); $('#output').text(data.output);
@ -24,6 +45,8 @@ outputWS.onmessage = function(e) {
to: CodeMirror.Pos(lint.lineNo, 0), to: CodeMirror.Pos(lint.lineNo, 0),
message: lint.msg, severity: lint.severity}); message: lint.msg, severity: lint.severity});
} }
// TODO: 修改 [构建&运行] 图标状态为可用状态
} }
// 触发一次 gutter lint // 触发一次 gutter lint
@ -31,49 +54,28 @@ outputWS.onmessage = function(e) {
} else if ('go get' === data.cmd || 'go install' === data.cmd) { } else if ('go get' === data.cmd || 'go install' === data.cmd) {
$('#output').text($('#output').text() + data.output); $('#output').text($('#output').text() + data.output);
} }
if ('build' === data.cmd) {
if ('run' === data.nextCmd) {
var request = {
executable: data.executable
};
$.ajax({
type: 'POST',
url: '/run',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function(data) {
$('#output').text('');
},
success: function(data) {
}
});
}
}
}; };
outputWS.onclose = function(e) { outputWS.onclose = function (e) {
console.log('[output onclose] disconnected (' + e.code + ')'); console.log('[output onclose] disconnected (' + e.code + ')');
delete outputWS; delete outputWS;
}; };
outputWS.onerror = function(e) { outputWS.onerror = function (e) {
console.log('[output onerror] ' + e); console.log('[output onerror] ' + e);
}; };
var wide = { var wide = {
curNode: undefined, curNode: undefined,
curEditor: undefined, curEditor: undefined,
_initLayout: function() { _initLayout: function () {
var mainH = $(window).height() - $(".menu").height() - $(".footer").height() - 2; var mainH = $(window).height() - $(".menu").height() - $(".footer").height() - 2;
$(".content, .ztree").height(mainH); $(".content, .ztree").height(mainH);
$(".edit-panel").height(mainH - $(".output").height()); $(".edit-panel").height(mainH - $(".output").height());
}, },
init: function() { init: function () {
this._initLayout(); this._initLayout();
$("body").bind("mousedown", function(event) { $("body").bind("mousedown", function (event) {
if (!(event.target.id === "dirRMenu" || $(event.target).closest("#dirRMenu").length > 0)) { if (!(event.target.id === "dirRMenu" || $(event.target).closest("#dirRMenu").length > 0)) {
$("#dirRMenu").hide(); $("#dirRMenu").hide();
} }
@ -90,7 +92,7 @@ var wide = {
}); });
}, },
saveFile: function() { saveFile: function () {
var request = { var request = {
file: $(".edit-header .current span:eq(0)").attr("title"), file: $(".edit-header .current span:eq(0)").attr("title"),
code: wide.curEditor.getValue() code: wide.curEditor.getValue()
@ -100,41 +102,44 @@ var wide = {
url: '/file/save', url: '/file/save',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
success: function(data) { success: function (data) {
} }
}); });
}, },
saveAllFiles: function() { saveAllFiles: function () {
// TODO: save all files // TODO: save all files
}, },
closeFile: function() { closeFile: function () {
// TODO: close file // TODO: close file
}, },
closeAllFiles: function() { closeAllFiles: function () {
// TODO: close all files // TODO: close all files
}, },
exit: function() { exit: function () {
// TODO: exit // TODO: exit
}, },
run: function() { // 构建 & 运行.
run: function () {
var request = { var request = {
file: $(".edit-header .current span:eq(0)").attr("title"), file: $(".edit-header .current span:eq(0)").attr("title"),
code: wide.curEditor.getValue() code: wide.curEditor.getValue()
}; };
// TODO: 修改 [构建&运行] 图标状态为不可用状态
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '/build', url: '/build',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
beforeSend: function(data) { beforeSend: function (data) {
$('#output').text(''); $('#output').text('');
}, },
success: function(data) { success: function (data) {
} }
}); });
}, },
goget: function() { goget: function () {
var request = { var request = {
file: $(".edit-header .current span:eq(0)").attr("title") file: $(".edit-header .current span:eq(0)").attr("title")
}; };
@ -144,14 +149,14 @@ var wide = {
url: '/go/get', url: '/go/get',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
beforeSend: function(data) { beforeSend: function (data) {
$('#output').text(''); $('#output').text('');
}, },
success: function(data) { success: function (data) {
} }
}); });
}, },
goinstall: function() { goinstall: function () {
var request = { var request = {
file: $(".edit-header .current span:eq(0)").attr("title"), file: $(".edit-header .current span:eq(0)").attr("title"),
code: wide.curEditor.getValue() code: wide.curEditor.getValue()
@ -162,14 +167,14 @@ var wide = {
url: '/go/install', url: '/go/install',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
beforeSend: function(data) { beforeSend: function (data) {
$('#output').text(''); $('#output').text('');
}, },
success: function(data) { success: function (data) {
} }
}); });
}, },
fmt: function() { fmt: function () {
var path = $(".edit-header .current span:eq(0)").attr("title"); var path = $(".edit-header .current span:eq(0)").attr("title");
var mode = wide.curNode.mode; var mode = wide.curNode.mode;
@ -187,7 +192,7 @@ var wide = {
url: '/go/fmt', url: '/go/fmt',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
success: function(data) { success: function (data) {
if (data.succ) { if (data.succ) {
wide.curEditor.setValue(data.code); wide.curEditor.setValue(data.code);
} }
@ -201,7 +206,7 @@ var wide = {
url: '/html/fmt', url: '/html/fmt',
data: JSON.stringify(request), data: JSON.stringify(request),
dataType: "json", dataType: "json",
success: function(data) { success: function (data) {
if (data.succ) { if (data.succ) {
wide.curEditor.setValue(data.code); wide.curEditor.setValue(data.code);
} }
@ -228,7 +233,7 @@ var wide = {
} }
}; };
$(document).ready(function() { $(document).ready(function () {
wide.init(); wide.init();
tree.init(); tree.init();
menu.init(); menu.init();