File Coverage

lib/Linux/Epoll.xs
Criterion Covered Total %
statement 112 139 80.5
total 112 139 80.5


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 22 static int S_get_fd(pTHX_ SV* fh) {
20 22 IO* io = sv_2io(fh);
21    
22 22 MAGIC* magic = mg_find((SV*)io, PERL_MAGIC_tiedscalar);
23 22 if (magic) {
24   int ret = -1;
25 2 dSP;
26 2 SAVETMPS;
27 2 PUSHMARK(SP);
28 2 PUSHs(magic->mg_obj);
29 2 PUTBACK;
30 2 call_method("FILENO", G_SCALAR);
31 2 SPAGAIN;
32 2 ret = POPi;
33 2 PUTBACK;
34 2 FREETMPS;
35   return ret;
36   }
37   else
38 20 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 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 if (!SvOK(sigmask))
68   return NULL;
69 0 if (!SvROK(sigmask) || !sv_derived_from(sigmask, "POSIX::SigSet"))
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 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   { "oneshot", 7, EPOLLONESHOT }
94   };
95    
96 5 static uint32_t S_get_eventid(pTHX_ SV* event) {
97   STRLEN len;
98 5 const char* event_name = SvPV(event, len);
99   size_t i;
100 8 for (i = 0; i < sizeof events / sizeof *events; ++i) {
101 8 if (events[i].keylen == len && strEQ(events[i].key, event_name))
102 5 return events[i].value;
103   }
104 0 Perl_croak(aTHX_ "No such event type '%s' known", event_name);
105   }
106   #define get_eventid(name) S_get_eventid(aTHX_ name)
107    
108 4 static uint32_t S_event_names_to_bits(pTHX_ SV* names) {
109 4 if (SvROK(names)) {
110 1 AV* array = (AV*)SvRV(names);
111   uint32_t ret = 0;
112   int i, len;
113   if (!SvTYPE(array) == SVt_PVAV)
114   Perl_croak(aTHX_ "event names must be string or arrayref");
115 1 len = av_len(array) + 1;
116 3 for (i = 0; i < len; ++i) {
117 2 SV** elem = av_fetch(array, i, FALSE);
118 2 ret |= get_eventid(*elem);
119   }
120   return ret;
121   }
122   else
123 3 return get_eventid(names);
124   }
125   #define event_names_to_bits(name) S_event_names_to_bits(aTHX_ name)
126    
127   static entry* S_get_event_name(pTHX_ uint32_t event_bit) {
128   size_t i;
129 9 for (i = 0; i < sizeof events / sizeof *events; ++i)
130 9 if (events[i].value == event_bit)
131   return &events[i];
132 0 Perl_croak(aTHX_ "No such event type '%d' known", event_bit);
133   }
134   #define get_event_name(event_bit) S_get_event_name(aTHX_ event_bit)
135    
136 4 static CV* S_extract_cv(pTHX_ SV* sv) {
137   HV* stash;
138   GV* gv;
139 4 CV* ret = sv_2cv(sv, &stash, &gv, FALSE);
140 4 if (!ret)
141 0 Perl_croak(aTHX_ "Couldn't convert callback parameter to a CV");
142 4 return ret;
143   }
144   #define extract_cv(sv) S_extract_cv(aTHX_ sv)
145    
146   struct data {
147   AV* backrefs;
148   int index;
149   };
150    
151 1 static int weak_set(pTHX_ SV* sv, MAGIC* magic) {
152 1 struct data* data = (struct data*)magic->mg_ptr;
153 1 av_delete(data->backrefs, data->index, G_DISCARD);
154 1 return 0;
155   }
156    
157   static int weak_free(pTHX_ SV* sv, MAGIC* magic);
158    
159   MGVTBL epoll_magic = { NULL };
160   MGVTBL weak_magic = { NULL, weak_set, NULL, NULL, weak_free };
161    
162 4 static int weak_free(pTHX_ SV* sv, MAGIC* magic) {
163   struct data* data = (struct data*)magic->mg_ptr;
164 4 mg_findext(sv, PERL_MAGIC_ext, &weak_magic)->mg_virtual = NULL; /* Cover perl bugs under the carpet */
165 4 }
166    
167   #define get_backrefs(epoll) (AV*)mg_findext(SvRV(epoll), PERL_MAGIC_ext, &epoll_magic)->mg_obj
168    
169 8 static void S_set_backref(pTHX_ SV* epoll, SV* fh, CV* callback) {
170 4 AV* backrefs = get_backrefs(epoll);
171 4 int fd = get_fd(fh);
172 4 struct data backref = { backrefs, fd };
173 4 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, cb) S_set_backref(aTHX_ epoll, fh, cb)
179    
180 2 static void S_del_backref(pTHX_ SV* epoll, SV* fh) {
181 1 av_delete(get_backrefs(epoll), get_fd(fh), G_DISCARD);
182 1 }
183   #define del_backref(epoll, fh) S_del_backref(aTHX_ epoll, fh)
184    
185   #define undef &PL_sv_undef
186    
187 2 static SV* S_io_fdopen(pTHX_ int fd) {
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 IO* io = GvIOn(gv);
192 2 IoTYPE(io) = '<';
193 2 IoIFP(io) = pio;
194 2 IoOFP(io) = pio;
195 2 return ret;
196   }
197   #define io_fdopen(fd) S_io_fdopen(aTHX_ fd)
198    
199 3 static SV* S_event_bits_to_hash(pTHX_ UV bits) {
200   int shift;
201 3 HV* ret = newHV();
202 35 for (shift = 0; shift < 32; ++shift) {
203 34 int bit_value = 1 << shift;
204 34 if (bits & bit_value) {
205 4 entry* tmp = get_event_name(bit_value);
206 4 hv_store(ret, tmp->key, tmp->keylen, &PL_sv_yes, 0);
207 4 if (bits == bit_value)
208   break;
209   }
210   }
211 3 return newRV_noinc((SV*)ret);
212   }
213   #define event_bits_to_hash(bits) S_event_bits_to_hash(aTHX_ bits)
214    
215   MODULE = Linux::Epoll PACKAGE = Linux::Epoll
216    
217   SV*
218   new(package)
219   const char* package;
220   PREINIT:
221   int fd;
222   MAGIC* mg;
223   CODE:
224   #ifdef EPOLL_CLOEXEC
225 2 fd = epoll_create1(EPOLL_CLOEXEC);
226   #else
227   fd = epoll_create(0);
228   #endif
229 2 if (fd < 0)
230 0 die_sys("Couldn't open epollfd: %s");
231 2 RETVAL = io_fdopen(fd);
232 2 mg = sv_magicext(SvRV(RETVAL), sv_2mortal((SV*)newAV()), PERL_MAGIC_ext, &epoll_magic, NULL, 0);
233 2 sv_bless(RETVAL, gv_stashpv(package, TRUE));
234   OUTPUT:
235   RETVAL
236    
237   const char*
238   add(self, fh, events, callback)
239   SV* self;
240   SV* fh;
241   SV* events;
242   SV* callback;
243   PREINIT:
244   int efd, ofd;
245   struct epoll_event event;
246   CV* real_callback;
247   MAGIC* mg;
248   CODE:
249 3 efd = get_fd(self);
250 3 ofd = get_fd(fh);
251 3 event.events = event_names_to_bits(events);
252 3 real_callback = extract_cv(callback);
253 3 event.data.ptr = real_callback;
254 3 if (epoll_ctl(efd, EPOLL_CTL_ADD, ofd, &event) == -1) {
255 0 if (GIMME_V != G_VOID && (errno == EEXIST || errno == EPERM))
256 0 XSRETURN_EMPTY;
257   else
258 0 die_sys("Couldn't add filehandle from epoll set: %s");
259   }
260 3 set_backref(self, fh, real_callback);
261   RETVAL = "0 but true";
262   OUTPUT:
263   RETVAL
264    
265   const char*
266   modify(self, fh, events, callback)
267   SV* self;
268   SV* fh;
269   SV* events;
270   SV* callback;
271   PREINIT:
272   int efd, ofd;
273   struct epoll_event event;
274   CV* real_callback;
275   CODE:
276 1 efd = get_fd(self);
277 1 ofd = get_fd(fh);
278 1 event.events = event_names_to_bits(events);
279 1 real_callback = extract_cv(callback);
280 1 event.data.ptr = real_callback;
281 1 if (epoll_ctl(efd, EPOLL_CTL_MOD, ofd, &event) == -1) {
282 0 if (GIMME_V != G_VOID && errno == ENOENT)
283 0 XSRETURN_EMPTY;
284   else
285 0 die_sys("Couldn't modify filehandle from epoll set: %s");
286   }
287 1 set_backref(self, fh, real_callback);
288   RETVAL = "0 but true";
289   OUTPUT:
290   RETVAL
291    
292   const char*
293   delete(self, fh)
294   SV* self;
295   SV* fh;
296   PREINIT:
297   int efd, ofd;
298   CODE:
299 1 efd = get_fd(self);
300 1 ofd = get_fd(fh);
301 1 if (epoll_ctl(efd, EPOLL_CTL_DEL, ofd, NULL) == -1) {
302 0 if (GIMME_V != G_VOID && errno == ENOENT)
303 0 XSRETURN_EMPTY;
304   else
305 0 die_sys("Couldn't delete filehandle from epoll set: %s");
306   }
307 1 del_backref(self, fh);
308   RETVAL = "0 but true";
309   OUTPUT:
310   RETVAL
311    
312   int
313   wait(self, maxevents = 1, timeout = undef, sigset = undef)
314   SV* self;
315   ssize_t maxevents;
316   SV* timeout;
317   SV* sigset;
318   PREINIT:
319   int efd, i;
320   int real_timeout;
321   const sigset_t* real_sigset;
322   struct epoll_event* events;
323   CODE:
324 7 if (maxevents <= 0)
325 0 Perl_croak(aTHX_ "Can't wait for a non-positive number of events (maxevents = %d)", maxevents);
326 7 efd = get_fd(self);
327 7 real_timeout = SvOK(timeout) ? (int)ceil(SvNV(timeout) * 1000) : -1;
328 7 real_sigset = SvOK(sigset) ? sv_to_sigset(sigset, "epoll_pwait") : NULL;
329    
330 7 events = alloca(sizeof(struct epoll_event) * maxevents);
331 7 RETVAL = epoll_pwait(efd, events, maxevents, real_timeout, real_sigset);
332 7 if (RETVAL == -1) {
333 1 if (errno != EINTR)
334 0 die_sys("Couldn't wait on epollfd: %s");
335 1 XSRETURN_EMPTY;
336   }
337 9 for (i = 0; i < RETVAL; ++i) {
338 3 SV* tmp = (SV*)events[i].data.ptr;
339   SvREFCNT_inc(tmp);
340 3 SAVEFREESV(tmp);
341   }
342 9 for (i = 0; i < RETVAL; ++i) {
343 3 CV* callback = (CV*) events[i].data.ptr;
344 3 PUSHMARK(SP);
345 3 mXPUSHs(event_bits_to_hash(events[i].events));
346 3 PUTBACK;
347 3 call_sv((SV*)callback, G_VOID | G_DISCARD);
348 3 SPAGAIN;
349   }
350   OUTPUT:
351   RETVAL
352    
353   int
354   CLONE_SKIP(...)
355   CODE:
356   RETVAL = 1;
357   OUTPUT:
358   RETVAL
359