From fcabf963471c3630f0093acedd9c6c5b2373fd13 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 12 Aug 2024 17:01:33 +0200 Subject: [PATCH] Improve process logic --- src/basic/filesystem.h | 28 ++---- src/basic/win32.cpp | 136 +++++++++++----------------- src/text_editor/commands_window.cpp | 27 +++--- src/text_editor/process.cpp | 97 ++------------------ src/text_editor/text_editor.cpp | 10 +- 5 files changed, 92 insertions(+), 206 deletions(-) diff --git a/src/basic/filesystem.h b/src/basic/filesystem.h index 6d4db0f..859431e 100644 --- a/src/basic/filesystem.h +++ b/src/basic/filesystem.h @@ -24,7 +24,7 @@ String GetAbsolutePath(Allocator arena, String relative); bool IsValid(const FileIter &it); void Advance(FileIter *it); FileIter IterateFiles(Allocator allocator, String path); -bool InitOS(); +void InitOS(void (*error_proc)(const char *, ...)); String GetExePath(Allocator allocator); String GetExeDir(Allocator allocator); @@ -34,26 +34,18 @@ bool IsFile(String path); String GetWorkingDir(Allocator arena); bool IsAbsolute(String path); -struct StdoutPollInfo { - int64_t size_read; - int64_t size_available; -}; - struct Process { - bool is_valid; - String error_message; - bool exited; - int exit_code; - char platform[6 * 8]; + bool is_valid; + int exit_code; + char platform[6 * 8]; int64_t view_id; // text editor view bool scroll_to_end; }; -Process CreateCommandLineProcess(String command_line, String working_dir); -bool PollExitCode(Process *process); -void CloseProcess(Process *process); -void KillProcess(Process *process); -StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size); -void WriteStdin(Process *process, String string); -void CloseStdin(Process *process); \ No newline at end of file +Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}); +bool IsValid(Process *process); +void KillProcess(Process *process); +String PollStdout(Allocator allocator, Process *process); +void WriteStdin(Process *process, String string); +void CloseStdin(Process *process); \ No newline at end of file diff --git a/src/basic/win32.cpp b/src/basic/win32.cpp index c4b9f83..74d6480 100644 --- a/src/basic/win32.cpp +++ b/src/basic/win32.cpp @@ -13,6 +13,8 @@ #include "win32_thread.cpp" +void (*Error)(const char *, ...); + // Basic begin void *VReserve(size_t size) { void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); @@ -35,26 +37,8 @@ bool VDecommit(void *p, size_t size) { } // Basic end -bool InitOS() { - SetConsoleOutputCP(CP_UTF8); - - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut != INVALID_HANDLE_VALUE) { - DWORD dwMode = 0; - if (GetConsoleMode(hOut, &dwMode)) { - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (SetConsoleMode(hOut, dwMode)) { - return true; - } else { - printf("Failed to enable colored terminal output C\n"); - } - } else { - printf("Failed to enable colored terminal output B\n"); - } - } else { - printf("Failed to enable colored terminal output A\n"); - } - return false; +void InitOS(void (*error_proc)(const char *, ...)) { + Error = error_proc; } String ReadFile(Allocator arena, String path) { @@ -287,25 +271,17 @@ struct Win32Process { }; static_assert(sizeof(Win32Process) < sizeof(Process::platform)); -static String Win32GetPlatformError(String msg, String cmd) { +void Win32ReportError(String msg, String cmd) { LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); - + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); + defer { LocalFree(lpMsgBuf); }; char *buff = (char *)lpMsgBuf; size_t buffLen = strlen((const char *)buff); - if (buffLen > 0 && buff[buffLen - 1] == '\n') { - buff[buffLen - 1] = 0; - } - - // @warning: leak! but we don't care - String r = Format(GetSystemAllocator(), "%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return r; + if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0; + Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf); } -static void Win32CloseProcess(Process *process) { +void Win32CloseProcess(Process *process) { Win32Process *p = (Win32Process *)process->platform; if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle); if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write); @@ -316,12 +292,32 @@ static void Win32CloseProcess(Process *process) { } static void Win32ProcessError(Process *process, String msg, String cmd) { - Win32Process *p = (Win32Process *)process->platform; - process->error_message = Win32GetPlatformError(msg, cmd); + Win32Process *p = (Win32Process *)process->platform; + Win32ReportError(msg, cmd); Win32CloseProcess(process); } -Process CreateCommandLineProcess(String command_line, String working_dir) { +void WriteStdin(Process *process, String string) { + if (string.len == 0) return; + Assert(process->is_valid); + + Win32Process *p = (Win32Process *)process->platform; + Assert(p->child_stdin_write != INVALID_HANDLE_VALUE); + + DWORD written = 0; + bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0; + + Assert(write_error == false); + Assert(written == string.len); +} + +void CloseStdin(Process *process) { + Win32Process *p = (Win32Process *)process->platform; + CloseHandle(p->child_stdin_write); + p->child_stdin_write = INVALID_HANDLE_VALUE; +} + +Process SpawnProcess(String command_line, String working_dir, String write_stdin) { Process process = {}; Win32Process *p = (Win32Process *)process.platform; @@ -389,16 +385,21 @@ Process CreateCommandLineProcess(String command_line, String working_dir) { p->handle = info.hProcess; process.is_valid = true; + + if (write_stdin.len) { + WriteStdin(&process, write_stdin); + CloseStdin(&process); + } + return process; } -bool PollExitCode(Process *process) { - Assert(process->is_valid); +bool IsValid(Process *process) { + if (process->is_valid == false) return false; Win32Process *p = (Win32Process *)process->platform; - if (process->exited) return process->exited; if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) { - return false; + return true; } DWORD exit_code; @@ -407,13 +408,9 @@ bool PollExitCode(Process *process) { exit_code = -1; } - process->exited = true; process->exit_code = (int)exit_code; - return true; -} - -void CloseProcess(Process *process) { Win32CloseProcess(process); + return false; } void KillProcess(Process *process) { @@ -428,53 +425,30 @@ void KillProcess(Process *process) { process->exit_code = -1; } -StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size) { +String PollStdout(Allocator allocator, Process *process) { Assert(process->is_valid); Win32Process *p = (Win32Process *)process->platform; DWORD bytes_avail = 0; - - 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) { - process->error_message = Win32GetPlatformError("Failed to peek stdout of child process", "PeekNamedPipe"); - return result; + return {}; + } else if (bytes_avail == 0) { + return {}; } - if (!buffer) { - return {0, bytes_avail}; - } - - if (bytes_avail == 0) { - return result; - } + size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16)); + char *buffer = AllocArray(allocator, char, buffer_size); DWORD bytes_read = 0; bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0; if (read_error) { - process->error_message = Win32GetPlatformError("Failed to read the stdout of child process", "ReadFile"); - return result; + Win32ReportError("Failed to read the stdout of child process", "ReadFile"); + Dealloc(allocator, &buffer); + Win32CloseProcess(process); + return {}; } - result = {bytes_read, bytes_avail - bytes_read}; + String result = {buffer, bytes_read}; return result; } - -void WriteStdin(Process *process, String string) { - if (string.len == 0) return; - Assert(process->is_valid); - - Win32Process *p = (Win32Process *)process->platform; - - DWORD written = 0; - bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0; - - Assert(write_error == false); - Assert(written == string.len); -} - -void CloseStdin(Process *process) { - Win32Process *p = (Win32Process *)process->platform; - CloseHandle(p->child_stdin_write); - p->child_stdin_write = INVALID_HANDLE_VALUE; -} diff --git a/src/text_editor/commands_window.cpp b/src/text_editor/commands_window.cpp index 06984c5..f08597f 100644 --- a/src/text_editor/commands_window.cpp +++ b/src/text_editor/commands_window.cpp @@ -383,19 +383,24 @@ void SaveBuffer(View *view) { Assert(view->active_buffer == buffer->id); } + { + Scratch scratch; + String16 string16 = GetString(*buffer); + String string = ToString(scratch, string16); + Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string); + Command_ReplaceWithoutMovingCarets(view, GetRange(*buffer), {temp_buffer->str, temp_buffer->len}); + } + Scratch scratch; - String16 string16 = GetString(*buffer); - String string = ToString(scratch, string16); - Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string); - Command_ReplaceWithoutMovingCarets(view, GetRange(*buffer), {temp_buffer->str, temp_buffer->len}); + String16 string16 = GetString(*buffer); + String string = ToString(scratch, string16); + bool success = WriteFile(buffer->name, string); - // bool success = WriteFile(buffer->name, string); - - // if (success) { - // buffer->dirty = false; - // } else { - // ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name)); - // } + if (success) { + buffer->dirty = false; + } else { + ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name)); + } } void Command_KillSelectedLines(View *view) { diff --git a/src/text_editor/process.cpp b/src/text_editor/process.cpp index 16cee95..6cd68b9 100644 --- a/src/text_editor/process.cpp +++ b/src/text_editor/process.cpp @@ -7,35 +7,20 @@ Array ActiveProcesses = {}; // 'less' program which errors out and doesn't print anything // @todo: maybe I should ask someone smarter about this! void UpdateProcesses() { - Scratch scratch; - int64_t buffer_size = 4096; - char *buffer = AllocArray(scratch, char, buffer_size); IterRemove(ActiveProcesses) { IterRemovePrepare(ActiveProcesses); - StdoutPollInfo info = PollStdout(&it, buffer, buffer_size); - String string = {buffer, info.size_read}; - ViewID view_id = {it.view_id}; - 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); - if (exited) { - CloseProcess(&it); - remove_item = true; - // String s = Format(scratch, "exited with code: %d", it.exit_code); - // Command_Append(view_id, s, it.scroll_to_end); - } + Scratch scratch; + String poll = PollStdout(scratch, &it); + if (poll.len) Command_Append({it.view_id}, poll, it.scroll_to_end); + if (!IsValid(&it)) remove_item = true; } } 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 process = SpawnProcess(cmd, working_dir); process.view_id = view.id; process.scroll_to_end = scroll_to_end; - if (!process.is_valid) return; - Assert(process.error_message.len == 0); - Add(&ActiveProcesses, process); + if (process.is_valid) Add(&ActiveProcesses, process); } void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) { @@ -45,78 +30,16 @@ void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) { } Buffer *ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) { - Process process = CreateCommandLineProcess(cmd, working_dir); - if (process.error_message.len) ReportWarningf("%.*s", FmtString(process.error_message)); - Buffer *temp_buffer = CreateTempBuffer(allocator, 4096 * 4); - if (!process.is_valid) return temp_buffer; - - if (stdin_string.len) { - WriteStdin(&process, stdin_string); - CloseStdin(&process); - } - - Scratch scratch(allocator); - char *buffer = AllocArray(scratch, char, 4096); - for (;;) { - StdoutPollInfo info = PollStdout(&process, buffer, 4096); - String string = {buffer, info.size_read}; - String16 string16 = ToString16(scratch, string); - - if (string.len) { - IKnowWhatImDoing_Append(temp_buffer, string16); - } - - bool exited = PollExitCode(&process); - if (exited) { - CloseProcess(&process); - break; - } - } - - if (process.error_message.len) { - ReportConsolef("%.*s", FmtString(process.error_message)); + for (Process process = SpawnProcess(cmd, working_dir, stdin_string); IsValid(&process);) { + Scratch scratch(allocator); + String poll = PollStdout(scratch, &process); + if (poll.len) IKnowWhatImDoing_Append(temp_buffer, poll); } return temp_buffer; } -void ExecAndWait(View *view, String cmd, String working_dir, String stdin_string = {}) { - Process process = CreateCommandLineProcess(cmd, working_dir); - if (process.error_message.len) ReportWarningf("%.*s", FmtString(process.error_message)); - if (!process.is_valid) return; - - if (stdin_string.len) { - WriteStdin(&process, stdin_string); - CloseStdin(&process); - } - - Scratch scratch; - char *buffer = AllocArray(scratch, char, 4096); - Buffer *the_buffer = GetBuffer(view->active_buffer); - Range range = GetRange(*the_buffer); - for (;;) { - StdoutPollInfo info = PollStdout(&process, buffer, 4096); - String string = {buffer, info.size_read}; - String16 string16 = ToString16(scratch, string); - - if (string.len) { - Command_ReplaceWithoutMovingCarets(view, range, string16); - range = GetEndAsRange(*the_buffer); - } - - bool exited = PollExitCode(&process); - if (exited) { - CloseProcess(&process); - break; - } - } - - if (process.error_message.len) { - ReportConsolef("%.*s", FmtString(process.error_message)); - } -} - void KillProcess(ViewID view) { IterRemove(ActiveProcesses) { IterRemovePrepare(ActiveProcesses); diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index d31cb2c..2dbfbef 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -250,15 +250,6 @@ int main(int argc, char **argv) SDL_free(sdl_config_path.data); } - // Process process = CreateCommandLineProcess("git log", WorkingDir); - // Scratch scratch; - // char *buffer = AllocArray(scratch, char, 4096); - // for (int i = 0; i < 4; i += 1) { - // StdoutPollInfo info = PollStdout(&process, buffer, 4096); - // bool exited = PollExitCode(&process); - // if (exited) break; - // } - if (SDL_Init(SDL_INIT_VIDEO) == -1) { ReportErrorf("Couldn't initialize SDL! %s", SDL_GetError()); return 1; @@ -315,6 +306,7 @@ int main(int argc, char **argv) ReloadFont(); InitLuaConfig(); InitWindows(); + InitOS(ReportWarningf); for (int i = 1; i < argc; i += 1) { String it = argv[i];