From 1780e6ac17c208d749deee31af541a04e3b123fe Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 6 Jan 2023 18:25:44 -0500 Subject: check LSP process status --- build.c | 10 +++++----- lsp.c | 29 +++++++++++++++++++++++++---- main.c | 1 - os-posix.c | 44 ++++++++++++++++++++++++++++++++++++-------- os-win.c | 14 +++++++++++--- os.h | 8 +++++--- ted.cfg | 3 ++- 7 files changed, 84 insertions(+), 25 deletions(-) diff --git a/build.c b/build.c index 56bc316..556cffb 100644 --- a/build.c +++ b/build.c @@ -5,7 +5,7 @@ void build_stop(Ted *ted) { if (ted->building) - process_kill(ted->build_process); + process_kill(&ted->build_process); ted->building = false; ted->build_shown = false; arr_foreach_ptr(ted->build_errors, BuildError, err) { @@ -36,6 +36,7 @@ void build_queue_command(Ted *ted, const char *command) { static bool build_run_next_command_in_queue(Ted *ted) { if (!ted->build_queue) return false; + assert(!ted->build_process); assert(*ted->build_dir); change_directory(ted->build_dir); char *command = ted->build_queue[0]; @@ -243,7 +244,6 @@ void build_check_for_errors(Ted *ted) { void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { TextBuffer *buffer = &ted->build_buffer; - Process *process = ted->build_process; assert(ted->build_shown); char buf[256]; if (ted->building) { @@ -254,9 +254,9 @@ void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { memcpy(incomplete, ted->build_incomplete_codepoint, sizeof incomplete); *ted->build_incomplete_codepoint = 0; - i64 bytes_read = (i64)process_read(process, buf + 3, sizeof buf - 3); + i64 bytes_read = (i64)process_read(ted->build_process, buf + 3, sizeof buf - 3); if (bytes_read == -2) { - ted_error(ted, "Error reading command output: %s.", process_geterr(process)); + ted_error(ted, "Error reading command output: %s.", process_geterr(ted->build_process)); build_stop(ted); break; } else if (bytes_read == -1) { @@ -302,7 +302,7 @@ void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { } char message[64]; - int status = process_check_status(process, message, sizeof message); + int status = process_check_status(&ted->build_process, message, sizeof message); if (status == 0) { // hasn't exited yet } else { diff --git a/lsp.c b/lsp.c index 6fb1d15..1cbe9e6 100644 --- a/lsp.c +++ b/lsp.c @@ -268,7 +268,8 @@ const char *lsp_response_string(const LSPResponse *response, LSPString string) { } // receive responses/requests/notifications from LSP, up to max_size bytes. -static void lsp_receive(LSP *lsp, size_t max_size) { +// returns false if the process exited +static bool lsp_receive(LSP *lsp, size_t max_size) { { // read stderr. if all goes well, we shouldn't get anything over stderr. @@ -287,13 +288,26 @@ static void lsp_receive(LSP *lsp, size_t max_size) { } } } + + { + // check process status + char text[64] = {0}; + int status = process_check_status(&lsp->process, text, sizeof text); + if (status != 0) { + lsp_set_error(lsp, "Can't access LSP server: %s\n" + "Run ted in a terminal or set lsp-log = on for more details." + , text); + return false; + } + + } size_t received_so_far = arr_len(lsp->received_data); arr_reserve(lsp->received_data, received_so_far + max_size + 1); long long bytes_read = process_read(lsp->process, lsp->received_data + received_so_far, max_size); if (bytes_read <= 0) { // no data - return; + return true; } received_so_far += (size_t)bytes_read; // kind of a hack. this is needed because arr_set_len zeroes the data. @@ -334,6 +348,7 @@ static void lsp_receive(LSP *lsp, size_t max_size) { arr_reserve(lsp->received_data, leftover_data_len + 1); lsp->received_data[leftover_data_len] = '\0'; } + return true; } // send requests. @@ -388,11 +403,17 @@ static int lsp_communication_thread(void *data) { bool quit = lsp_send(lsp); if (quit) break; - lsp_receive(lsp, (size_t)10<<20); + if (!lsp_receive(lsp, (size_t)10<<20)) + break; if (SDL_SemWaitTimeout(lsp->quit_sem, 5) == 0) break; } + if (!lsp->process) { + // process already exited + return 0; + } + if (lsp->initialized) { LSPRequest shutdown = { .type = LSP_REQUEST_SHUTDOWN, @@ -556,7 +577,7 @@ void lsp_free(LSP *lsp) { SDL_DestroyMutex(lsp->workspace_folders_mutex); SDL_DestroyMutex(lsp->error_mutex); SDL_DestroySemaphore(lsp->quit_sem); - process_kill(lsp->process); + process_kill(&lsp->process); arr_free(lsp->received_data); diff --git a/main.c b/main.c index 85d1a6f..7d9c287 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,5 @@ /* @TODO: -- check LSP process status (TEST: what happens if LSP server is not installed) - make tags_dir the root folder - check that tags still works - check that phantom completions works with tags diff --git a/os-posix.c b/os-posix.c index 3f19534..f34d33b 100644 --- a/os-posix.c +++ b/os-posix.c @@ -245,10 +245,15 @@ Process *process_run(const char *command) { const char *process_geterr(Process *p) { + if (!p) return "no such process"; return *p->error ? p->error : NULL; } long long process_write(Process *proc, const char *data, size_t size) { + if (!proc) { + assert(0); + return -2; + } if (!proc->stdin_pipe) { // check that process hasn't been killed strbuf_printf(proc->error, "Process terminated"); return -2; @@ -299,10 +304,18 @@ static long long process_read_fd(Process *proc, int fd, char *data, size_t size) } long long process_read(Process *proc, char *data, size_t size) { + if (!proc) { + assert(0); + return 0; + } return process_read_fd(proc, proc->stdout_pipe, data, size); } long long process_read_stderr(Process *proc, char *data, size_t size) { + if (!proc) { + assert(0); + return 0; + } return process_read_fd(proc, proc->stderr_pipe, data, size); } @@ -313,44 +326,59 @@ static void process_close_pipes(Process *proc) { proc->stdin_pipe = 0; proc->stdout_pipe = 0; proc->stderr_pipe = 0; + proc->pid = 0; } -void process_kill(Process *proc) { +void process_kill(Process **pproc) { + Process *proc = *pproc; + if (!proc) return; + kill(-proc->pid, SIGKILL); // kill everything in process group // get rid of zombie process waitpid(proc->pid, NULL, 0); - proc->pid = 0; process_close_pipes(proc); free(proc); + *pproc = NULL; } -int process_check_status(Process *proc, char *message, size_t message_size) { +int process_check_status(Process **pproc, char *message, size_t message_size) { + Process *proc = *pproc; + if (!proc) { + assert(0); + if (message) str_printf(message, message_size, "checked status twice"); + return -1; + } + assert(!message || message_size > 0); int wait_status = 0; int ret = waitpid(proc->pid, &wait_status, WNOHANG); if (ret == 0) { // process still running + if (message) + *message = '\0'; return 0; } else if (ret > 0) { if (WIFEXITED(wait_status)) { - process_close_pipes(proc); + process_kill(pproc); int code = WEXITSTATUS(wait_status); if (code == 0) { - str_printf(message, message_size, "exited successfully"); + if (message) str_printf(message, message_size, "exited successfully"); return +1; } else { - str_printf(message, message_size, "exited with code %d", code); + if (message) str_printf(message, message_size, "exited with code %d", code); return -1; } } else if (WIFSIGNALED(wait_status)) { process_close_pipes(proc); - str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status)); + if (message) + str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status)); return -1; } return 0; } else { // this process is gone or something? process_close_pipes(proc); - str_printf(message, message_size, "process ended unexpectedly"); + if (message) + str_printf(message, message_size, "process ended unexpectedly"); return -1; } } diff --git a/os-win.c b/os-win.c index 0e88e68..d7f29ca 100644 --- a/os-win.c +++ b/os-win.c @@ -6,6 +6,7 @@ #include #include + static FsType windows_file_attributes_to_type(DWORD attrs) { if (attrs == INVALID_FILE_ATTRIBUTES) return FS_NON_EXISTENT; @@ -129,6 +130,7 @@ void time_sleep_ns(u64 ns) { Sleep((DWORD)(ns / 1000000)); } +#error "@TODO: fix process functions to take Process**" #error "@TODO : implement process_write, separate_stderr, working_directory" #error "@TODO : make sure process_read & process_write do what they're supposed to for both blocking & non-blocking read/writes." #include "process.h" @@ -244,24 +246,30 @@ void process_kill(Process *process) { } int process_check_status(Process *process, char *message, size_t message_size) { + assert(!message || message_size); HANDLE hProcess = process->process_info.hProcess; DWORD exit_code = 1; if (GetExitCodeProcess(hProcess, &exit_code)) { if (exit_code == STILL_ACTIVE) { + if (message) + *message = '\0'; return 0; } else { process_kill(process); if (exit_code == 0) { - str_printf(message, message_size, "exited successfully"); + if (message) + str_printf(message, message_size, "exited successfully"); return +1; } else { - str_printf(message, message_size, "exited with code %d", (int)exit_code); + if (message) + str_printf(message, message_size, "exited with code %d", (int)exit_code); return -1; } } } else { // something has gone wrong. - str_printf(message, message_size, "couldn't get process exit status"); + if (message) + str_printf(message, message_size, "couldn't get process exit status"); process_kill(process); return -1; } diff --git a/os.h b/os.h index b7d189e..20135ff 100644 --- a/os.h +++ b/os.h @@ -123,10 +123,12 @@ long long process_read_stderr(Process *process, char *data, size_t size); // 1 if the process exited successfully // 0 if the process hasn't exited. // If message is not NULL, it will be set to a description of what happened (e.g. "exited successfully") -int process_check_status(Process *process, char *message, size_t message_size); +// if the process is no longer running, *process will be freed and set to NULL. +int process_check_status(Process **process, char *message, size_t message_size); // kills process if still running -// this also frees any resources used by `process`. -void process_kill(Process *process); +// this also frees any resources used by `*process`. +// *process will be set to NULL. +void process_kill(Process **process); #endif // OS_H_ diff --git a/ted.cfg b/ted.cfg index 8d571c1..c316ff3 100644 --- a/ted.cfg +++ b/ted.cfg @@ -40,7 +40,8 @@ phantom-completions = on lsp-enabled = yes # enable this to log all messages between ted and the LSP server # (may require restarting ted to update) -lsp-log = yes +# the log file is in the same folder as ted.cfg. +lsp-log = off # display function signature help? (only with LSP running) # this is the thing at the bottom of ted which shows the parameters to the function you're calling signature-help-enabled = yes -- cgit v1.2.3