运行的进程状态维护
This commit is contained in:
parent
75ee2b9c89
commit
c516a7e289
2
main.go
2
main.go
|
@ -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()
|
||||||
|
|
||||||
// 加载事件处理
|
// 加载事件处理
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
||||||
// 用户事件处理:将事件转为通知,并通过通知通道推送给前端.
|
// 用户事件处理:将事件转为通知,并通过通知通道推送给前端.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 首页.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue