diff options
Diffstat (limited to 'src/nvim/event')
-rw-r--r-- | src/nvim/event/libuv_process.c | 25 | ||||
-rw-r--r-- | src/nvim/event/libuv_process.h | 3 | ||||
-rw-r--r-- | src/nvim/event/loop.c | 49 | ||||
-rw-r--r-- | src/nvim/event/loop.h | 6 | ||||
-rw-r--r-- | src/nvim/event/multiqueue.c | 6 | ||||
-rw-r--r-- | src/nvim/event/process.c | 44 | ||||
-rw-r--r-- | src/nvim/event/process.h | 14 | ||||
-rw-r--r-- | src/nvim/event/rstream.c | 9 | ||||
-rw-r--r-- | src/nvim/event/signal.c | 1 | ||||
-rw-r--r-- | src/nvim/event/signal.h | 3 | ||||
-rw-r--r-- | src/nvim/event/socket.c | 13 | ||||
-rw-r--r-- | src/nvim/event/socket.h | 3 | ||||
-rw-r--r-- | src/nvim/event/stream.c | 12 | ||||
-rw-r--r-- | src/nvim/event/stream.h | 8 | ||||
-rw-r--r-- | src/nvim/event/time.h | 4 | ||||
-rw-r--r-- | src/nvim/event/wstream.c | 6 | ||||
-rw-r--r-- | src/nvim/event/wstream.h | 3 |
17 files changed, 155 insertions, 54 deletions
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 0251ea9957..e528d21a71 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -2,16 +2,18 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <assert.h> +#include <stdint.h> #include <uv.h> +#include "nvim/eval/typval.h" #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/process.h" -#include "nvim/event/rstream.h" -#include "nvim/event/wstream.h" +#include "nvim/event/stream.h" #include "nvim/log.h" #include "nvim/macros.h" #include "nvim/os/os.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/libuv_process.c.generated.h" @@ -25,7 +27,7 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvopts.file = proc->argv[0]; uvproc->uvopts.args = proc->argv; uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE; -#ifdef WIN32 +#ifdef MSWIN // libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe // expects a different syntax (must be prepared by the caller before now). if (os_shell_is_cmdexe(proc->argv[0])) { @@ -40,11 +42,19 @@ int libuv_process_spawn(LibuvProcess *uvproc) #endif uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = proc->cwd; + uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; uvproc->uvstdio[0].flags = UV_IGNORE; uvproc->uvstdio[1].flags = UV_IGNORE; uvproc->uvstdio[2].flags = UV_IGNORE; + + if (ui_client_forward_stdin) { + assert(UI_CLIENT_STDIN_FD == 3); + uvproc->uvopts.stdio_count = 4; + uvproc->uvstdio[3].data.fd = 0; + uvproc->uvstdio[3].flags = UV_INHERIT_FD; + } uvproc->uv.data = proc; if (proc->env) { @@ -55,7 +65,7 @@ int libuv_process_spawn(LibuvProcess *uvproc) if (!proc->in.closed) { uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; -#ifdef WIN32 +#ifdef MSWIN uvproc->uvstdio[0].flags |= proc->overlapped ? UV_OVERLAPPED_PIPE : 0; #endif uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t, @@ -64,7 +74,7 @@ int libuv_process_spawn(LibuvProcess *uvproc) if (!proc->out.closed) { uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; -#ifdef WIN32 +#ifdef MSWIN // pipe must be readable for IOCP to work on Windows. uvproc->uvstdio[1].flags |= proc->overlapped ? (UV_READABLE_PIPE | UV_OVERLAPPED_PIPE) : 0; @@ -77,6 +87,9 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; uvproc->uvstdio[2].data.stream = STRUCT_CAST(uv_stream_t, &proc->err.uv.pipe); + } else if (proc->fwd_err) { + uvproc->uvstdio[2].flags = UV_INHERIT_FD; + uvproc->uvstdio[2].data.fd = STDERR_FILENO; } int status; @@ -113,7 +126,7 @@ static void close_cb(uv_handle_t *handle) static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) { Process *proc = handle->data; -#if defined(WIN32) +#if defined(MSWIN) // Use stored/expected signal. term_signal = proc->exit_signal; #endif diff --git a/src/nvim/event/libuv_process.h b/src/nvim/event/libuv_process.h index 1132ce79ca..4472839944 100644 --- a/src/nvim/event/libuv_process.h +++ b/src/nvim/event/libuv_process.h @@ -3,13 +3,14 @@ #include <uv.h> +#include "nvim/event/loop.h" #include "nvim/event/process.h" typedef struct libuv_process { Process process; uv_process_t uv; uv_process_options_t uvopts; - uv_stdio_container_t uvstdio[3]; + uv_stdio_container_t uvstdio[4]; } LibuvProcess; static inline LibuvProcess libuv_process_init(Loop *loop, void *data) diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 1b5cc23b09..ab2524c1a9 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -1,13 +1,16 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include <stdarg.h> +#include <stdbool.h> #include <stdint.h> +#include <stdlib.h> #include <uv.h> +#include "nvim/event/defs.h" #include "nvim/event/loop.h" -#include "nvim/event/process.h" #include "nvim/log.h" +#include "nvim/memory.h" +#include "nvim/os/time.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/loop.c.generated.h" @@ -31,41 +34,57 @@ void loop_init(Loop *loop, void *data) loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag } -/// Processes one `Loop.uv` event (at most). -/// Processes all `Loop.fast_events` events. -/// Does NOT process `Loop.events`, that is an application-specific decision. +/// Process `Loop.uv` events with a timeout. /// /// @param loop -/// @param ms 0: non-blocking poll. -/// >0: timeout after `ms`. -/// <0: wait forever. -/// @returns true if `ms` timeout was reached -bool loop_poll_events(Loop *loop, int ms) +/// @param ms 0: non-blocking poll. +/// > 0: timeout after `ms`. +/// < 0: wait forever. +/// @param once true: process at most one `Loop.uv` event. +/// false: process until `ms` timeout (only has effect if `ms` > 0). +/// @return true if `ms` > 0 and was reached +bool loop_uv_run(Loop *loop, int64_t ms, bool once) { if (loop->recursive++) { abort(); // Should not re-enter uv_run } uv_run_mode mode = UV_RUN_ONCE; - bool timeout_expired = false; + bool *timeout_expired = loop->poll_timer.data; + *timeout_expired = false; if (ms > 0) { - *((bool *)loop->poll_timer.data) = false; // reset "timeout expired" flag - // Dummy timer to ensure UV_RUN_ONCE does not block indefinitely for I/O. + // This timer ensures UV_RUN_ONCE does not block indefinitely for I/O. uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms); } else if (ms == 0) { // For ms == 0, do a non-blocking event poll. mode = UV_RUN_NOWAIT; } - uv_run(&loop->uv, mode); + do { // -V1044 + uv_run(&loop->uv, mode); + } while (ms > 0 && !once && !*timeout_expired); // -V560 if (ms > 0) { - timeout_expired = *((bool *)loop->poll_timer.data); uv_timer_stop(&loop->poll_timer); } loop->recursive--; // Can re-enter uv_run now + return *timeout_expired; +} + +/// Processes one `Loop.uv` event (at most). +/// Processes all `Loop.fast_events` events. +/// Does NOT process `Loop.events`, that is an application-specific decision. +/// +/// @param loop +/// @param ms 0: non-blocking poll. +/// > 0: timeout after `ms`. +/// < 0: wait forever. +/// @return true if `ms` > 0 and was reached +bool loop_poll_events(Loop *loop, int64_t ms) +{ + bool timeout_expired = loop_uv_run(loop, ms, true); multiqueue_process_events(loop->fast_events); return timeout_expired; } diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index 65980c6c05..b2265a726d 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -4,8 +4,8 @@ #include <stdint.h> #include <uv.h> +#include "klib/klist.h" #include "nvim/event/multiqueue.h" -#include "nvim/lib/klist.h" #include "nvim/os/time.h" typedef void *WatcherPtr; @@ -58,7 +58,7 @@ typedef struct loop { // Poll for events until a condition or timeout #define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ do { \ - int remaining = timeout; \ + int64_t remaining = timeout; \ uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ while (!(condition)) { \ LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ @@ -66,7 +66,7 @@ typedef struct loop { break; \ } else if (remaining > 0) { \ uint64_t now = os_hrtime(); \ - remaining -= (int)((now - before) / 1000000); \ + remaining -= (int64_t)((now - before) / 1000000); \ before = now; \ if (remaining <= 0) { \ break; \ diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 40d20033e0..e05084b656 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -46,14 +46,14 @@ // other sources and focus on a specific channel. #include <assert.h> -#include <stdarg.h> #include <stdbool.h> -#include <stdint.h> +#include <stddef.h> #include <uv.h> +#include "nvim/event/defs.h" #include "nvim/event/multiqueue.h" +#include "nvim/lib/queue.h" #include "nvim/memory.h" -#include "nvim/os/time.h" typedef struct multiqueue_item MultiQueueItem; struct multiqueue_item { diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index e029f778f6..1a524a56ca 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -2,20 +2,25 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <assert.h> +#include <inttypes.h> +#include <signal.h> #include <stdlib.h> #include <uv.h> +#include "klib/klist.h" #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/process.h" -#include "nvim/event/rstream.h" -#include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/macros.h" +#include "nvim/main.h" #include "nvim/os/process.h" #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" +#include "nvim/os/time.h" +#include "nvim/rbuffer.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/process.c.generated.h" @@ -32,10 +37,16 @@ void __gcov_flush(void); static bool process_is_tearing_down = false; +// Delay exit until handles are closed, to avoid deadlocks +static int exit_need_delay = 0; + /// @returns zero on success, or negative error code int process_spawn(Process *proc, bool in, bool out, bool err) FUNC_ATTR_NONNULL_ALL { + // forwarding stderr contradicts with processing it internally + assert(!(err && proc->fwd_err)); + if (in) { uv_pipe_init(&proc->loop->uv, &proc->in.uv.pipe, 0); } else { @@ -395,12 +406,41 @@ static void process_close_handles(void **argv) exit_need_delay--; } +static void exit_delay_cb(uv_timer_t *handle) +{ + uv_timer_stop(&main_loop.exit_delay_timer); + multiqueue_put(main_loop.fast_events, exit_event, 1, main_loop.exit_delay_timer.data); +} + +static void exit_event(void **argv) +{ + int status = (int)(intptr_t)argv[0]; + if (exit_need_delay) { + main_loop.exit_delay_timer.data = argv[0]; + uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0); + return; + } + + if (!exiting) { + os_exit(status); + } +} + +void exit_from_channel(int status) +{ + multiqueue_put(main_loop.fast_events, exit_event, 1, status); +} + static void on_process_exit(Process *proc) { Loop *loop = proc->loop; ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status, proc->stopped_time); + if (ui_client_channel_id) { + exit_from_channel(proc->status); + } + // Process has terminated, but there could still be data to be read from the // OS. We are still in the libuv loop, so we cannot call code that polls for // more data directly. Instead delay the reading after the libuv loop by diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 30254bfe07..e0057faffb 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -1,11 +1,20 @@ #ifndef NVIM_EVENT_PROCESS_H #define NVIM_EVENT_PROCESS_H +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/rstream.h" +#include "nvim/event/stream.h" #include "nvim/event/wstream.h" +struct process; + typedef enum { kProcessTypeUv, kProcessTypePty, @@ -29,7 +38,7 @@ struct process { /// Exit handler. If set, user must call process_free(). process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; - bool closed, detach, overlapped; + bool closed, detach, overlapped, fwd_err; MultiQueue *events; }; @@ -53,7 +62,8 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data) .closed = false, .internal_close_cb = NULL, .internal_exit_cb = NULL, - .detach = false + .detach = false, + .fwd_err = false, }; } diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 2847788ef8..a88d62fd6b 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -3,17 +3,18 @@ #include <assert.h> #include <stdbool.h> +#include <stddef.h> #include <stdint.h> -#include <stdlib.h> #include <uv.h> -#include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/event/rstream.h" +#include "nvim/event/stream.h" #include "nvim/log.h" +#include "nvim/macros.h" #include "nvim/main.h" -#include "nvim/memory.h" -#include "nvim/vim.h" +#include "nvim/os/os_defs.h" +#include "nvim/rbuffer.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/rstream.c.generated.h" diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c index 4a45a2ec2f..8256ca2091 100644 --- a/src/nvim/event/signal.c +++ b/src/nvim/event/signal.c @@ -1,6 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <stddef.h> #include <uv.h> #include "nvim/event/loop.h" diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h index 7fe352edef..f9adf62c20 100644 --- a/src/nvim/event/signal.h +++ b/src/nvim/event/signal.h @@ -4,6 +4,9 @@ #include <uv.h> #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" + +struct signal_watcher; typedef struct signal_watcher SignalWatcher; typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data); diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index 9496a568b9..10756015ad 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -2,23 +2,24 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <assert.h> -#include <stdint.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> #include <uv.h> #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/event/loop.h" -#include "nvim/event/rstream.h" #include "nvim/event/socket.h" -#include "nvim/event/wstream.h" +#include "nvim/event/stream.h" +#include "nvim/gettext.h" #include "nvim/log.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/memory.h" #include "nvim/os/os.h" #include "nvim/path.h" -#include "nvim/strings.h" -#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/socket.c.generated.h" @@ -125,7 +126,7 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) // Libuv converts ENOENT to EACCES for Windows compatibility, but if // the parent directory does not exist, ENOENT would be more accurate. *path_tail(watcher->addr) = NUL; - if (!os_path_exists((char_u *)watcher->addr)) { + if (!os_path_exists(watcher->addr)) { result = UV_ENOENT; } } diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h index d30ae45502..c6fcdec4bb 100644 --- a/src/nvim/event/socket.h +++ b/src/nvim/event/socket.h @@ -4,9 +4,12 @@ #include <uv.h> #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" +struct socket_watcher; + #define ADDRESS_MAX_SIZE 256 typedef struct socket_watcher SocketWatcher; diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index b34fd73d52..0a4918636a 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -3,14 +3,16 @@ #include <assert.h> #include <stdbool.h> -#include <stdio.h> +#include <stddef.h> #include <uv.h> +#include <uv/version.h> +#include "nvim/event/loop.h" #include "nvim/event/stream.h" #include "nvim/log.h" #include "nvim/macros.h" #include "nvim/rbuffer.h" -#ifdef WIN32 +#ifdef MSWIN # include "nvim/os/os_win_console.h" #endif @@ -60,7 +62,7 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream) stream->uv.idle.data = stream; } else { assert(type == UV_NAMED_PIPE || type == UV_TTY); -#ifdef WIN32 +#ifdef MSWIN if (type == UV_TTY) { uv_tty_init(&loop->uv, &stream->uv.tty, fd, 0); uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_RAW); @@ -75,7 +77,7 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream) uv_pipe_init(&loop->uv, &stream->uv.pipe, 0); uv_pipe_open(&stream->uv.pipe, fd); stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.pipe); -#ifdef WIN32 +#ifdef MSWIN } #endif } @@ -109,7 +111,7 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) stream->close_cb = on_stream_close; stream->close_cb_data = data; -#ifdef WIN32 +#ifdef MSWIN if (UV_TTY == uv_guess_handle(stream->fd)) { // Undo UV_TTY_MODE_RAW from stream_init(). #10801 uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_NORMAL); diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index 02e816b4be..33d2d6e775 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -6,8 +6,11 @@ #include <uv.h> #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/rbuffer.h" +struct stream; + typedef struct stream Stream; /// Type of function called when the Stream buffer is filled with data /// @@ -16,8 +19,7 @@ typedef struct stream Stream; /// @param count Number of bytes that was read. /// @param data User-defined data /// @param eof If the stream reached EOF. -typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, - void *data, bool eof); +typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof); /// Type of function called when the Stream has information about a write /// request. @@ -35,7 +37,7 @@ struct stream { uv_pipe_t pipe; uv_tcp_t tcp; uv_idle_t idle; -#ifdef WIN32 +#ifdef MSWIN uv_tty_t tty; #endif } uv; diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h index a6de89ad6e..e84488fdd6 100644 --- a/src/nvim/event/time.h +++ b/src/nvim/event/time.h @@ -1,9 +1,13 @@ #ifndef NVIM_EVENT_TIME_H #define NVIM_EVENT_TIME_H +#include <stdbool.h> #include <uv.h> #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" + +struct time_watcher; typedef struct time_watcher TimeWatcher; typedef void (*time_cb)(TimeWatcher *watcher, void *data); diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index d81ffa5c15..65391ba5cf 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -3,15 +3,13 @@ #include <assert.h> #include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> #include <uv.h> #include "nvim/event/loop.h" +#include "nvim/event/stream.h" #include "nvim/event/wstream.h" -#include "nvim/log.h" +#include "nvim/macros.h" #include "nvim/memory.h" -#include "nvim/vim.h" #define DEFAULT_MAXMEM 1024 * 1024 * 2000 diff --git a/src/nvim/event/wstream.h b/src/nvim/event/wstream.h index d599d29913..ef1311c619 100644 --- a/src/nvim/event/wstream.h +++ b/src/nvim/event/wstream.h @@ -2,12 +2,15 @@ #define NVIM_EVENT_WSTREAM_H #include <stdbool.h> +#include <stddef.h> #include <stdint.h> #include <uv.h> #include "nvim/event/loop.h" #include "nvim/event/stream.h" +struct wbuffer; + typedef struct wbuffer WBuffer; typedef void (*wbuffer_data_finalizer)(void *data); |