File Coverage

lib/Linux/Epoll.xs
Criterion Covered Total %
statement 115 143 80.4
branch 46 88 52.2
condition n/a
subroutine n/a
pod n/a
total 161 231 69.7


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