aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/event.c
blob: cdf40541d55da87084596a2436a7f7bce4aa8273 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include <uv.h>

#include "nvim/os/event.h"
#include "nvim/os/input.h"
#include "nvim/os/channel.h"
#include "nvim/os/server.h"
#include "nvim/os/signal.h"
#include "nvim/os/rstream.h"
#include "nvim/os/job.h"
#include "nvim/vim.h"
#include "nvim/memory.h"
#include "nvim/misc2.h"

#include "nvim/lib/klist.h"

// event will be cleaned up after it gets processed
#define _destroy_event(x)  // do nothing
KLIST_INIT(Event, Event, _destroy_event)

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/event.c.generated.h"
#endif
static klist_t(Event) *event_queue;
static uv_timer_t timer;
static uv_prepare_t timer_prepare;

void event_init()
{
  // Initialize the event queue
  event_queue = kl_init(Event);
  // Initialize input events
  input_init();
  // Timer to wake the event loop if a timeout argument is passed to
  // `event_poll`
  // Signals
  signal_init();
  // Jobs
  job_init();
  // Channels
  channel_init();
  // Servers
  server_init();
  uv_timer_init(uv_default_loop(), &timer);
  // This prepare handle that actually starts the timer
  uv_prepare_init(uv_default_loop(), &timer_prepare);
}

void event_teardown()
{
  channel_teardown();
  job_teardown();
  server_teardown();
}

// Wait for some event
bool event_poll(int32_t ms)
{
  bool timed_out;
  uv_run_mode run_mode = UV_RUN_ONCE;

  if (input_ready()) {
    // If there's a pending input event to be consumed, do it now
    return true;
  }

  input_start();
  timed_out = false;

  if (ms > 0) {
    // Timeout passed as argument to the timer
    timer.data = &timed_out;
    // We only start the timer after the loop is running, for that we
    // use an prepare handle(pass the interval as data to it)
    timer_prepare.data = &ms;
    uv_prepare_start(&timer_prepare, timer_prepare_cb);
  } else if (ms == 0) {
    // For ms == 0, we need to do a non-blocking event poll by
    // setting the run mode to UV_RUN_NOWAIT.
    run_mode = UV_RUN_NOWAIT;
  }

  do {
    // Run one event loop iteration, blocking for events if run_mode is
    // UV_RUN_ONCE
    uv_run(uv_default_loop(), run_mode);
  } while (
      // Continue running if ...
      !input_ready() &&   // we have no input
      kl_empty(event_queue) &&   // no events are waiting to be processed
      run_mode != UV_RUN_NOWAIT &&   // ms != 0
      !timed_out);  // we didn't get a timeout

  input_stop();

  if (ms > 0) {
    // Stop the timer
    uv_timer_stop(&timer);
  }

  return input_ready() || event_is_pending();
}

bool event_is_pending()
{
  return !kl_empty(event_queue);
}

// Push an event to the queue
void event_push(Event event)
{
  *kl_pushp(Event, event_queue) = event;
}

// Runs the appropriate action for each queued event
void event_process()
{
  Event event;

  while (kl_shift(Event, event_queue, &event) == 0) {
    switch (event.type) {
      case kEventSignal:
        signal_handle(event);
        break;
      case kEventRStreamData:
        rstream_read_event(event);
        break;
      case kEventJobExit:
        job_exit_event(event);
        break;
      default:
        abort();
    }
  }
}

// Set a flag in the `event_poll` loop for signaling of a timeout
static void timer_cb(uv_timer_t *handle)
{
  *((bool *)handle->data) = true;
}

static void timer_prepare_cb(uv_prepare_t *handle)
{
  uv_timer_start(&timer, timer_cb, *(uint32_t *)timer_prepare.data, 0);
  uv_prepare_stop(&timer_prepare);
}