]> git.neil.brown.name Git - edlib.git/blob - lib-libevent.c
TODO: clean out done items.
[edlib.git] / lib-libevent.c
1 /*
2  * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * libevent support for edlib.
6  *
7  * Register command "attach-libevent".
8  * When that is called, register:
9  *   "event:read"
10  *   "event:write"
11  *   "event:signal"
12  *   "event:run"
13  *   "event:deactivate"
14  *
15  * When "event:deactivate" is called, cause event:run to abort.
16  */
17
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <event.h>
21 #include <string.h>
22 #define PANE_DATA_TYPE struct event_info
23 #include "core.h"
24 #include "misc.h"
25
26 enum {
27         EV_LIST,        /* Events handled by libevent */
28         POLL_LIST,      /* Events to poll before calling event_base_loop */
29         PRIO_0_LIST,    /* background task - run one per loop */
30         PRIO_1_LIST,    /* non-trivial follow-up tasks, line pane_refresh */
31         PRIO_2_LIST,    /* fast follow-up tasks like freeing memory */
32         NR_LISTS
33 };
34
35 struct event_info {
36         struct event_base *base;
37         struct list_head event_list[NR_LISTS];
38         struct pane *home safe;
39         int dont_block;
40         int deactivated;
41         struct command read, write, signal, timer, poll, on_idle,
42                 run, deactivate, free, refresh, noblock;
43 };
44 #include "core-pane.h"
45
46 static struct map *libevent_map;
47 DEF_LOOKUP_CMD(libevent_handle, libevent_map);
48
49 struct evt {
50         struct event *l;
51         struct pane *home safe;
52         char *event safe;
53         struct command *comm safe;
54         struct list_head lst;
55         int active;     /* Don't delete or free this event, it is running */
56         int num;        /* signal or mseconds or fd */
57 };
58
59 static void call_event(int thing, short sev, void *evv)
60 {
61         struct evt *ev safe = safe_cast evv;
62         int type;
63
64         if (sev & EV_SIGNAL)
65                 type = TIME_SIG;
66         else
67                 type = TIME_READ;
68
69         ev->active = 1;
70         time_start(type);
71         if (comm_call(ev->comm, "callback:event", ev->home, thing) < 0 ||
72             ev->active == 2) {
73                 event_del(ev->l);
74                 event_free(ev->l);
75                 list_del(&ev->lst);
76                 command_put(ev->comm);
77                 free(ev);
78         } else
79                 ev->active = 0;
80         time_stop(type);
81 }
82
83 static void call_timeout_event(int thing, short sev, void *evv)
84 {
85         struct evt *ev safe = safe_cast evv;
86
87         ev->active = 1;
88         time_start(TIME_TIMER);
89         if (comm_call(ev->comm, "callback:event", ev->home, thing) < 0 ||
90             ev->active == 2) {
91                 event_free(ev->l);
92                 list_del(&ev->lst);
93                 command_put(ev->comm);
94                 free(ev);
95         } else {
96                 struct timeval tv;
97                 tv.tv_sec = ev->num / 1000;
98                 tv.tv_usec = (ev->num % 1000) * 1000;
99                 ev->active = 0;
100                 event_add(ev->l, &tv);
101         }
102         time_stop(TIME_TIMER);
103 }
104
105 DEF_CB(libevent_read)
106 {
107         struct event_info *ei = container_of(ci->comm, struct event_info, read);
108         struct evt *ev;
109
110         if (!ci->comm2)
111                 return Enoarg;
112
113         /* If there is already an event with this 'fd', we need
114          * to remove it now, else libevent gets confused.
115          * Presumably call_event() is now running and will clean up
116          * soon.
117          */
118         list_for_each_entry(ev, &ei->event_list[EV_LIST], lst) {
119                 int fd = event_get_fd(ev->l);
120                 if (fd >= 0 && ci->num >= 0 && fd == ci->num)
121                         event_del(ev->l);
122         }
123
124         ev = malloc(sizeof(*ev));
125
126         if (!ei->base)
127                 ei->base = event_base_new();
128
129         ev->l = safe_cast event_new(ei->base, ci->num, EV_READ|EV_PERSIST,
130                                     call_event, ev);
131         ev->home = ci->focus;
132         ev->comm = command_get(ci->comm2);
133         ev->num = ci->num;
134         ev->active = 0;
135         ev->event = "event:read";
136         pane_add_notify(ei->home, ev->home, "Notify:Close");
137         list_add(&ev->lst, &ei->event_list[EV_LIST]);
138         event_add(ev->l, NULL);
139         return 1;
140 }
141
142 DEF_CB(libevent_write)
143 {
144         struct event_info *ei = container_of(ci->comm, struct event_info, write);
145         struct evt *ev;
146
147         if (!ci->comm2)
148                 return Enoarg;
149
150         /* If there is already an event with this 'fd', we need
151          * to remove it now, else libevent gets confused.
152          * Presumably call_event() is now running and will clean up
153          * soon.
154          */
155         list_for_each_entry(ev, &ei->event_list[EV_LIST], lst) {
156                 int fd = event_get_fd(ev->l);
157                 if (fd >= 0 && ci->num >= 0 && fd == ci->num)
158                         event_del(ev->l);
159         }
160
161         ev = malloc(sizeof(*ev));
162
163         if (!ei->base)
164                 ei->base = event_base_new();
165
166         ev->l = safe_cast event_new(ei->base, ci->num, EV_WRITE|EV_PERSIST,
167                                     call_event, ev);
168         ev->home = ci->focus;
169         ev->comm = command_get(ci->comm2);
170         ev->num = ci->num;
171         ev->active = 0;
172         ev->event = "event:write";
173         pane_add_notify(ei->home, ev->home, "Notify:Close");
174         list_add(&ev->lst, &ei->event_list[EV_LIST]);
175         event_add(ev->l, NULL);
176         return 1;
177 }
178
179 DEF_CB(libevent_signal)
180 {
181         struct event_info *ei = container_of(ci->comm, struct event_info, signal);
182         struct evt *ev;
183
184         if (!ci->comm2)
185                 return Enoarg;
186
187         ev = malloc(sizeof(*ev));
188
189         if (!ei->base)
190                 ei->base = event_base_new();
191
192         ev->l = safe_cast event_new(ei->base, ci->num, EV_SIGNAL|EV_PERSIST,
193                                     call_event, ev);
194         ev->home = ci->focus;
195         ev->comm = command_get(ci->comm2);
196         ev->num = ci->num;
197         ev->active = 0;
198         ev->event = "event:signal";
199         pane_add_notify(ei->home, ev->home, "Notify:Close");
200         list_add(&ev->lst, &ei->event_list[EV_LIST]);
201         event_add(ev->l, NULL);
202         return 1;
203 }
204
205 DEF_CB(libevent_timer)
206 {
207         struct event_info *ei = container_of(ci->comm, struct event_info, timer);
208         struct evt *ev;
209         struct timeval tv;
210
211         if (!ci->comm2)
212                 return Enoarg;
213
214         ev = malloc(sizeof(*ev));
215
216         if (!ei->base)
217                 ei->base = event_base_new();
218
219         ev->l = safe_cast event_new(ei->base, -1, 0,
220                                     call_timeout_event, ev);
221         ev->home = ci->focus;
222         ev->comm = command_get(ci->comm2);
223         ev->num = ci->num;
224         ev->active = 0;
225         ev->event = "event:timer";
226         pane_add_notify(ei->home, ev->home, "Notify:Close");
227         list_add(&ev->lst, &ei->event_list[EV_LIST]);
228         tv.tv_sec = ev->num / 1000;
229         tv.tv_usec = (ev->num % 1000) * 1000;
230         event_add(ev->l, &tv);
231         return 1;
232 }
233
234 DEF_CB(libevent_poll)
235 {
236         struct event_info *ei = container_of(ci->comm, struct event_info, poll);
237         struct evt *ev;
238
239         if (!ci->comm2)
240                 return Enoarg;
241
242         ev = malloc(sizeof(*ev));
243
244         if (!ei->base)
245                 ei->base = event_base_new();
246
247         ev->home = ci->focus;
248         ev->comm = command_get(ci->comm2);
249         ev->active = 0;
250         ev->event = "event:poll";
251         ev->num = -1;
252         pane_add_notify(ei->home, ev->home, "Notify:Close");
253         list_add(&ev->lst, &ei->event_list[POLL_LIST]);
254         return 1;
255 }
256
257 DEF_CMD(libevent_on_idle)
258 {
259         struct event_info *ei = container_of(ci->comm, struct event_info, on_idle);
260         struct evt *ev;
261         int prio = ci->num;
262
263         if (!ci->comm2)
264                 return Enoarg;
265
266         ev = malloc(sizeof(*ev));
267
268         if (!ei->base)
269                 ei->base = event_base_new();
270
271         ev->home = ci->focus;
272         pane_add_notify(ei->home, ev->home, "Notify:Close");
273         ev->comm = command_get(ci->comm2);
274         ev->active = 0;
275         ev->event = "event:on-idle";
276         if (prio < 0)
277                 prio = 0;
278         if (prio > 2)
279                 prio = 2;
280         ev->num = prio;
281         list_add(&ev->lst, &ei->event_list[PRIO_0_LIST + prio]);
282         return 1;
283 }
284
285 static int run_list(struct event_info *ei safe, int list, char *cb safe,
286                     bool stop_on_first)
287 {
288         bool dont_block = False;
289         struct evt *ev;
290
291         list_for_each_entry(ev, &ei->event_list[list], lst) {
292                 ev->active = 1;
293                 if (comm_call(ev->comm, cb, ev->home, ev->num) >= 1)
294                         dont_block = True;
295                 if (ev->active == 2 || list >= PRIO_0_LIST) {
296                         list_del(&ev->lst);
297                         command_put(ev->comm);
298                         free(ev);
299                         break;
300                 } else
301                         ev->active = 0;
302                 if (dont_block && stop_on_first)
303                         /* Other things might have been removed from list */
304                         break;
305         }
306         return dont_block;
307 }
308
309 DEF_CB(libevent_run)
310 {
311         struct event_info *ei = container_of(ci->comm, struct event_info, run);
312         struct event_base *b = ei->base;
313         int dont_block = ei->dont_block;
314         struct evt *ev;
315         int i;
316
317         ei->dont_block = 0;
318
319         if (ei->deactivated)
320                 return Efallthrough;
321         if (!b) {
322                 /* No events to wait for.. */
323                 if (dont_block)
324                         return 1;
325                 return 0;
326         }
327
328         /* First run any 'poll' events */
329         if (run_list(ei, POLL_LIST, "callback:poll", True))
330                 dont_block = True;
331
332         for (i = PRIO_0_LIST ; i <= PRIO_2_LIST; i++)
333                 if (!list_empty(&ei->event_list[i]))
334                         dont_block = 1;
335
336         /* Disable any alarm set by python (or other interpreter) */
337         alarm(0);
338         event_base_loop(b, EVLOOP_ONCE | (dont_block ? EVLOOP_NONBLOCK : 0));
339
340         time_start(TIME_IDLE);
341         /* Prio 2 comes first - unconditional */
342         run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
343         /* Now prio1 */
344         run_list(ei, PRIO_1_LIST, "callback:on-idle", False);
345         /* Repeat PRIO_2 just in case */
346         run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
347         /* And do one background task */
348         run_list(ei, PRIO_0_LIST, "callback:on-idle", True);
349         time_stop(TIME_IDLE);
350
351         /* Check if we have been deactivated. */
352         if (ei->base == b)
353                 return 1;
354
355         for (i = 0 ; i < NR_LISTS; i++) {
356                 while (!list_empty(&ei->event_list[i])) {
357                         ev = list_first_entry(&ei->event_list[i], struct evt, lst);
358                         list_del(&ev->lst);
359                         if (i == EV_LIST) {
360                                 event_del(ev->l);
361                                 event_free(ev->l);
362                         }
363                         command_put(ev->comm);
364                         free(ev);
365                 }
366         }
367         event_base_free(b);
368         return Efail;
369 }
370
371 DEF_CB(libevent_deactivate)
372 {
373         struct event_info *ei = container_of(ci->comm, struct event_info, deactivate);
374         ei->base = NULL;
375         ei->deactivated = 1;
376         return 1;
377 }
378
379 DEF_CB(libevent_free)
380 {
381         /* destroy for ci->focus and, if comm2 given, which activate
382          * comm2
383          */
384         struct evt *ev;
385         struct list_head *tmp;
386         struct event_info *ei = container_of(ci->comm, struct event_info, free);
387         int i;
388
389         for (i = 0; i < NR_LISTS; i++) {
390                 list_for_each_entry_safe(ev, tmp, &ei->event_list[i], lst)
391                         if (ev->home == ci->focus &&
392                             (ci->comm2 == NULL || ev->comm == ci->comm2)) {
393                                 list_del_init(&ev->lst);
394                                 if (ev->active)
395                                         ev->active = 2;
396                                 else {
397                                         if (i == EV_LIST) {
398                                                 event_del(ev->l);
399                                                 event_free(ev->l);
400                                         }
401                                         command_put(ev->comm);
402                                         free(ev);
403                                 }
404                         }
405         }
406         return 1;
407 }
408
409 DEF_CB(libevent_refresh)
410 {
411         struct evt *ev;
412         struct list_head *tmp;
413         struct event_info *ei = container_of(ci->comm, struct event_info, refresh);
414         struct list_head old;
415         int i;
416
417         for (i = 0; i < NR_LISTS; i++) {
418                 list_add(&old, &ei->event_list[i]);
419                 list_del_init(&ei->event_list[i]);
420                 list_for_each_entry_safe(ev, tmp, &old, lst) {
421                         if (i == EV_LIST) {
422                                 event_del(ev->l);
423                                 event_free(ev->l);
424                         }
425                         list_del(&ev->lst);
426                         call_comm(ev->event, ev->home, ev->comm, ev->num);
427                         command_put(ev->comm);
428                         free(ev);
429                 }
430         }
431         return Efallthrough;
432 }
433
434 DEF_CB(libevent_noblock)
435 {
436         struct event_info *ei = container_of(ci->comm, struct event_info,
437                                              noblock);
438
439         ei->dont_block = 1;
440         return 1;
441 }
442
443 DEF_CMD(libevent_notify)
444 {
445         struct event_info *ei = ci->home->data;
446
447         home_comm_call(ci->home, &ei->free, "free", ci->focus);
448         return 1;
449 }
450
451 DEF_CMD(libevent_activate)
452 {
453         struct event_info *ei;
454         struct pane *p;
455         int i;
456
457         p = pane_register(pane_root(ci->home), 0, &libevent_handle.c);
458         if (!p)
459                 return Efail;
460         ei = p->data;
461         ei->home = p;
462         for (i = 0; i < NR_LISTS; i++)
463                 INIT_LIST_HEAD(&ei->event_list[i]);
464         ei->read = libevent_read;
465         ei->write = libevent_write;
466         ei->signal = libevent_signal;
467         ei->timer = libevent_timer;
468         ei->poll = libevent_poll;
469         ei->on_idle = libevent_on_idle;
470         ei->run = libevent_run;
471         ei->deactivate = libevent_deactivate;
472         ei->free = libevent_free;
473         ei->refresh = libevent_refresh;
474         ei->noblock = libevent_noblock;
475
476         /* These are defaults, so make them sort late */
477         call_comm("global-set-command", ci->focus, &ei->read,
478                   0, NULL, "event:read-zz");
479         call_comm("global-set-command", ci->focus, &ei->write,
480                   0, NULL, "event:write-zz");
481         call_comm("global-set-command", ci->focus, &ei->signal,
482                   0, NULL, "event:signal-zz");
483         call_comm("global-set-command", ci->focus, &ei->timer,
484                   0, NULL, "event:timer-zz");
485         call_comm("global-set-command", ci->focus, &ei->poll,
486                   0, NULL, "event:poll-zz");
487         call_comm("global-set-command", ci->focus, &ei->on_idle,
488                   0, NULL, "event:on-idle-zz");
489         call_comm("global-set-command", ci->focus, &ei->run,
490                   0, NULL, "event:run-zz");
491         call_comm("global-set-command", ci->focus, &ei->deactivate,
492                   0, NULL, "event:deactivate-zz");
493         call_comm("global-set-command", ci->focus, &ei->free,
494                   0, NULL, "event:free-zz");
495         call_comm("global-set-command", ci->focus, &ei->refresh,
496                   0, NULL, "event:refresh-zz");
497         call_comm("global-set-command", ci->focus, &ei->noblock,
498                   0, NULL, "event:noblock-zz");
499         call("event:refresh", ci->focus);
500
501         return 1;
502 }
503
504 void edlib_init(struct pane *ed safe)
505 {
506         call_comm("global-set-command", ed, &libevent_activate,
507                   0, NULL, "attach-libevent");
508
509         if (libevent_map)
510                 return;
511         libevent_map = key_alloc();
512         key_add(libevent_map, "Notify:Close", &libevent_notify);
513 }