Process error handling and process killing
This commit is contained in:
@@ -283,7 +283,7 @@ struct Win32Process {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||||
|
|
||||||
static String Win32GetPlatformError(String cmd) {
|
static String Win32GetPlatformError(String msg, String cmd) {
|
||||||
LPVOID lpMsgBuf;
|
LPVOID lpMsgBuf;
|
||||||
DWORD dw = GetLastError();
|
DWORD dw = GetLastError();
|
||||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
@@ -296,7 +296,7 @@ static String Win32GetPlatformError(String cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @warning: leak! but we don't care
|
// @warning: leak! but we don't care
|
||||||
String r = Format(GetSystemAllocator(), "Failed to create process using cmd: %.*s! %s", FmtString(cmd), (char *)lpMsgBuf);
|
String r = Format(GetSystemAllocator(), "%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
|
||||||
LocalFree(lpMsgBuf);
|
LocalFree(lpMsgBuf);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -312,9 +312,9 @@ static void Win32CloseProcess(Process *process) {
|
|||||||
*p = {};
|
*p = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Win32ProcessError(Process *process, String cmd) {
|
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||||
Win32Process *p = (Win32Process *)process->platform;
|
Win32Process *p = (Win32Process *)process->platform;
|
||||||
process->error_message = Win32GetPlatformError(cmd);
|
process->error_message = Win32GetPlatformError(msg, cmd);
|
||||||
Win32CloseProcess(process);
|
Win32CloseProcess(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,22 +336,22 @@ Process CreateCommandLineProcess(String command_line, String working_dir) {
|
|||||||
security_atrb.bInheritHandle = TRUE;
|
security_atrb.bInheritHandle = TRUE;
|
||||||
|
|
||||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||||
Win32ProcessError(&process, command_line);
|
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
Win32ProcessError(&process, command_line);
|
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||||
Win32ProcessError(&process, command_line);
|
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
Win32ProcessError(&process, command_line);
|
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ Process CreateCommandLineProcess(String command_line, String working_dir) {
|
|||||||
|
|
||||||
PROCESS_INFORMATION info = {};
|
PROCESS_INFORMATION info = {};
|
||||||
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", cmd.data, 0, 0, TRUE, 0, env, cwd.data, &startup, &info)) {
|
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", cmd.data, 0, 0, TRUE, 0, env, cwd.data, &startup, &info)) {
|
||||||
Win32ProcessError(&process, command_line);
|
Win32ProcessError(&process, "failed to create process", command_line);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,6 +426,7 @@ StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size) {
|
|||||||
StdoutPollInfo result = {};
|
StdoutPollInfo result = {};
|
||||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||||
if (peek_error) {
|
if (peek_error) {
|
||||||
|
process->error_message = Win32GetPlatformError("Failed to peek stdout of child process", "PeekNamedPipe");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +437,7 @@ StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size) {
|
|||||||
DWORD bytes_read = 0;
|
DWORD bytes_read = 0;
|
||||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||||
if (read_error) {
|
if (read_error) {
|
||||||
|
process->error_message = Win32GetPlatformError("Failed to read the stdout of child process", "ReadFile");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -205,9 +205,9 @@ function MatchAgainstIncludes(s)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MatchGitCommit(s)
|
function MatchGitCommit(s)
|
||||||
local i, j = string.find(s, "^commit ")
|
local i, j = string.find(s, "^commit ([a-zA-Z0-9]+)")
|
||||||
print(tostring(i))
|
|
||||||
if i then
|
if i then
|
||||||
|
s = s:sub(8)
|
||||||
local command = "git --no-pager show "..s
|
local command = "git --no-pager show "..s
|
||||||
return {kind = "exec", cmd = command, working_dir = GetCurrentBufferDir()}
|
return {kind = "exec", cmd = command, working_dir = GetCurrentBufferDir()}
|
||||||
end
|
end
|
||||||
@@ -220,7 +220,6 @@ Rules = {
|
|||||||
MatchGitCommit,
|
MatchGitCommit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ApplyRules(s)
|
function ApplyRules(s)
|
||||||
for i = #Rules,1,-1 do
|
for i = #Rules,1,-1 do
|
||||||
rule = Rules[i]
|
rule = Rules[i]
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ void Open(String16 path) {
|
|||||||
Open(string);
|
Open(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LuaKill(lua_State *L) {
|
||||||
|
Window *window = GetCurrentWindow();
|
||||||
|
KillProcess(window->active_view);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int LuaOpen(lua_State *L) {
|
int LuaOpen(lua_State *L) {
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
String path = luaL_checkstring(L, 1);
|
String path = luaL_checkstring(L, 1);
|
||||||
@@ -97,7 +103,7 @@ int LuaOpen(lua_State *L) {
|
|||||||
|
|
||||||
int LuaPrint(lua_State *L) {
|
int LuaPrint(lua_State *L) {
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
String string = luaL_checkstring(L, 1);
|
String string = lua_tostring(L, 1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
Command_Append(ConsoleViewID, string, true);
|
Command_Append(ConsoleViewID, string, true);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -153,19 +159,6 @@ int LuaGetWorkingDir(lua_State *L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaOpenBigBuffer(lua_State *L) {
|
|
||||||
Window *window = GetWindow(GetLastActiveWindow());
|
|
||||||
|
|
||||||
// @todo: ViewOpenBuffer - new or old view for specified buffer
|
|
||||||
Buffer *buffer = CreateBuffer(GetSystemAllocator(), "big", 2500000 * 4);
|
|
||||||
LoadBigTextAndBigLine(buffer);
|
|
||||||
View *view = CreateView(buffer->id);
|
|
||||||
window->active_view = view->id;
|
|
||||||
|
|
||||||
SetActiveWindow(window->id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaGetCurrentBufferName(lua_State *L) {
|
int LuaGetCurrentBufferName(lua_State *L) {
|
||||||
String name = GetCurrentBufferName();
|
String name = GetCurrentBufferName();
|
||||||
lua_pushlstring(LuaState, name.data, name.len);
|
lua_pushlstring(LuaState, name.data, name.len);
|
||||||
@@ -188,6 +181,7 @@ luaL_Reg LuaFunctions[] = {
|
|||||||
{ "GetCurrentBufferDir", LuaGetCurrentBufferDir},
|
{ "GetCurrentBufferDir", LuaGetCurrentBufferDir},
|
||||||
{ "print", LuaPrint},
|
{ "print", LuaPrint},
|
||||||
{ "Print", LuaPrint},
|
{ "Print", LuaPrint},
|
||||||
|
{ "Kill", LuaKill},
|
||||||
{ NULL, NULL},
|
{ NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,3 +23,6 @@ https://handmade.network/forums/t/1219-win32_asynchronous_pipes_question
|
|||||||
- would it be fast enough to just reparse the entire file of certain size every time?
|
- would it be fast enough to just reparse the entire file of certain size every time?
|
||||||
- coloring seems even more hairy to do correctly because there are multiline comments etc.
|
- coloring seems even more hairy to do correctly because there are multiline comments etc.
|
||||||
- it honestly seems no matter what you do it's a very hairy problem, text was not meant for this
|
- it honestly seems no matter what you do it's a very hairy problem, text was not meant for this
|
||||||
|
|
||||||
|
|
||||||
|
Ok for reference only, I would like to share my experience and how I solved the problem. IT may be possible that there's something wrong in the child process I spawn, but from what I see, if I call Read in non-blocking mode, so only after a PeekNamedPipe, there's nothing that prevent the process from being deallocated, even I keep a reference to the pipe. I've solved launching another thread that does blocking Read on the pipe descriptor, and I'm longer loosing the last bytes..
|
||||||
@@ -1,18 +1,5 @@
|
|||||||
Array<Process> ActiveProcesses = {};
|
Array<Process> ActiveProcesses = {};
|
||||||
|
|
||||||
void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
|
|
||||||
Process process = CreateCommandLineProcess(cmd, working_dir);
|
|
||||||
process.view_id = view.id;
|
|
||||||
process.scroll_to_end = scroll_to_end;
|
|
||||||
if (process.is_valid) Add(&ActiveProcesses, process);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
|
||||||
Scratch scratch;
|
|
||||||
String cmd = ToString(scratch, cmd16);
|
|
||||||
Exec(view, scroll_to_end, cmd, working_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateProcesses() {
|
void UpdateProcesses() {
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
int64_t buffer_size = 4096;
|
int64_t buffer_size = 4096;
|
||||||
@@ -23,8 +10,44 @@ void UpdateProcesses() {
|
|||||||
String string = {buffer, info.size_read};
|
String string = {buffer, info.size_read};
|
||||||
ViewID view_id = {it.view_id};
|
ViewID view_id = {it.view_id};
|
||||||
if (string.len) Command_Append(view_id, string, it.scroll_to_end);
|
if (string.len) Command_Append(view_id, string, it.scroll_to_end);
|
||||||
|
if (it.error_message.len) Command_Append(view_id, it.error_message, it.scroll_to_end);
|
||||||
|
|
||||||
bool exited = PollExitCode(&it);
|
bool exited = PollExitCode(&it);
|
||||||
if (exited) remove_item = true;
|
if (exited) {
|
||||||
|
remove_item = true;
|
||||||
|
String s = Format(scratch, "exited with code: %d", it.exit_code);
|
||||||
|
Command_Append(view_id, s, it.scroll_to_end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
|
||||||
|
Process process = CreateCommandLineProcess(cmd, working_dir);
|
||||||
|
if (process.error_message.len) Command_Append(view, process.error_message, process.scroll_to_end);
|
||||||
|
process.view_id = view.id;
|
||||||
|
process.scroll_to_end = scroll_to_end;
|
||||||
|
if (process.is_valid) {
|
||||||
|
Assert(process.error_message.len == 0);
|
||||||
|
Add(&ActiveProcesses, process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
||||||
|
Scratch scratch;
|
||||||
|
String cmd = ToString(scratch, cmd16);
|
||||||
|
Exec(view, scroll_to_end, cmd, working_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KillProcess(ViewID view) {
|
||||||
|
IterRemove(ActiveProcesses) {
|
||||||
|
IterRemovePrepare(ActiveProcesses);
|
||||||
|
|
||||||
|
if (it.view_id == view.id) {
|
||||||
|
KillProcess(&it);
|
||||||
|
remove_item = true;
|
||||||
|
String string = "process was killed by user";
|
||||||
|
Command_Append(view, string, it.scroll_to_end);
|
||||||
|
// dont break because that will fuck with removal ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user