File Coverage

lib/Linux/Epoll.xs
Criterion Covered Total %
statement 110 138 79.7
branch 68 192 35.4
condition n/a
subroutine n/a
pod n/a
total 178 330 53.9


line stmt bran cond sub pod time code
1             #ifndef _GNU_SOURCE
2             # define _GNU_SOURCE
3             #endif
4             #define GNU_STRERROR_R
5              
6             #include
7             #include
8              
9             #include
10              
11             #define PERL_NO_GET_CONTEXT
12             #include "EXTERN.h"
13             #include "perl.h"
14             #include "XSUB.h"
15             #define NEED_mg_findext
16             #define NEED_sv_unmagicext
17             #include "ppport.h"
18              
19 17           static int S_get_fd(pTHX_ SV* fh) {
20 17           IO* io = sv_2io(fh);
21              
22 17           MAGIC* magic = mg_find((SV*)io, PERL_MAGIC_tiedscalar);
23 17 100         if (magic) {
24             int ret = -1;
25 1           dSP;
26 1           SAVETMPS;
27 1 50         PUSHMARK(SP);
28 1           PUSHs(magic->mg_obj);
29 1           PUTBACK;
30 1           call_method("FILENO", G_SCALAR);
31 1           SPAGAIN;
32 1 50         ret = POPi;
33 1           PUTBACK;
34 1 50         FREETMPS;
35             return ret;
36             }
37             else
38 16           return PerlIO_fileno(IoIFP(io));
39             }
40             #define get_fd(fh) S_get_fd(aTHX_ fh)
41              
42 0           static void get_sys_error(char* buffer, size_t buffer_size) {
43             #if _POSIX_VERSION >= 200112L
44             # ifdef GNU_STRERROR_R
45 0           const char* message = strerror_r(errno, buffer, buffer_size);
46 0 0         if (message != buffer)
47             memcpy(buffer, message, buffer_size);
48             # else
49             strerror_r(errno, buffer, buffer_size);
50             # endif
51             #else
52             const char* message = strerror(errno);
53             strncpy(buffer, message, buffer_size - 1);
54             buffer[buffer_size - 1] = '\0';
55             #endif
56 0           }
57              
58 0           static void S_die_sys(pTHX_ const char* format) {
59             char buffer[128];
60 0           get_sys_error(buffer, sizeof buffer);
61 0           Perl_croak(aTHX_ format, buffer);
62             }
63             #define die_sys(format) S_die_sys(aTHX_ format)
64              
65 0           static sigset_t* S_sv_to_sigset(pTHX_ SV* sigmask, const char* name) {
66             IV tmp;
67 0 0         if (!SvOK(sigmask))
    0          
    0          
68             return NULL;
69 0 0         if (!SvROK(sigmask) || !sv_derived_from(sigmask, "POSIX::SigSet"))
    0          
70 0           Perl_croak(aTHX_ "%s is not of type POSIX::SigSet");
71             #if PERL_VERSION > 15 || PERL_VERSION == 15 && PERL_SUBVERSION > 2
72 0 0         return (sigset_t *) SvPV_nolen(SvRV(sigmask));
73             #else
74             tmp = SvIV((SV*)SvRV(sigmask));
75             return INT2PTR(sigset_t*, tmp);
76             #endif
77             }
78             #define sv_to_sigset(sigmask, name) S_sv_to_sigset(aTHX_ sigmask, name)
79              
80             typedef struct { const char* key; size_t keylen; uint32_t value; } entry;
81             typedef entry map[];
82              
83             static map events = {
84             { "in" , 2, EPOLLIN },
85             { "out" , 3, EPOLLOUT },
86             { "err" , 3, EPOLLERR },
87             { "prio" , 4, EPOLLPRI },
88             { "et" , 2, EPOLLET },
89             { "hup" , 3, EPOLLHUP },
90             #ifdef EPOLLRDHUP
91             { "rdhup" , 5, EPOLLRDHUP },
92             #endif
93             #ifdef EPOLLWAKEUP
94             { "wakeup" , 6, EPOLLWAKEUP },
95             #endif
96             #ifdef EPOLLEXCLUSIVE
97             { "exclusive", 9, EPOLLEXCLUSIVE },
98             #endif
99             { "oneshot" , 7, EPOLLONESHOT }
100             };
101              
102 5           static uint32_t S_get_eventid(pTHX_ SV* event) {
103             STRLEN len;
104 5 50         const char* event_name = SvPV(event, len);
105             size_t i;
106 8 50         for (i = 0; i < sizeof events / sizeof *events; ++i) {
107 8 100         if (events[i].keylen == len && strEQ(events[i].key, event_name))
    50          
108 5           return events[i].value;
109             }
110 0           Perl_croak(aTHX_ "No such event type '%s' known", event_name);
111             }
112             #define get_eventid(name) S_get_eventid(aTHX_ name)
113              
114 4           static uint32_t S_event_names_to_bits(pTHX_ SV* names) {
115 4 100         if (SvROK(names)) {
116 1           AV* array = (AV*)SvRV(names);
117             uint32_t ret = 0;
118             int i, len;
119             if (!SvTYPE(array) == SVt_PVAV)
120             Perl_croak(aTHX_ "event names must be string or arrayref");
121 1           len = av_len(array) + 1;
122 3 100         for (i = 0; i < len; ++i) {
123 2           SV** elem = av_fetch(array, i, FALSE);
124 2           ret |= get_eventid(*elem);
125             }
126             return ret;
127             }
128             else
129 3           return get_eventid(names);
130             }
131             #define event_names_to_bits(name) S_event_names_to_bits(aTHX_ name)
132              
133             static entry* S_get_event_name(pTHX_ uint32_t event_bit) {
134             size_t i;
135 9 50         for (i = 0; i < sizeof events / sizeof *events; ++i)
136 9 100         if (events[i].value == event_bit)
137             return &events[i];
138 0           Perl_croak(aTHX_ "No such event type '%d' known", event_bit);
139             }
140             #define get_event_name(event_bit) S_get_event_name(aTHX_ event_bit)
141              
142 4           static CV* S_extract_cv(pTHX_ SV* sv) {
143             HV* stash;
144             GV* gv;
145 4           CV* ret = sv_2cv(sv, &stash, &gv, FALSE);
146 4 50         if (!ret)
147 0           Perl_croak(aTHX_ "Couldn't convert callback parameter to a CV");
148 4           return ret;
149             }
150             #define extract_cv(sv) S_extract_cv(aTHX_ sv)
151              
152             struct data {
153             AV* backrefs;
154             int index;
155             };
156              
157 1           static int weak_set(pTHX_ SV* sv, MAGIC* magic) {
158 1 50         if (!SvOK(sv) && !PL_dirty) {
    50          
    50          
    50          
159 1           struct data* data = (struct data*)magic->mg_ptr;
160 1           av_delete(data->backrefs, data->index, G_DISCARD);
161             }
162 1           return 0;
163             }
164              
165             MGVTBL epoll_magic = { NULL };
166             MGVTBL weak_magic = { NULL, weak_set, NULL, NULL, NULL };
167              
168             #define get_backrefs(epoll) (AV*)mg_findext(SvRV(epoll), PERL_MAGIC_ext, &epoll_magic)->mg_obj
169              
170 8           static void S_set_backref(pTHX_ SV* epoll, SV* fh, int fd, CV* callback) {
171 4           AV* backrefs = get_backrefs(epoll);
172 4           struct data backref = { backrefs, fd };
173 4 100         SV* ref = sv_rvweaken(SvROK(fh) ? newSVsv(fh) : newRV(fh));
174              
175 4           av_store(backrefs, fd, ref);
176 4           sv_magicext(ref, (SV*)callback, PERL_MAGIC_ext, &weak_magic, (const char*)&backref, sizeof backref);
177 4           }
178             #define set_backref(epoll, fh, fd, cb) S_set_backref(aTHX_ epoll, fh, fd, cb)
179              
180 2           static void S_del_backref(pTHX_ SV* epoll, int fd) {
181 1           av_delete(get_backrefs(epoll), fd, G_DISCARD);
182 1           }
183             #define del_backref(epoll, fd) S_del_backref(aTHX_ epoll, fd)
184              
185             #define undef &PL_sv_undef
186              
187 2           static SV* S_io_fdopen(pTHX_ int fd, const char* package) {
188 2           PerlIO* pio = PerlIO_fdopen(fd, "r");
189 2           GV* gv = newGVgen("Linux::Epoll");
190 2           SV* ret = newRV_noinc((SV*)gv);
191 2 50         IO* io = GvIOn(gv);
    50          
    50          
    50          
192 2           IoTYPE(io) = '<';
193 2           IoIFP(io) = pio;
194 2           IoOFP(io) = pio;
195 2           sv_bless(ret, gv_stashpv(package, TRUE));
196 2           return ret;
197             }
198             #define io_fdopen(fd, package) S_io_fdopen(aTHX_ fd, package)
199              
200 3           static SV* S_event_bits_to_hash(pTHX_ UV bits) {
201             int shift;
202 3           HV* ret = newHV();
203 35 100         for (shift = 0; shift < 32; ++shift) {
204 34           int bit_value = 1 << shift;
205 34 100         if (bits & bit_value) {
206 4           entry* tmp = get_event_name(bit_value);
207 4           hv_store(ret, tmp->key, tmp->keylen, &PL_sv_yes, 0);
208 4 100         if (bits == bit_value)
209             break;
210             }
211             }
212 3           return newRV_noinc((SV*)ret);
213             }
214             #define event_bits_to_hash(bits) S_event_bits_to_hash(aTHX_ bits)
215              
216             MODULE = Linux::Epoll PACKAGE = Linux::Epoll
217              
218             SV*
219             new(package)
220             const char* package;
221             PREINIT:
222             int fd;
223             CODE:
224             #ifdef EPOLL_CLOEXEC
225 2           fd = epoll_create1(EPOLL_CLOEXEC);
226             #else
227             fd = epoll_create(0);
228             #endif
229 2 50         if (fd < 0)
230 0           die_sys("Couldn't open epollfd: %s");
231 2           RETVAL = io_fdopen(fd, package);
232 2           sv_magicext(SvRV(RETVAL), sv_2mortal((SV*)newAV()), PERL_MAGIC_ext, &epoll_magic, NULL, 0);
233             OUTPUT:
234             RETVAL
235              
236             const char*
237             add(self, fh, events, callback)
238             SV* self;
239             SV* fh;
240             SV* events;
241             SV* callback;
242             PREINIT:
243             int efd, ofd;
244             struct epoll_event event;
245             CV* real_callback;
246             MAGIC* mg;
247             CODE:
248 3           efd = get_fd(self);
249 3           ofd = get_fd(fh);
250 3           event.events = event_names_to_bits(events);
251 3           real_callback = extract_cv(callback);
252 3           event.data.ptr = real_callback;
253 3 50         if (epoll_ctl(efd, EPOLL_CTL_ADD, ofd, &event) == -1) {
254 0 0         if (GIMME_V != G_VOID && (errno == EEXIST || errno == EPERM))
    0          
    0          
    0          
255 0           XSRETURN_EMPTY;
256             else
257 0           die_sys("Couldn't add filehandle from epoll set: %s");
258             }
259 3           set_backref(self, fh, ofd, real_callback);
260             RETVAL = "0 but true";
261             OUTPUT:
262             RETVAL
263              
264             const char*
265             modify(self, fh, events, callback)
266             SV* self;
267             SV* fh;
268             SV* events;
269             SV* callback;
270             PREINIT:
271             int efd, ofd;
272             struct epoll_event event;
273             CV* real_callback;
274             CODE:
275 1           efd = get_fd(self);
276 1           ofd = get_fd(fh);
277 1           event.events = event_names_to_bits(events);
278 1           real_callback = extract_cv(callback);
279 1           event.data.ptr = real_callback;
280 1 50         if (epoll_ctl(efd, EPOLL_CTL_MOD, ofd, &event) == -1) {
281 0 0         if (GIMME_V != G_VOID && errno == ENOENT)
    0          
    0          
    0          
282 0           XSRETURN_EMPTY;
283             else
284 0           die_sys("Couldn't modify filehandle from epoll set: %s");
285             }
286 1           set_backref(self, fh, ofd, real_callback);
287             RETVAL = "0 but true";
288             OUTPUT:
289             RETVAL
290              
291             const char*
292             delete(self, fh)
293             SV* self;
294             SV* fh;
295             PREINIT:
296             int efd, ofd;
297             CODE:
298 1           efd = get_fd(self);
299 1           ofd = get_fd(fh);
300 1 50         if (epoll_ctl(efd, EPOLL_CTL_DEL, ofd, NULL) == -1) {
301 0 0         if (GIMME_V != G_VOID && errno == ENOENT)
    0          
    0          
    0          
302 0           XSRETURN_EMPTY;
303             else
304 0           die_sys("Couldn't delete filehandle from epoll set: %s");
305             }
306 1           del_backref(self, ofd);
307             RETVAL = "0 but true";
308             OUTPUT:
309             RETVAL
310              
311             int
312             wait(self, maxevents = 1, timeout = undef, sigset = undef)
313             SV* self;
314             ssize_t maxevents;
315             SV* timeout;
316             SV* sigset;
317             PREINIT:
318             int efd, i;
319             int real_timeout;
320             const sigset_t* real_sigset;
321             struct epoll_event* events;
322             CODE:
323 7 50         if (maxevents <= 0)
324 0           Perl_croak(aTHX_ "Can't wait for a non-positive number of events (maxevents = %d)", maxevents);
325 7           efd = get_fd(self);
326 7 50         real_timeout = SvOK(timeout) ? (int)ceil(SvNV(timeout) * 1000) : -1;
    0          
    0          
    50          
327 7 50         real_sigset = SvOK(sigset) ? sv_to_sigset(sigset, "epoll_pwait") : NULL;
    50          
    50          
328              
329 7           events = alloca(sizeof(struct epoll_event) * maxevents);
330 7           RETVAL = epoll_pwait(efd, events, maxevents, real_timeout, real_sigset);
331 7 100         if (RETVAL == -1) {
332 1 50         if (errno != EINTR)
333 0           die_sys("Couldn't wait on epollfd: %s");
334 1           XSRETURN_EMPTY;
335             }
336 9 100         for (i = 0; i < RETVAL; ++i) {
337 3           SV* tmp = (SV*)events[i].data.ptr;
338             SvREFCNT_inc(tmp);
339 3           SAVEFREESV(tmp);
340             }
341 9 100         for (i = 0; i < RETVAL; ++i) {
342 3           CV* callback = (CV*) events[i].data.ptr;
343 3 50         PUSHMARK(SP);
344 3 50         mXPUSHs(event_bits_to_hash(events[i].events));
345 3           PUTBACK;
346 3           call_sv((SV*)callback, G_VOID | G_DISCARD | G_EVAL);
347 3           SPAGAIN;
348 3 50         if (SvTRUE(ERRSV))
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
349 0 0         warn_sv(ERRSV);
350             }
351             OUTPUT:
352             RETVAL
353              
354             int
355             CLONE_SKIP(...)
356             CODE:
357             RETVAL = 1;
358             OUTPUT:
359             RETVAL
360