File Coverage

lib/Linux/Epoll.xs
Criterion Covered Total %
statement 110 137 80.2
total 110 137 80.2


line stmt 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 if (magic) {
24   int ret = -1;
25 1 dSP;
26 1 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 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 if (!SvOK(sigmask))
66   return NULL;
67 0 if (!SvROK(sigmask) || !sv_derived_from(sigmask, "POSIX::SigSet"))
68 0 Perl_croak(aTHX_ "%s 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 for (i = 0; i < sizeof events / sizeof *events; ++i) {
105 8 if (events[i].keylen == len && strEQ(events[i].key, event_name))
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 if (SvROK(names)) {
114 1 AV* array = (AV*)SvRV(names);
115   uint32_t ret = 0;
116   int i, len;
117   if (!SvTYPE(array) == SVt_PVAV)
118   Perl_croak(aTHX_ "event names must be string or arrayref");
119 1 len = av_len(array) + 1;
120 3 for (i = 0; i < len; ++i) {
121 2 SV** elem = av_fetch(array, i, FALSE);
122 2 ret |= get_eventid(*elem);
123   }
124   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   static entry* S_get_event_name(pTHX_ uint32_t event_bit) {
132   size_t i;
133 9 for (i = 0; i < sizeof events / sizeof *events; ++i)
134 9 if (events[i].value == event_bit)
135   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 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 }
170    
171   #define get_backrefs(epoll) (AV*)mg_findext(SvRV(epoll), PERL_MAGIC_ext, &epoll_magic)->mg_obj
172    
173 8 static void S_set_backref(pTHX_ SV* epoll, SV* fh, int fd, CV* callback) {
174 4 AV* backrefs = get_backrefs(epoll);
175 4 struct data backref = { backrefs, fd };
176 4 SV* ref = sv_rvweaken(SvROK(fh) ? newSVsv(fh) : newRV(fh));
177    
178 4 av_store(backrefs, fd, ref);
179 4 sv_magicext(ref, (SV*)callback, PERL_MAGIC_ext, &weak_magic, (const char*)&backref, sizeof backref);
180 4 }
181   #define set_backref(epoll, fh, fd, cb) S_set_backref(aTHX_ epoll, fh, fd, cb)
182    
183 2 static void S_del_backref(pTHX_ SV* epoll, int fd) {
184 1 av_delete(get_backrefs(epoll), fd, G_DISCARD);
185 1 }
186   #define del_backref(epoll, fd) S_del_backref(aTHX_ epoll, fd)
187    
188   #define undef &PL_sv_undef
189    
190 2 static SV* S_io_fdopen(pTHX_ int fd, const char* package) {
191 2 PerlIO* pio = PerlIO_fdopen(fd, "r");
192 2 GV* gv = newGVgen("Linux::Epoll");
193 2 SV* ret = newRV_noinc((SV*)gv);
194 2 IO* io = GvIOn(gv);
195 2 IoTYPE(io) = '<';
196 2 IoIFP(io) = pio;
197 2 IoOFP(io) = pio;
198 2 sv_bless(ret, gv_stashpv(package, TRUE));
199 2 return ret;
200   }
201   #define io_fdopen(fd, package) S_io_fdopen(aTHX_ fd, package)
202    
203 3 static SV* S_event_bits_to_hash(pTHX_ UV bits) {
204   int shift;
205 3 HV* ret = newHV();
206 35 for (shift = 0; shift < 32; ++shift) {
207 34 int bit_value = 1 << shift;
208 34 if (bits & bit_value) {
209 4 entry* tmp = get_event_name(bit_value);
210 4 hv_store(ret, tmp->key, tmp->keylen, &PL_sv_yes, 0);
211 4 if (bits == bit_value)
212   break;
213   }
214   }
215 3 return newRV_noinc((SV*)ret);
216   }
217   #define event_bits_to_hash(bits) S_event_bits_to_hash(aTHX_ bits)
218    
219   MODULE = Linux::Epoll PACKAGE = Linux::Epoll
220    
221   SV*
222   new(package)
223   const char* package;
224   PREINIT:
225   int fd;
226   CODE:
227   #ifdef EPOLL_CLOEXEC
228 2 fd = epoll_create1(EPOLL_CLOEXEC);
229   #else
230   fd = epoll_create(0);
231   #endif
232 2 if (fd < 0)
233 0 die_sys("Couldn't open epollfd: %s");
234 2 RETVAL = io_fdopen(fd, package);
235 2 sv_magicext(SvRV(RETVAL), sv_2mortal((SV*)newAV()), PERL_MAGIC_ext, &epoll_magic, NULL, 0);
236   OUTPUT:
237   RETVAL
238    
239   const char*
240   add(self, fh, events, callback)
241   SV* self;
242   SV* fh;
243   SV* events;
244   SV* callback;
245   PREINIT:
246   int efd, ofd;
247   struct epoll_event event;
248   CV* real_callback;
249   MAGIC* mg;
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 if (epoll_ctl(efd, EPOLL_CTL_ADD, ofd, &event) == -1) {
257 0 if (GIMME_V != G_VOID && (errno == EEXIST || errno == EPERM))
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 if (epoll_ctl(efd, EPOLL_CTL_MOD, ofd, &event) == -1) {
284 0 if (GIMME_V != G_VOID && errno == ENOENT)
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 if (epoll_ctl(efd, EPOLL_CTL_DEL, ofd, NULL) == -1) {
304 0 if (GIMME_V != G_VOID && errno == ENOENT)
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 if (maxevents <= 0)
327 0 Perl_croak(aTHX_ "Can't wait for a non-positive number of events (maxevents = %d)", maxevents);
328 7 efd = get_fd(self);
329 7 real_timeout = SvOK(timeout) ? (int)ceil(SvNV(timeout) * 1000) : -1;
330 7 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 if (RETVAL == -1) {
335 1 if (errno != EINTR)
336 0 die_sys("Couldn't wait on epollfd: %s");
337 1 XSRETURN_EMPTY;
338   }
339 9 for (i = 0; i < RETVAL; ++i) {
340 3 SV* tmp = (SV*)events[i].data.ptr;
341   SvREFCNT_inc(tmp);
342 3 SAVEFREESV(tmp);
343   }
344 9 for (i = 0; i < RETVAL; ++i) {
345 3 CV* callback = (CV*) events[i].data.ptr;
346 3 PUSHMARK(SP);
347 3 mXPUSHs(event_bits_to_hash(events[i].events));
348 3 PUTBACK;
349 3 call_sv((SV*)callback, G_VOID | G_DISCARD);
350 3 SPAGAIN;
351   }
352   OUTPUT:
353   RETVAL
354    
355   int
356   CLONE_SKIP(...)
357   CODE:
358   RETVAL = 1;
359   OUTPUT:
360   RETVAL
361