Improve process logic

This commit is contained in:
Krzosa Karol
2024-08-12 17:01:33 +02:00
parent 2b48f30cfe
commit fcabf96347
5 changed files with 92 additions and 206 deletions

View File

@@ -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);
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);

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -7,35 +7,20 @@ Array<Process> 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);

View File

@@ -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];