1 #include "kqueue_event.h"
2 
3 #include <sys/param.h>
4 
5 #include <assert.h>
6 
7 #include <fcntl.h>
8 #include <time.h>
9 #include <unistd.h>
10 
11 errno_t
kqueue_event_init(KQueueEvent * kqueue_event,struct kevent * kevs,int * kevs_length,bool should_trigger)12 kqueue_event_init(KQueueEvent *kqueue_event, struct kevent *kevs,
13     int *kevs_length, bool should_trigger)
14 {
15 	*kqueue_event = (KQueueEvent) { .self_pipe_ = { -1, -1 } };
16 
17 #ifdef EVFILT_USER
18 	EV_SET(&kevs[(*kevs_length)++], 0, /**/
19 	    EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
20 
21 	if (should_trigger) {
22 		EV_SET(&kevs[(*kevs_length)++], 0, /**/
23 		    EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
24 		kqueue_event->is_triggered_ = true;
25 	}
26 
27 	return 0;
28 #else
29 	errno_t ec;
30 
31 	if (pipe2(kqueue_event->self_pipe_, O_NONBLOCK | O_CLOEXEC) < 0) {
32 		return errno;
33 	}
34 
35 	EV_SET(&kevs[(*kevs_length)++], kqueue_event->self_pipe_[0], /**/
36 	    EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
37 	if (should_trigger) {
38 		if ((ec = kqueue_event_trigger(kqueue_event, -1)) != 0) {
39 			goto out;
40 		}
41 		assert(kqueue_event->is_triggered_);
42 	}
43 
44 	return 0;
45 
46 out:
47 	(void)kqueue_event_terminate(kqueue_event);
48 	return ec;
49 #endif
50 }
51 
52 errno_t
kqueue_event_terminate(KQueueEvent * kqueue_event)53 kqueue_event_terminate(KQueueEvent *kqueue_event)
54 {
55 #ifdef EVFILT_USER
56 	(void)kqueue_event;
57 	return 0;
58 #else
59 	errno_t ec = 0;
60 	if (close(kqueue_event->self_pipe_[0]) < 0) {
61 		ec = ec != 0 ? ec : errno;
62 	}
63 	if (close(kqueue_event->self_pipe_[1]) < 0) {
64 		ec = ec != 0 ? ec : errno;
65 	}
66 	return ec;
67 #endif
68 }
69 
70 bool
kqueue_event_is_triggered(KQueueEvent * kqueue_event)71 kqueue_event_is_triggered(KQueueEvent *kqueue_event)
72 {
73 	return kqueue_event->is_triggered_;
74 }
75 
76 errno_t
kqueue_event_trigger(KQueueEvent * kqueue_event,int kq)77 kqueue_event_trigger(KQueueEvent *kqueue_event, int kq)
78 {
79 	if (kqueue_event->is_triggered_) {
80 		return 0;
81 	}
82 
83 #ifdef EVFILT_USER
84 	struct kevent kevs[1];
85 	EV_SET(&kevs[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
86 
87 	if (kevent(kq, kevs, 1, NULL, 0, NULL) < 0) {
88 		return errno;
89 	}
90 #else
91 	char c = 0;
92 	if (write(kqueue_event->self_pipe_[1], &c, 1) < 0) {
93 		if (errno != EAGAIN && errno != EWOULDBLOCK) {
94 			return errno;
95 		}
96 	}
97 #endif
98 
99 	kqueue_event->is_triggered_ = true;
100 	return 0;
101 }
102 
103 void
kqueue_event_clear(KQueueEvent * kqueue_event,int kq)104 kqueue_event_clear(KQueueEvent *kqueue_event, int kq)
105 {
106 #ifndef EVFILT_USER
107 	char c[32];
108 	while (read(kqueue_event->self_pipe_[0], c, sizeof(c)) >= 0) {
109 	}
110 #endif
111 
112 	struct kevent kevs[32];
113 	int n;
114 
115 	while ((n = kevent(kq, NULL, 0, kevs, 32,
116 		    &(struct timespec) { 0, 0 })) > 0) {
117 	}
118 
119 	kqueue_event->is_triggered_ = false;
120 }
121