aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/event/loop.c
blob: d90565002e89b325e352916d9004a39f80032089 (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
#include <stdint.h>

#include <uv.h>

#include "nvim/event/loop.h"
#include "nvim/event/process.h"

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/loop.c.generated.h"
#endif


void loop_init(Loop *loop, void *data)
{
  uv_loop_init(&loop->uv);
  loop->uv.data = loop;
  loop->deferred_events = kl_init(Event);
  loop->immediate_events = kl_init(Event);
  loop->children = kl_init(WatcherPtr);
  loop->children_stop_requests = 0;
  uv_signal_init(&loop->uv, &loop->children_watcher);
  uv_timer_init(&loop->uv, &loop->children_kill_timer);
}

void loop_poll_events(Loop *loop, int ms)
{
  static int recursive = 0;

  if (recursive++) {
    abort();  // Should not re-enter uv_run
  }

  bool wait = true;
  uv_timer_t timer;

  if (ms > 0) {
    uv_timer_init(&loop->uv, &timer);
    // Use a repeating timeout of ms milliseconds to make sure
    // we do not block indefinitely for I/O.
    uv_timer_start(&timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
  } 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.
    wait = false;
  }

  if (wait) {
    loop_run_once(loop);
  } else {
    loop_run_nowait(loop);
  }

  if (ms > 0) {
    // Ensure the timer handle is closed and run the event loop
    // once more to let libuv perform it's cleanup
    uv_timer_stop(&timer);
    uv_close((uv_handle_t *)&timer, NULL);
    loop_run_nowait(loop);
  }

  recursive--;  // Can re-enter uv_run now
  process_events_from(loop->immediate_events);
}

bool loop_has_deferred_events(Loop *loop)
{
  return loop->deferred_events_allowed && !kl_empty(loop->deferred_events);
}

void loop_enable_deferred_events(Loop *loop)
{
  ++loop->deferred_events_allowed;
}

void loop_disable_deferred_events(Loop *loop)
{
  --loop->deferred_events_allowed;
}

// Queue an event
void loop_push_event(Loop *loop, Event event, bool deferred)
{
  // Sometimes libuv will run pending callbacks(timer for example) before
  // blocking for a poll. If this happens and the callback pushes a event to one
  // of the queues, the event would only be processed after the poll
  // returns(user hits a key for example). To avoid this scenario, we call
  // uv_stop when a event is enqueued.
  loop_stop(loop);
  kl_push(Event, deferred ? loop->deferred_events : loop->immediate_events,
      event);
}

void loop_process_event(Loop *loop)
{
  process_events_from(loop->deferred_events);
}


void loop_run(Loop *loop)
{
  uv_run(&loop->uv, UV_RUN_DEFAULT);
}

void loop_run_once(Loop *loop)
{
  uv_run(&loop->uv, UV_RUN_ONCE);
}

void loop_run_nowait(Loop *loop)
{
  uv_run(&loop->uv, UV_RUN_NOWAIT);
}

void loop_stop(Loop *loop)
{
  uv_stop(&loop->uv);
}

void loop_close(Loop *loop)
{
  uv_close((uv_handle_t *)&loop->children_watcher, NULL);
  uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
  do {
    uv_run(&loop->uv, UV_RUN_DEFAULT);
  } while (uv_loop_close(&loop->uv));
}

void loop_process_all_events(Loop *loop)
{
  process_events_from(loop->immediate_events);
  process_events_from(loop->deferred_events);
}

static void process_events_from(klist_t(Event) *queue)
{
  while (!kl_empty(queue)) {
    Event event = kl_shift(Event, queue);
    event.handler(event);
  }
}

static void timer_cb(uv_timer_t *handle)
{
}