Files
neovim/src/nvim/event/stream.c
dundargoc 66360675cf build: allow IWYU to fix includes for all .c files
Allow Include What You Use to remove unnecessary includes and only
include what is necessary. This helps with reducing compilation times
and makes it easier to visualise which dependencies are actually
required.

Work on https://github.com/neovim/neovim/issues/549, but doesn't close
it since this only works fully for .c files and not headers.
2022-11-15 10:30:03 +01:00

161 lines
4.4 KiB
C

// 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 <assert.h>
#include <stdbool.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 MSWIN
# include "nvim/os/os_win_console.h"
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/stream.c.generated.h"
#endif
// For compatibility with libuv < 1.19.0 (tested on 1.18.0)
#if UV_VERSION_MINOR < 19
# define uv_stream_get_write_queue_size(stream) stream->write_queue_size
#endif
/// Sets the stream associated with `fd` to "blocking" mode.
///
/// @return `0` on success, or libuv error code on failure.
int stream_set_blocking(int fd, bool blocking)
{
// Private loop to avoid conflict with existing watcher(s):
// uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed.
uv_loop_t loop;
uv_pipe_t stream;
uv_loop_init(&loop);
uv_pipe_init(&loop, &stream, 0);
uv_pipe_open(&stream, fd);
int retval = uv_stream_set_blocking(STRUCT_CAST(uv_stream_t, &stream),
blocking);
uv_close(STRUCT_CAST(uv_handle_t, &stream), NULL);
uv_run(&loop, UV_RUN_NOWAIT); // not necessary, but couldn't hurt.
uv_loop_close(&loop);
return retval;
}
void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
FUNC_ATTR_NONNULL_ARG(2)
{
stream->uvstream = uvstream;
if (fd >= 0) {
uv_handle_type type = uv_guess_handle(fd);
stream->fd = fd;
if (type == UV_FILE) {
// Non-blocking file reads are simulated with an idle handle that reads in
// chunks of the ring buffer size, giving time for other events to be
// processed between reads.
uv_idle_init(&loop->uv, &stream->uv.idle);
stream->uv.idle.data = stream;
} else {
assert(type == UV_NAMED_PIPE || type == UV_TTY);
#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);
DWORD dwMode;
if (GetConsoleMode(stream->uv.tty.handle, &dwMode)) {
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode(stream->uv.tty.handle, dwMode);
}
stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.tty);
} else {
#endif
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 MSWIN
}
#endif
}
}
if (stream->uvstream) {
stream->uvstream->data = stream;
}
stream->internal_data = NULL;
stream->fpos = 0;
stream->curmem = 0;
stream->maxmem = 0;
stream->pending_reqs = 0;
stream->read_cb = NULL;
stream->write_cb = NULL;
stream->close_cb = NULL;
stream->internal_close_cb = NULL;
stream->closed = false;
stream->buffer = NULL;
stream->events = NULL;
stream->num_bytes = 0;
}
void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data)
FUNC_ATTR_NONNULL_ARG(1)
{
assert(!stream->closed);
DLOG("closing Stream: %p", (void *)stream);
stream->closed = true;
stream->close_cb = on_stream_close;
stream->close_cb_data = data;
#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);
}
#endif
if (!stream->pending_reqs) {
stream_close_handle(stream);
}
}
void stream_may_close(Stream *stream)
{
if (!stream->closed) {
stream_close(stream, NULL, NULL);
}
}
void stream_close_handle(Stream *stream)
FUNC_ATTR_NONNULL_ALL
{
if (stream->uvstream) {
if (uv_stream_get_write_queue_size(stream->uvstream) > 0) {
WLOG("closed Stream (%p) with %zu unwritten bytes",
(void *)stream,
uv_stream_get_write_queue_size(stream->uvstream));
}
uv_close((uv_handle_t *)stream->uvstream, close_cb);
} else {
uv_close((uv_handle_t *)&stream->uv.idle, close_cb);
}
}
static void close_cb(uv_handle_t *handle)
{
Stream *stream = handle->data;
if (stream->buffer) {
rbuffer_free(stream->buffer);
}
if (stream->close_cb) {
stream->close_cb(stream, stream->close_cb_data);
}
if (stream->internal_close_cb) {
stream->internal_close_cb(stream, stream->internal_data);
}
}