<commit_before>#define LUA_LIB
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>
#include <uv.h>

#define META_NAME "nvim.Loop"
#define UNUSED(x) ((void)x)

typedef struct {
  lua_State *L;
  char read_buffer[0xffff];
  bool connected, reading, exited;
  int data_cb;
  const char *error;
  uv_loop_t loop;
  uv_process_t process;
  uv_process_options_t process_options;
  uv_stdio_container_t stdio[3];
  uv_pipe_t in, out;
  uv_prepare_t prepare;
  uv_timer_t timer;
  uint32_t timeout;
} UV;

static void timer_cb(uv_timer_t *handle)
{
  UV *uv = handle->data;
  uv_stop(&uv->loop);
}

static void prepare_cb(uv_prepare_t *handle)
{
  UV *uv = handle->data;
  uv_timer_start(&uv->timer, timer_cb, uv->timeout, 0);
  uv_prepare_stop(handle);
}

static void walk_cb(uv_handle_t *handle, void *arg) {
  UNUSED(arg);

  if (!uv_is_closing(handle)) {
    uv_close(handle, NULL);
  }
}

static void exit_cb(uv_process_t *proc, int64_t status, int term_signal)
{
  UV *uv;
  UNUSED(status);
  UNUSED(term_signal);

  uv = proc->data;
  uv->error = "EOF";
  /* uv_stop will be called by read_cb once all data is consumed */
}

static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
  UV *uv;
  UNUSED(suggested);

  uv = handle->data;

  if (uv->reading) {
    buf->len = 0;
    buf->base = NULL;
    return;
  }

  buf->len = sizeof(uv->read_buffer);
  buf->base = uv->read_buffer;
  uv->reading = true;
}

static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
{
  UV *uv;
  UNUSED(buf);

  uv = stream->data;

  if (cnt <= 0) {
    if (cnt != UV_ENOBUFS) {
      uv->error = uv_strerror((int)cnt);
      uv_read_stop(stream);
      uv_stop(&uv->loop);
    }
    return;
  }

  /* push data_cb */
  lua_rawgeti(uv->L, LUA_REGISTRYINDEX, uv->data_cb);
  /* push read buffer */
  lua_pushlstring(uv->L, uv->read_buffer, (size_t)cnt);
  /* call data_cb */
  lua_call(uv->L, 1, 0);
  /* restore reading state */
  uv->reading = false;
}

static void write_cb(uv_write_t *req, int status)
{
  UNUSED(status);

  free(req->data);
  free(req);
}

static UV *checkuv(lua_State *L) {
  return luaL_checkudata(L, 1, META_NAME);
}

static int loop_new(lua_State *L) {
  UV *uv = lua_newuserdata(L, sizeof(UV));
  uv->L = L;
  uv->error = NULL;
  uv->connected = false;
  uv->reading = false;
  uv->exited = false;
  uv->data_cb = LUA_REFNIL;
  uv->timeout = 0;
  uv_loop_init(&uv->loop);
  uv_prepare_init(&uv->loop, &uv->prepare);
  uv_timer_init(&uv->loop, &uv->timer);
  uv->prepare.data = uv;
  uv->timer.data = uv;
  luaL_getmetatable(L, META_NAME);
  lua_setmetatable(L, -2);
  return 1;
}

static int loop_exit(lua_State *L) {
  UV *uv = checkuv(L);

  if (uv->exited) {
    return 0;
  }

  uv->exited = true;

  if (uv->data_cb != LUA_REFNIL) {
    luaL_error(L, "This should only be called after stopping the loop");
  }

  /* Call uv_close on every active handle */
  uv_walk(&uv->loop, walk_cb, uv);
  /* Run the event loop until all handles are successfully closed */
  while (uv_loop_close(&uv->loop)) {
    uv_run(&uv->loop, UV_RUN_DEFAULT);
  }

  return 0;
}

static int loop_delete(lua_State *L) {
  return loop_exit(L);
}

static int loop_spawn(lua_State *L) {
  int status;
  size_t i, len;<commit_msg>Work around libuv bug<commit_after>#define LUA_LIB
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <sys/wait.h>

#include <lua.h>
#include <lauxlib.h>
#include <uv.h>

#define META_NAME "nvim.Loop"
#define UNUSED(x) ((void)x)

typedef struct {
  lua_State *L;
  char read_buffer[0xffff];
  bool connected, reading, exited;
  int data_cb;
  const char *error;
  uv_loop_t loop;
  uv_process_t process;
  uv_process_options_t process_options;
  uv_stdio_container_t stdio[3];
  uv_pipe_t in, out;
  uv_prepare_t prepare;
  uv_timer_t timer;
  uint32_t timeout;
} UV;

static void timer_cb(uv_timer_t *handle)
{
  UV *uv = handle->data;
  uv_stop(&uv->loop);
}

static void prepare_cb(uv_prepare_t *handle)
{
  UV *uv = handle->data;
  uv_timer_start(&uv->timer, timer_cb, uv->timeout, 0);
  uv_prepare_stop(handle);
}

static void walk_cb(uv_handle_t *handle, void *arg) {
  UNUSED(arg);

  if (!uv_is_closing(handle)) {
    uv_close(handle, NULL);
  }
}

static void exit_cb(uv_process_t *proc, int64_t status, int term_signal)
{
  UV *uv;
  UNUSED(status);
  UNUSED(term_signal);

  uv = proc->data;
  uv->error = "EOF";
  /* uv_stop will be called by read_cb once all data is consumed */
}

static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
  UV *uv;
  UNUSED(suggested);

  uv = handle->data;

  if (uv->reading) {
    buf->len = 0;
    buf->base = NULL;
    return;
  }

  buf->len = sizeof(uv->read_buffer);
  buf->base = uv->read_buffer;
  uv->reading = true;
}

static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
{
  UV *uv;
  UNUSED(buf);

  uv = stream->data;

  if (cnt <= 0) {
    if (cnt != UV_ENOBUFS) {
      uv->error = uv_strerror((int)cnt);
      uv_read_stop(stream);
      uv_stop(&uv->loop);
    }
    return;
  }

  /* push data_cb */
  lua_rawgeti(uv->L, LUA_REGISTRYINDEX, uv->data_cb);
  /* push read buffer */
  lua_pushlstring(uv->L, uv->read_buffer, (size_t)cnt);
  /* call data_cb */
  lua_call(uv->L, 1, 0);
  /* restore reading state */
  uv->reading = false;
}

static void write_cb(uv_write_t *req, int status)
{
  UNUSED(status);

  free(req->data);
  free(req);
}

static UV *checkuv(lua_State *L) {
  return luaL_checkudata(L, 1, META_NAME);
}

static int loop_new(lua_State *L) {
  UV *uv = lua_newuserdata(L, sizeof(UV));
  uv->L = L;
  uv->error = NULL;
  uv->connected = false;
  uv->reading = false;
  uv->exited = false;
  uv->data_cb = LUA_REFNIL;
  uv->timeout = 0;
  uv_loop_init(&uv->loop);
  uv_prepare_init(&uv->loop, &uv->prepare);
  uv_timer_init(&uv->loop, &uv->timer);
  uv->prepare.data = uv;
  uv->timer.data = uv;
  luaL_getmetatable(L, META_NAME);
  lua_setmetatable(L, -2);
  return 1;
}

static int loop_exit(lua_State *L) {
  int status;
  UV *uv = checkuv(L);

  if (uv->exited) {
    return 0;
  }

  uv->exited = true;

  if (uv->data_cb != LUA_REFNIL) {
    luaL_error(L, "This should only be called after stopping the loop");
  }

  /* Call uv_close on every active handle */
  uv_walk(&uv->loop, walk_cb, uv);
  /* Run the event loop until all handles are successfully closed */
  while (uv_loop_close(&uv->loop)) {
    uv_run(&uv->loop, UV_RUN_DEFAULT);
  }

  /* Work around libuv bug that leaves defunct children:
   * https://github.com/libuv/libuv/issues/154 */
  while (!uv_process_kill(&uv->process, 0)) {
    waitpid(uv->process.pid, &status, WNOHANG);
  }

  return 0;
}

static int loop_delete(lua_State *L) {
  return loop_exit(L);
}

static int loop_spawn(lua_State *L) {
  int status;
  size_t i, len;
{'fraction_non_alphanumeric': 0.15092149180089973, 'fraction_numerical': 0.005514439123494413, 'mean_word_length': 3.666215301286391, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 112, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1112582', 'n_tokens_mistral': 2985, 'n_tokens_neox': 2773, 'n_words': 631}