File Coverage

Event.xs
Criterion Covered Total %
statement 491 556 88.3
branch 241 410 58.7
condition n/a
subroutine n/a
pod n/a
total 732 966 75.7


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #include
5             #include
6             #include
7              
8             typedef struct {
9             SV **slots;
10             UV cap;
11             UV count;
12             } le_registry_t;
13              
14             static le_registry_t *
15 238           le_registry_from_sv(SV *obj) {
16 238 50         if (!sv_isobject(obj)) {
17 0           croak("registry is not an object");
18             }
19              
20 238           SV *rv = SvRV(obj);
21 238 50         if (!SvIOK(rv)) {
22 0           croak("invalid registry object");
23             }
24              
25 238           return INT2PTR(le_registry_t *, SvIV(rv));
26             }
27              
28             static void
29 43           le_registry_grow(le_registry_t *r, UV fd) {
30 43           UV old_cap = r->cap;
31 43 100         UV new_cap = old_cap ? old_cap : 64;
32              
33 50 100         while (fd >= new_cap) {
34 7 50         if (new_cap > (UV_MAX / 2)) {
35 0           croak("fd registry too large");
36             }
37 7           new_cap *= 2;
38             }
39              
40 43 50         Renew(r->slots, new_cap, SV *);
41 43 50         Zero(r->slots + old_cap, new_cap - old_cap, SV *);
42 43           r->cap = new_cap;
43 43           }
44              
45              
46              
47              
48              
49             typedef struct {
50             int epfd;
51             struct epoll_event *events;
52             int max_events;
53             } le_epoll_t;
54              
55             static le_epoll_t *
56 108           le_epoll_from_sv(SV *obj) {
57 108 50         if (!sv_isobject(obj)) {
58 0           croak("epoll buffer is not an object");
59             }
60 108           SV *rv = SvRV(obj);
61 108 50         if (!SvIOK(rv)) {
62 0           croak("invalid epoll buffer object");
63             }
64 108           return INT2PTR(le_epoll_t *, SvIV(rv));
65             }
66              
67             static uint32_t
68 41           le_mask_to_epoll_events(IV mask) {
69 41           uint32_t ev = 0;
70 41 50         if (mask & 0x01) ev |= EPOLLIN;
71 41 100         if (mask & 0x02) ev |= EPOLLOUT;
72             #ifdef EPOLLPRI
73 41 50         if (mask & 0x04) ev |= EPOLLPRI;
74             #endif
75             #ifdef EPOLLRDHUP
76 41 50         if (mask & 0x08) ev |= EPOLLRDHUP;
77             #endif
78             #ifdef EPOLLET
79 41 100         if (mask & 0x10) ev |= EPOLLET;
80             #endif
81             #ifdef EPOLLONESHOT
82 41 100         if (mask & 0x20) ev |= EPOLLONESHOT;
83             #endif
84 41           return ev;
85             }
86              
87             static IV
88 24           le_epoll_events_to_mask(uint32_t ev) {
89 24           IV mask = 0;
90 24 50         if (ev & EPOLLIN) mask |= 0x01;
91 24 100         if (ev & EPOLLOUT) mask |= 0x02;
92             #ifdef EPOLLPRI
93 24 50         if (ev & EPOLLPRI) mask |= 0x04;
94             #endif
95             #ifdef EPOLLRDHUP
96 24 50         if (ev & EPOLLRDHUP) mask |= 0x08;
97             #endif
98             #ifdef EPOLLET
99 24 50         if (ev & EPOLLET) mask |= 0x10;
100             #endif
101             #ifdef EPOLLONESHOT
102 24 50         if (ev & EPOLLONESHOT) mask |= 0x20;
103             #endif
104 24 50         if (ev & EPOLLERR) mask |= 0x40;
105 24 100         if (ev & EPOLLHUP) mask |= 0x80;
106 24           return mask;
107             }
108              
109             static int
110 33           le_timeout_ms(SV *timeout_s) {
111 33 100         if (!SvOK(timeout_s)) {
112 3           return -1;
113             }
114 30           double sec = SvNV(timeout_s);
115 30 50         if (sec <= 0.0) {
116 0           return 0;
117             }
118 30           double ms = sec * 1000.0;
119 30 50         if (ms > 2147483647.0) {
120 0           return 2147483647;
121             }
122 30           int out = (int)ms;
123 30 100         if ((double)out < ms) {
124 14           out++;
125             }
126 30           return out < 0 ? 0 : out;
127             }
128              
129             typedef struct {
130             IV fd;
131             IV mask;
132             SV *fh;
133             SV *cb;
134             SV *loop;
135             SV *tag;
136             SV *watcher; /* optional direct Loop watcher; avoids a Perl dispatch closure */
137             } le_backend_watch_t;
138              
139             static le_backend_watch_t *
140 75           le_backend_watch_from_sv(SV *obj) {
141 75 50         if (!sv_isobject(obj)) {
142 0           croak("backend watch is not an object");
143             }
144              
145 75           SV *rv = SvRV(obj);
146 75 50         if (!SvIOK(rv)) {
147 0           croak("invalid backend watch object");
148             }
149              
150 75           return INT2PTR(le_backend_watch_t *, SvIV(rv));
151             }
152              
153              
154              
155              
156              
157             static SV *
158 35           le_backend_watch_make_sv(const char *class, IV fd, SV *fh, SV *watcher, IV mask, SV *loop, SV *tag) {
159             le_backend_watch_t *w;
160 35           Newxz(w, 1, le_backend_watch_t);
161 35           w->fd = fd;
162 35           w->mask = mask;
163 35           w->fh = newSVsv(fh);
164 35           w->cb = newSV(0);
165 35           w->loop = newSVsv(loop);
166 35           w->tag = newSVsv(tag);
167 35           w->watcher = newSVsv(watcher);
168 35           SV *inner = newSViv(PTR2IV(w));
169 35           SV *ref = newRV_noinc(inner);
170 35           sv_bless(ref, gv_stashpv(class, GV_ADD));
171 35           return ref;
172             }
173              
174             static SV *
175 212           le_hv_fetch_required(HV *hv, const char *key, I32 klen) {
176 212           SV **svp = hv_fetch(hv, key, klen, 0);
177 212 50         if (!svp) {
178 0           croak("missing required hash key '%.*s'", (int)klen, key);
179             }
180 212           return *svp;
181             }
182              
183             static void
184 24           le_registry_delete_slot(le_registry_t *r, UV fd) {
185 24 50         if (fd < r->cap && r->slots[fd]) {
    50          
186 24           SvREFCNT_dec(r->slots[fd]);
187 24           r->slots[fd] = NULL;
188 24 50         if (r->count) r->count--;
189             }
190 24           }
191              
192             static void
193 494           le_hv_store_sv(HV *hv, const char *key, I32 klen, SV *value) {
194 494           (void)hv_store(hv, key, klen, value, 0);
195 494           }
196              
197             static SV *
198 190           le_bool_sv(int v) {
199 190           return newSViv(v ? 1 : 0);
200             }
201              
202             static int
203 8           le_hv_bool(HV *hv, const char *key, I32 len) {
204 8           SV **svp = hv_fetch(hv, key, len, 0);
205 8 100         return (svp && SvTRUE(*svp)) ? 1 : 0;
    50          
206             }
207              
208             static IV
209 1           le_events_to_mask(SV *ev) {
210 1 50         if (!SvROK(ev) || SvTYPE(SvRV(ev)) != SVt_PVHV) {
    50          
211 0           croak("epoll event must be a hash reference");
212             }
213              
214 1           HV *hv = (HV *)SvRV(ev);
215 1           IV m = 0;
216              
217 1 50         if (le_hv_bool(hv, "in", 2)) m |= 0x01;
218 1 50         if (le_hv_bool(hv, "out", 3)) m |= 0x02;
219 1 50         if (le_hv_bool(hv, "prio", 4)) m |= 0x04;
220 1 50         if (le_hv_bool(hv, "rdhup", 5)) m |= 0x08;
221 1 50         if (le_hv_bool(hv, "et", 2)) m |= 0x10;
222 1 50         if (le_hv_bool(hv, "oneshot", 7)) m |= 0x20;
223 1 50         if (le_hv_bool(hv, "err", 3)) m |= 0x40;
224 1 50         if (le_hv_bool(hv, "hup", 3)) m |= 0x80;
225              
226 1           return m;
227             }
228              
229              
230              
231             typedef struct {
232             IV deadline_ns;
233             UV id;
234             SV *cb;
235             } le_timer_entry_t;
236              
237             typedef struct {
238             le_timer_entry_t *heap;
239             UV size;
240             UV cap;
241             char *live;
242             UV live_cap;
243             UV next_id;
244             UV cancelled;
245             } le_timer_heap_t;
246              
247             static le_timer_heap_t *
248 165           le_timer_heap_from_sv(SV *obj) {
249 165 50         if (!sv_isobject(obj)) {
250 0           croak("timer heap is not an object");
251             }
252 165           SV *rv = SvRV(obj);
253 165 50         if (!SvIOK(rv)) {
254 0           croak("invalid timer heap object");
255             }
256 165           return INT2PTR(le_timer_heap_t *, SvIV(rv));
257             }
258              
259             static void
260 10           le_timer_heap_grow(le_timer_heap_t *h, UV need) {
261 10           UV old_cap = h->cap;
262 10 50         UV new_cap = old_cap ? old_cap : 64;
263 10 50         while (need >= new_cap) {
264 0 0         if (new_cap > (UV_MAX / 2)) croak("timer heap too large");
265 0           new_cap *= 2;
266             }
267 10 50         Renew(h->heap, new_cap, le_timer_entry_t);
268 10 50         if (new_cap > old_cap) {
269 10 50         Zero(h->heap + old_cap, new_cap - old_cap, le_timer_entry_t);
270             }
271 10           h->cap = new_cap;
272 10           }
273              
274             static void
275 10           le_timer_live_grow(le_timer_heap_t *h, UV id) {
276 10           UV old_cap = h->live_cap;
277 10 50         UV new_cap = old_cap ? old_cap : 64;
278 10 50         while (id >= new_cap) {
279 0 0         if (new_cap > (UV_MAX / 2)) croak("timer id table too large");
280 0           new_cap *= 2;
281             }
282 10           Renew(h->live, new_cap, char);
283 10           Zero(h->live + old_cap, new_cap - old_cap, char);
284 10           h->live_cap = new_cap;
285 10           }
286              
287             static int
288 92           le_timer_is_live(le_timer_heap_t *h, UV id) {
289 92 50         return (id < h->live_cap && h->live[id]) ? 1 : 0;
    100          
290             }
291              
292             static void
293 5           le_timer_swap(le_timer_entry_t *a, le_timer_entry_t *b) {
294 5           le_timer_entry_t tmp = *a;
295 5           *a = *b;
296 5           *b = tmp;
297 5           }
298              
299             static void
300 19           le_timer_sift_up(le_timer_heap_t *h, UV i) {
301 21 100         while (i > 0) {
302 9           UV p = (i - 1) / 2;
303 9 100         if (h->heap[p].deadline_ns <= h->heap[i].deadline_ns) break;
304 2           le_timer_swap(&h->heap[p], &h->heap[i]);
305 2           i = p;
306             }
307 19           }
308              
309             static void
310 9           le_timer_sift_down(le_timer_heap_t *h, UV i) {
311 3           while (1) {
312 12           UV l = i * 2 + 1;
313 12 100         if (l >= h->size) break;
314 4           UV r = l + 1;
315 4           UV m = l;
316 4 100         if (r < h->size && h->heap[r].deadline_ns < h->heap[l].deadline_ns) m = r;
    50          
317 4 100         if (h->heap[i].deadline_ns <= h->heap[m].deadline_ns) break;
318 3           le_timer_swap(&h->heap[i], &h->heap[m]);
319 3           i = m;
320             }
321 9           }
322              
323             static void
324 18           le_timer_pop_root(le_timer_heap_t *h) {
325 18 50         if (h->size == 0) return;
326 18           SvREFCNT_dec(h->heap[0].cb);
327 18           h->size--;
328 18 100         if (h->size > 0) {
329 9           h->heap[0] = h->heap[h->size];
330 9           Zero(&h->heap[h->size], 1, le_timer_entry_t);
331 9           le_timer_sift_down(h, 0);
332             }
333             else {
334 9           Zero(&h->heap[0], 1, le_timer_entry_t);
335             }
336             }
337              
338             static void
339 114           le_timer_drop_cancelled_roots(le_timer_heap_t *h) {
340 230 100         while (h->size && !le_timer_is_live(h, h->heap[0].id)) {
    100          
341 2           le_timer_pop_root(h);
342 2 50         if (h->cancelled) h->cancelled--;
343             }
344 114           }
345              
346             static int
347 149           le_hv_true_key(HV *hv, const char *key, I32 klen) {
348 149           SV **svp = hv_fetch(hv, key, klen, 0);
349 149 50         return (svp && SvTRUE(*svp)) ? 1 : 0;
    100          
350             }
351              
352             static SV *
353 55           le_hv_get_key(HV *hv, const char *key, I32 klen) {
354 55           SV **svp = hv_fetch(hv, key, klen, 0);
355 55 50         return svp ? *svp : &PL_sv_undef;
356             }
357              
358             static void
359 28           le_call_watcher_cb(SV *cb, SV *loop, SV *fh, SV *watcher) {
360 28           dSP;
361 28           ENTER;
362 28           SAVETMPS;
363 28 50         PUSHMARK(SP);
364 28 50         XPUSHs(sv_2mortal(newSVsv(loop)));
365 28 50         XPUSHs(sv_2mortal(newSVsv(fh)));
366 28 50         XPUSHs(sv_2mortal(newSVsv(watcher)));
367 28           PUTBACK;
368 28           call_sv(cb, G_DISCARD);
369 28 50         FREETMPS;
370 28           LEAVE;
371 28           }
372              
373             static void
374 26           le_backend_watch_dispatch_direct(le_backend_watch_t *w, IV mask) {
375 26 50         if (!w->watcher || !SvOK(w->watcher) || !SvROK(w->watcher) || SvTYPE(SvRV(w->watcher)) != SVt_PVHV) {
    50          
    50          
    50          
376 0           return;
377             }
378              
379 26           HV *whv = (HV *)SvRV(w->watcher);
380 26 50         if (!le_hv_true_key(whv, "active", 6)) {
381 0           return;
382             }
383              
384 26           SV *fh = le_hv_get_key(whv, "fh", 2);
385 26 50         if (!SvOK(fh)) {
386 0           return;
387             }
388              
389 26           int read_trig = (mask & 0x01) ? 1 : 0;
390 26           int write_trig = (mask & 0x02) ? 1 : 0;
391              
392 26 100         if (mask & 0x40) {
393 2           SV *ecb = le_hv_get_key(whv, "error_cb", 8);
394 2 100         if (SvOK(ecb) && le_hv_true_key(whv, "error_enabled", 13)) {
    50          
395 1           le_call_watcher_cb(ecb, w->loop, fh, w->watcher);
396 1           return;
397             }
398 1           read_trig = 1;
399 1           write_trig = 1;
400             }
401              
402 25 100         if (mask & 0x80) {
403 2           read_trig = 1;
404             }
405              
406 25 50         if (read_trig && le_hv_true_key(whv, "read_enabled", 12)) {
    50          
407 25           SV *rcb = le_hv_get_key(whv, "read_cb", 7);
408 25 50         if (SvOK(rcb)) {
409 25           le_call_watcher_cb(rcb, w->loop, fh, w->watcher);
410             }
411             }
412              
413 25 100         if (!le_hv_true_key(whv, "active", 6)) {
414 5           return;
415             }
416              
417 20 100         if (write_trig && le_hv_true_key(whv, "write_enabled", 13)) {
    50          
418 2           SV *wcb = le_hv_get_key(whv, "write_cb", 8);
419 2 50         if (SvOK(wcb)) {
420 2           le_call_watcher_cb(wcb, w->loop, fh, w->watcher);
421             }
422             }
423             }
424              
425              
426             MODULE = Linux::Event PACKAGE = Linux::Event::XS
427             PROTOTYPES: DISABLE
428              
429             SV *
430             registry_new(class = "Linux::Event::XS::Registry")
431             const char *class
432             CODE:
433             le_registry_t *r;
434 42           Newxz(r, 1, le_registry_t);
435 42           SV *inner = newSViv(PTR2IV(r));
436 42           SV *ref = newRV_noinc(inner);
437 42           sv_bless(ref, gv_stashpv(class, GV_ADD));
438 42           RETVAL = ref;
439             OUTPUT:
440             RETVAL
441              
442             SV *
443             registry_get(reg, fd)
444             SV *reg
445             UV fd
446             CODE:
447 53           le_registry_t *r = le_registry_from_sv(reg);
448 53 100         if (fd >= r->cap || !r->slots[fd]) {
    100          
449 40           RETVAL = &PL_sv_undef;
450             }
451             else {
452 13           RETVAL = newSVsv(r->slots[fd]);
453             }
454             OUTPUT:
455             RETVAL
456              
457             void
458             registry_set(reg, fd, value)
459             SV *reg
460             UV fd
461             SV *value
462             CODE:
463 8           le_registry_t *r = le_registry_from_sv(reg);
464 8 100         if (fd >= r->cap) {
465 5           le_registry_grow(r, fd);
466             }
467 8 100         if (r->slots[fd]) {
468 1           SvREFCNT_dec(r->slots[fd]);
469             }
470             else {
471 7           r->count++;
472             }
473 8           r->slots[fd] = newSVsv(value);
474              
475             SV *
476             registry_delete(reg, fd)
477             SV *reg
478             UV fd
479             CODE:
480 3           le_registry_t *r = le_registry_from_sv(reg);
481 3 50         if (fd >= r->cap || !r->slots[fd]) {
    100          
482 1           RETVAL = &PL_sv_undef;
483             }
484             else {
485 2           RETVAL = r->slots[fd];
486 2           r->slots[fd] = NULL;
487 2           r->count--;
488             }
489             OUTPUT:
490             RETVAL
491              
492             UV
493             registry_count(reg)
494             SV *reg
495             CODE:
496 5           le_registry_t *r = le_registry_from_sv(reg);
497 5 50         RETVAL = r->count;
498             OUTPUT:
499             RETVAL
500              
501              
502              
503              
504              
505             IV
506             loop_cancel_watcher(loop, watcher)
507             SV *loop
508             SV *watcher
509             CODE:
510 12 50         if (!SvROK(loop) || SvTYPE(SvRV(loop)) != SVt_PVHV) {
    50          
511 0           croak("loop must be a hash-based object");
512             }
513 12 50         if (!SvROK(watcher) || SvTYPE(SvRV(watcher)) != SVt_PVHV) {
    50          
514 0           croak("watcher must be a hash-based object");
515             }
516              
517 12           HV *lhv = (HV *)SvRV(loop);
518 12           HV *whv = (HV *)SvRV(watcher);
519              
520 12           SV *active = le_hv_fetch_required(whv, "active", 6);
521 12 50         if (!SvTRUE(active)) {
522 0           RETVAL = 0;
523             }
524             else {
525 12           SV *fdsv = le_hv_fetch_required(whv, "fd", 2);
526 12           UV fd = (UV)SvIV(fdsv);
527              
528 12           SV *watchers_sv = le_hv_fetch_required(lhv, "_watchers", 9);
529 12           le_registry_t *watchers = le_registry_from_sv(watchers_sv);
530 12           le_registry_delete_slot(watchers, fd);
531              
532 12           SV *backend_sv = le_hv_fetch_required(lhv, "backend", 7);
533 12 50         if (!SvROK(backend_sv) || SvTYPE(SvRV(backend_sv)) != SVt_PVHV) {
    50          
534 0           croak("backend must be a hash-based object");
535             }
536 12           HV *bhv = (HV *)SvRV(backend_sv);
537              
538 12           SV *ep_sv = le_hv_fetch_required(bhv, "ep", 2);
539 12           le_epoll_t *ep = le_epoll_from_sv(ep_sv);
540 12           int ret = epoll_ctl(ep->epfd, EPOLL_CTL_DEL, (int)fd, NULL);
541 12 50         if (ret < 0 && errno != ENOENT && errno != EBADF) {
    0          
    0          
542 0           croak("epoll_ctl delete failed for fd %ld: %s", (long)fd, strerror(errno));
543             }
544              
545 12           SV *breg_sv = le_hv_fetch_required(bhv, "watch", 5);
546 12           le_registry_t *breg = le_registry_from_sv(breg_sv);
547 12           le_registry_delete_slot(breg, fd);
548              
549 12           hv_store(whv, "active", 6, newSViv(0), 0);
550 12           hv_store(whv, "fh", 2, newSV(0), 0);
551 12           RETVAL = 1;
552             }
553             OUTPUT:
554             RETVAL
555              
556             SV *
557             watcher_new(class, loop, fh, fd, read_cb=&PL_sv_undef, write_cb=&PL_sv_undef, error_cb=&PL_sv_undef, data=&PL_sv_undef, edge_triggered=0, oneshot=0)
558             const char *class
559             SV *loop
560             SV *fh
561             IV fd
562             SV *read_cb
563             SV *write_cb
564             SV *error_cb
565             SV *data
566             IV edge_triggered
567             IV oneshot
568             CODE:
569 38 50         if (SvOK(read_cb) && (!SvROK(read_cb) || SvTYPE(SvRV(read_cb)) != SVt_PVCV)) {
    50          
    50          
570 0           croak("read must be a coderef or undef");
571             }
572 38 100         if (SvOK(write_cb) && (!SvROK(write_cb) || SvTYPE(SvRV(write_cb)) != SVt_PVCV)) {
    50          
    50          
573 0           croak("write must be a coderef or undef");
574             }
575 38 100         if (SvOK(error_cb) && (!SvROK(error_cb) || SvTYPE(SvRV(error_cb)) != SVt_PVCV)) {
    50          
    50          
576 0           croak("error must be a coderef or undef");
577             }
578              
579 38           HV *hv = newHV();
580 38           SV *fh_slot = newSVsv(fh);
581 38 50         if (SvROK(fh_slot)) {
582 38           sv_rvweaken(fh_slot);
583             }
584              
585 38           int read_enabled = SvOK(read_cb) ? 1 : 0;
586 38           int write_enabled = SvOK(write_cb) ? 1 : 0;
587 38           int error_enabled = SvOK(error_cb) ? 1 : 0;
588              
589 38           le_hv_store_sv(hv, "loop", 4, newSVsv(loop));
590 38           le_hv_store_sv(hv, "fh", 2, fh_slot);
591 38           le_hv_store_sv(hv, "fd", 2, newSViv(fd));
592 38           le_hv_store_sv(hv, "data", 4, newSVsv(data));
593 38           le_hv_store_sv(hv, "read_cb", 7, newSVsv(read_cb));
594 38           le_hv_store_sv(hv, "write_cb", 8, newSVsv(write_cb));
595 38           le_hv_store_sv(hv, "error_cb", 8, newSVsv(error_cb));
596 38           le_hv_store_sv(hv, "read_enabled", 12, le_bool_sv(read_enabled));
597 38           le_hv_store_sv(hv, "write_enabled", 13, le_bool_sv(write_enabled));
598 38           le_hv_store_sv(hv, "error_enabled", 13, le_bool_sv(error_enabled));
599 38           le_hv_store_sv(hv, "edge_triggered", 14, le_bool_sv(edge_triggered));
600 38           le_hv_store_sv(hv, "oneshot", 7, le_bool_sv(oneshot));
601 38           le_hv_store_sv(hv, "active", 6, newSViv(1));
602              
603 38           SV *ref = newRV_noinc((SV *)hv);
604 38           sv_bless(ref, gv_stashpv(class, GV_ADD));
605 38           RETVAL = ref;
606             OUTPUT:
607             RETVAL
608              
609             SV *
610             backend_watch_new(class, fd, fh, cb, mask, loop, tag=&PL_sv_undef)
611             const char *class
612             IV fd
613             SV *fh
614             SV *cb
615             IV mask
616             SV *loop
617             SV *tag
618             CODE:
619 3 50         if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) {
    50          
620 0           croak("cb must be a coderef");
621             }
622             le_backend_watch_t *w;
623 3           Newxz(w, 1, le_backend_watch_t);
624 3           w->fd = fd;
625 3           w->mask = mask;
626 3           w->fh = newSVsv(fh);
627 3           w->cb = newSVsv(cb);
628 3           w->loop = newSVsv(loop);
629 3           w->tag = newSVsv(tag);
630 3           SV *inner = newSViv(PTR2IV(w));
631 3           SV *ref = newRV_noinc(inner);
632 3           sv_bless(ref, gv_stashpv(class, GV_ADD));
633 3           RETVAL = ref;
634             OUTPUT:
635             RETVAL
636              
637             SV *
638             backend_watch_new_watcher(class, fd, fh, watcher, mask, loop, tag=&PL_sv_undef)
639             const char *class
640             IV fd
641             SV *fh
642             SV *watcher
643             IV mask
644             SV *loop
645             SV *tag
646             CODE:
647 1 50         if (!SvROK(watcher) || SvTYPE(SvRV(watcher)) != SVt_PVHV) {
    50          
648 0           croak("watcher must be a hash-based object");
649             }
650             le_backend_watch_t *w;
651 1           Newxz(w, 1, le_backend_watch_t);
652 1           w->fd = fd;
653 1           w->mask = mask;
654 1           w->fh = newSVsv(fh);
655 1           w->cb = newSV(0);
656 1           w->loop = newSVsv(loop);
657 1           w->tag = newSVsv(tag);
658 1           w->watcher = newSVsv(watcher);
659 1           SV *inner = newSViv(PTR2IV(w));
660 1           SV *ref = newRV_noinc(inner);
661 1           sv_bless(ref, gv_stashpv(class, GV_ADD));
662 1           RETVAL = ref;
663             OUTPUT:
664             RETVAL
665              
666             SV *
667             backend_watch_fh(watch)
668             SV *watch
669             CODE:
670 1           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
671 1           RETVAL = newSVsv(w->fh);
672             OUTPUT:
673             RETVAL
674              
675             IV
676             backend_watch_mask(watch)
677             SV *watch
678             CODE:
679 2           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
680 2 50         RETVAL = w->mask;
681             OUTPUT:
682             RETVAL
683              
684             void
685             backend_watch_set_mask(watch, mask)
686             SV *watch
687             IV mask
688             CODE:
689 3           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
690 3           w->mask = mask;
691              
692             void
693             backend_watch_set_loop_tag(watch, loop, tag=&PL_sv_undef)
694             SV *watch
695             SV *loop
696             SV *tag
697             CODE:
698 0           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
699 0           SvREFCNT_dec(w->loop);
700 0           SvREFCNT_dec(w->tag);
701 0           w->loop = newSVsv(loop);
702 0           w->tag = newSVsv(tag);
703              
704             void
705             backend_watch_dispatch(watch, ev)
706             SV *watch
707             SV *ev
708             CODE:
709 1           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
710 1           IV mask = le_events_to_mask(ev);
711 1           dSP;
712 1           ENTER;
713 1           SAVETMPS;
714 1 50         PUSHMARK(SP);
715 1 50         XPUSHs(sv_2mortal(newSVsv(w->loop)));
716 1 50         XPUSHs(sv_2mortal(newSVsv(w->fh)));
717 1 50         XPUSHs(sv_2mortal(newSViv(w->fd)));
718 1 50         XPUSHs(sv_2mortal(newSViv(mask)));
719 1 50         XPUSHs(sv_2mortal(newSVsv(w->tag)));
720 1           PUTBACK;
721 1           call_sv(w->cb, G_DISCARD);
722 1 50         FREETMPS;
723 1           LEAVE;
724              
725             void
726             backend_watch_dispatch_mask(watch, mask)
727             SV *watch
728             IV mask
729             CODE:
730 5           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
731 5 50         if (w->watcher && SvOK(w->watcher)) {
    50          
732 5           le_backend_watch_dispatch_direct(w, mask);
733             }
734             else {
735 0           dSP;
736 0           ENTER;
737 0           SAVETMPS;
738 0 0         PUSHMARK(SP);
739 0 0         XPUSHs(sv_2mortal(newSVsv(w->loop)));
740 0 0         XPUSHs(sv_2mortal(newSVsv(w->fh)));
741 0 0         XPUSHs(sv_2mortal(newSViv(w->fd)));
742 0 0         XPUSHs(sv_2mortal(newSViv(mask)));
743 0 0         XPUSHs(sv_2mortal(newSVsv(w->tag)));
744 0           PUTBACK;
745 0           call_sv(w->cb, G_DISCARD);
746 0 0         FREETMPS;
747 0           LEAVE;
748             }
749              
750              
751              
752              
753              
754             IV
755             loop_watch_watcher_fast(loop, fh, fd, watcher, mask)
756             SV *loop
757             SV *fh
758             IV fd
759             SV *watcher
760             IV mask
761             CODE:
762 35 50         if (!SvROK(loop) || SvTYPE(SvRV(loop)) != SVt_PVHV) {
    50          
763 0           croak("loop must be a hash-based object");
764             }
765 35 50         if (!SvROK(watcher) || SvTYPE(SvRV(watcher)) != SVt_PVHV) {
    50          
766 0           croak("watcher must be a hash-based object");
767             }
768              
769 35           HV *lhv = (HV *)SvRV(loop);
770 35           SV *watchers_sv = le_hv_fetch_required(lhv, "_watchers", 9);
771 35           le_registry_t *watchers = le_registry_from_sv(watchers_sv);
772              
773 35           SV *backend_sv = le_hv_fetch_required(lhv, "backend", 7);
774 35 50         if (!SvROK(backend_sv) || SvTYPE(SvRV(backend_sv)) != SVt_PVHV) {
    50          
775 0           croak("backend must be a hash-based object");
776             }
777 35           HV *bhv = (HV *)SvRV(backend_sv);
778              
779 35           SV *breg_sv = le_hv_fetch_required(bhv, "watch", 5);
780 35           le_registry_t *breg = le_registry_from_sv(breg_sv);
781 35 100         if ((UV)fd < breg->cap && breg->slots[(UV)fd]) {
    50          
782 0           croak("fd already watched: %ld", (long)fd);
783             }
784              
785 35           IV eff_mask = mask;
786 35 50         if (le_hv_true_key(bhv, "edge", 4)) eff_mask |= 0x10;
787 35 50         if (le_hv_true_key(bhv, "oneshot", 7)) eff_mask |= 0x20;
788              
789 35           SV *ep_sv = le_hv_fetch_required(bhv, "ep", 2);
790 35           le_epoll_t *ep = le_epoll_from_sv(ep_sv);
791             struct epoll_event ev;
792 35           Zero(&ev, 1, struct epoll_event);
793 35           ev.events = le_mask_to_epoll_events(eff_mask);
794 35           ev.data.fd = (int)fd;
795 35 50         if (epoll_ctl(ep->epfd, EPOLL_CTL_ADD, (int)fd, &ev) < 0) {
796 0           croak("epoll_ctl add failed for fd %ld: %s", (long)fd, strerror(errno));
797             }
798              
799 35           SV *rec = le_backend_watch_make_sv("Linux::Event::XS::BackendWatch", fd, fh, watcher, eff_mask, loop, &PL_sv_undef);
800              
801 35 100         if ((UV)fd >= watchers->cap) le_registry_grow(watchers, (UV)fd);
802 35 50         if (watchers->slots[(UV)fd]) SvREFCNT_dec(watchers->slots[(UV)fd]);
803 35           else watchers->count++;
804 35           watchers->slots[(UV)fd] = newSVsv(watcher);
805              
806 35 100         if ((UV)fd >= breg->cap) le_registry_grow(breg, (UV)fd);
807 35 50         if (breg->slots[(UV)fd]) SvREFCNT_dec(breg->slots[(UV)fd]);
808 35           else breg->count++;
809 35           breg->slots[(UV)fd] = newSVsv(rec);
810 35           SvREFCNT_dec(rec);
811              
812 35 100         RETVAL = 1;
813             OUTPUT:
814             RETVAL
815              
816             SV *
817             timer_heap_new(class = "Linux::Event::XS::TimerHeap")
818             const char *class
819             CODE:
820             le_timer_heap_t *h;
821 21           Newxz(h, 1, le_timer_heap_t);
822 21           h->next_id = 1;
823 21           SV *inner = newSViv(PTR2IV(h));
824 21           SV *ref = newRV_noinc(inner);
825 21           sv_bless(ref, gv_stashpv(class, GV_ADD));
826 21           RETVAL = ref;
827             OUTPUT:
828             RETVAL
829              
830             UV
831             timer_heap_at_ns(heap, deadline_ns, cb)
832             SV *heap
833             IV deadline_ns
834             SV *cb
835             CODE:
836 19 50         if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) {
    50          
837 0           croak("callback must be a coderef");
838             }
839 19           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
840 19           UV id = h->next_id++;
841 19 100         if (id >= h->live_cap) le_timer_live_grow(h, id);
842 19           h->live[id] = 1;
843 19 100         if (h->size >= h->cap) le_timer_heap_grow(h, h->size);
844 19           UV i = h->size++;
845 19           h->heap[i].deadline_ns = deadline_ns;
846 19           h->heap[i].id = id;
847 19           h->heap[i].cb = newSVsv(cb);
848 19           le_timer_sift_up(h, i);
849 19 100         RETVAL = id;
850             OUTPUT:
851             RETVAL
852              
853             IV
854             timer_heap_cancel(heap, id)
855             SV *heap
856             UV id
857             CODE:
858 3           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
859 3 50         if (id < h->live_cap && h->live[id]) {
    100          
860 2           h->live[id] = 0;
861 2           h->cancelled++;
862 2           RETVAL = 1;
863             }
864             else {
865 1           RETVAL = 0;
866             }
867             OUTPUT:
868             RETVAL
869              
870             SV *
871             timer_heap_next_deadline_ns(heap)
872             SV *heap
873             CODE:
874 76           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
875 76           le_timer_drop_cancelled_roots(h);
876 76 100         if (!h->size) {
877 23           RETVAL = &PL_sv_undef;
878             }
879             else {
880 53           RETVAL = newSViv(h->heap[0].deadline_ns);
881             }
882             OUTPUT:
883             RETVAL
884              
885             void
886             timer_heap_pop_expired(heap, now_ns)
887             SV *heap
888             IV now_ns
889             PPCODE:
890 46           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
891 62 100         while (h->size) {
892 38           le_timer_drop_cancelled_roots(h);
893 38 100         if (!h->size) break;
894 37 100         if (h->heap[0].deadline_ns > now_ns) break;
895              
896 16           UV id = h->heap[0].id;
897 16           IV deadline_ns = h->heap[0].deadline_ns;
898 16           SV *cb = newSVsv(h->heap[0].cb);
899 16 50         if (id < h->live_cap) h->live[id] = 0;
900 16           le_timer_pop_root(h);
901              
902 16           AV *av = newAV();
903 16           av_push(av, newSVuv(id));
904 16           av_push(av, cb);
905 16           av_push(av, newSViv(deadline_ns));
906 16 50         XPUSHs(sv_2mortal(newRV_noinc((SV *)av)));
907             }
908              
909             UV
910             timer_heap_size(heap)
911             SV *heap
912             CODE:
913 0           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
914 0 0         RETVAL = h->size;
915             OUTPUT:
916             RETVAL
917              
918             SV *
919             epoll_new(class = "Linux::Event::XS::Epoll", max_events = 256)
920             const char *class
921             IV max_events
922             CODE:
923 21 50         if (max_events <= 0) {
924 0           croak("max_events must be positive");
925             }
926             le_epoll_t *ep;
927 21           Newxz(ep, 1, le_epoll_t);
928 21           ep->epfd = epoll_create1(EPOLL_CLOEXEC);
929 21 50         if (ep->epfd < 0) {
930 0           Safefree(ep);
931 0           croak("epoll_create1 failed: %s", strerror(errno));
932             }
933 21           ep->max_events = (int)max_events;
934 21           Newxz(ep->events, ep->max_events, struct epoll_event);
935 21           SV *inner = newSViv(PTR2IV(ep));
936 21           SV *ref = newRV_noinc(inner);
937 21           sv_bless(ref, gv_stashpv(class, GV_ADD));
938 21           RETVAL = ref;
939             OUTPUT:
940             RETVAL
941              
942             void
943             epoll_add(epobj, fd, mask)
944             SV *epobj
945             IV fd
946             IV mask
947             CODE:
948 3           le_epoll_t *ep = le_epoll_from_sv(epobj);
949             struct epoll_event ev;
950 3           Zero(&ev, 1, struct epoll_event);
951 3           ev.events = le_mask_to_epoll_events(mask);
952 3           ev.data.fd = (int)fd;
953 3 50         if (epoll_ctl(ep->epfd, EPOLL_CTL_ADD, (int)fd, &ev) < 0) {
954 0           croak("epoll_ctl add failed for fd %ld: %s", (long)fd, strerror(errno));
955             }
956              
957             void
958             epoll_modify(epobj, fd, mask)
959             SV *epobj
960             IV fd
961             IV mask
962             CODE:
963 3           le_epoll_t *ep = le_epoll_from_sv(epobj);
964             struct epoll_event ev;
965 3           Zero(&ev, 1, struct epoll_event);
966 3           ev.events = le_mask_to_epoll_events(mask);
967 3           ev.data.fd = (int)fd;
968 3 50         if (epoll_ctl(ep->epfd, EPOLL_CTL_MOD, (int)fd, &ev) < 0) {
969 0           croak("epoll_ctl modify failed for fd %ld: %s", (long)fd, strerror(errno));
970             }
971              
972             IV
973             epoll_delete(epobj, fd)
974             SV *epobj
975             IV fd
976             CODE:
977 1           le_epoll_t *ep = le_epoll_from_sv(epobj);
978 1           int ret = epoll_ctl(ep->epfd, EPOLL_CTL_DEL, (int)fd, NULL);
979 1 50         if (ret < 0) {
980 0 0         if (errno == ENOENT || errno == EBADF) {
    0          
981 0           RETVAL = 0;
982             }
983             else {
984 0           croak("epoll_ctl delete failed for fd %ld: %s", (long)fd, strerror(errno));
985             }
986             }
987             else {
988 1           RETVAL = 1;
989             }
990             OUTPUT:
991             RETVAL
992              
993             IV
994             epoll_wait_dispatch(epobj, registry, timeout_s=&PL_sv_undef)
995             SV *epobj
996             SV *registry
997             SV *timeout_s
998             CODE:
999 33           le_epoll_t *ep = le_epoll_from_sv(epobj);
1000 33           le_registry_t *r = le_registry_from_sv(registry);
1001 33           int timeout = le_timeout_ms(timeout_s);
1002             int n;
1003             do {
1004 33           n = epoll_wait(ep->epfd, ep->events, ep->max_events, timeout);
1005 33 50         } while (n < 0 && errno == EINTR);
    0          
1006 33 50         if (n < 0) {
1007 0           croak("epoll_wait failed: %s", strerror(errno));
1008             }
1009             int i;
1010 57 100         for (i = 0; i < n; i++) {
1011 24           UV fd = (UV)ep->events[i].data.fd;
1012 24 50         if (fd < r->cap && r->slots[fd]) {
    50          
1013 24           SV *rec_sv = r->slots[fd];
1014 24           SvREFCNT_inc(rec_sv);
1015 24           le_backend_watch_t *w = le_backend_watch_from_sv(rec_sv);
1016 24           IV mask = le_epoll_events_to_mask(ep->events[i].events);
1017 24 100         if (w->watcher && SvOK(w->watcher)) {
    50          
1018 21           le_backend_watch_dispatch_direct(w, mask);
1019             }
1020             else {
1021 3           dSP;
1022 3           ENTER;
1023 3           SAVETMPS;
1024 3 50         PUSHMARK(SP);
1025 3 50         XPUSHs(sv_2mortal(newSVsv(w->loop)));
1026 3 50         XPUSHs(sv_2mortal(newSVsv(w->fh)));
1027 3 50         XPUSHs(sv_2mortal(newSViv(w->fd)));
1028 3 50         XPUSHs(sv_2mortal(newSViv(mask)));
1029 3 50         XPUSHs(sv_2mortal(newSVsv(w->tag)));
1030 3           PUTBACK;
1031 3           call_sv(w->cb, G_DISCARD);
1032 3 50         FREETMPS;
1033 3           LEAVE;
1034             }
1035 24           SvREFCNT_dec(rec_sv);
1036             }
1037             }
1038 33 100         RETVAL = n;
1039             OUTPUT:
1040             RETVAL
1041              
1042              
1043              
1044             MODULE = Linux::Event PACKAGE = Linux::Event::XS::BackendWatch
1045             PROTOTYPES: DISABLE
1046              
1047             void
1048             DESTROY(watch)
1049             SV *watch
1050             CODE:
1051 39           le_backend_watch_t *w = le_backend_watch_from_sv(watch);
1052 39 50         if (w) {
1053 39           SvREFCNT_dec(w->fh);
1054 39           SvREFCNT_dec(w->cb);
1055 39           SvREFCNT_dec(w->loop);
1056 39           SvREFCNT_dec(w->tag);
1057 39 100         if (w->watcher) SvREFCNT_dec(w->watcher);
1058 39           Safefree(w);
1059 39           sv_setiv(SvRV(watch), 0);
1060             }
1061              
1062             MODULE = Linux::Event PACKAGE = Linux::Event::XS::Epoll
1063             PROTOTYPES: DISABLE
1064              
1065             void
1066             DESTROY(epobj)
1067             SV *epobj
1068             CODE:
1069 21           le_epoll_t *ep = le_epoll_from_sv(epobj);
1070 21 50         if (ep) {
1071 21 50         if (ep->epfd >= 0) {
1072 21           close(ep->epfd);
1073             }
1074 21           Safefree(ep->events);
1075 21           Safefree(ep);
1076 21           sv_setiv(SvRV(epobj), 0);
1077             }
1078              
1079              
1080             MODULE = Linux::Event PACKAGE = Linux::Event::XS::TimerHeap
1081             PROTOTYPES: DISABLE
1082              
1083             void
1084             DESTROY(heap)
1085             SV *heap
1086             CODE:
1087 21           le_timer_heap_t *h = le_timer_heap_from_sv(heap);
1088 21 50         if (h) {
1089             UV i;
1090 22 100         for (i = 0; i < h->size; i++) {
1091 1 50         if (h->heap[i].cb) SvREFCNT_dec(h->heap[i].cb);
1092             }
1093 21           Safefree(h->heap);
1094 21           Safefree(h->live);
1095 21           Safefree(h);
1096 21           sv_setiv(SvRV(heap), 0);
1097             }
1098              
1099              
1100             MODULE = Linux::Event PACKAGE = Linux::Event::XS::Registry
1101             PROTOTYPES: DISABLE
1102              
1103             void
1104             DESTROY(reg)
1105             SV *reg
1106             CODE:
1107 42           le_registry_t *r = le_registry_from_sv(reg);
1108 42 50         if (r) {
1109             UV i;
1110 10858 100         for (i = 0; i < r->cap; i++) {
1111 10816 100         if (r->slots[i]) {
1112 51           SvREFCNT_dec(r->slots[i]);
1113             }
1114             }
1115 42           Safefree(r->slots);
1116 42           Safefree(r);
1117 42           sv_setiv(SvRV(reg), 0);
1118             }