File Coverage

lib/Linux/FD.xs
Criterion Covered Total %
statement 117 149 78.5
branch 67 138 48.5
condition n/a
subroutine n/a
pod n/a
total 184 287 64.1


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              
8             #include
9             #include
10             #include
11              
12             #define PERL_NO_GET_CONTEXT
13             #define PERL_REENTR_API 1
14             #include "EXTERN.h"
15             #include "perl.h"
16             #include "XSUB.h"
17             #include "ppport.h"
18              
19             #define get_fd(self) PerlIO_fileno(IoOFP(sv_2io(SvRV(self))));
20              
21             #define die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
22              
23 2           static sigset_t* S_sv_to_sigset(pTHX_ SV* sigmask, const char* name) {
24             IV tmp;
25 1 50         if (!SvOK(sigmask))
    0          
    0          
26             return NULL;
27 1 50         if (!SvROK(sigmask) || !sv_derived_from(sigmask, "POSIX::SigSet"))
    50          
28 0           Perl_croak(aTHX_ "%s is not of type POSIX::SigSet");
29             #if PERL_VERSION > 15 || PERL_VERSION == 15 && PERL_SUBVERSION > 2
30 1 50         return (sigset_t *) SvPV_nolen(SvRV(sigmask));
31             #else
32             tmp = SvIV((SV*)SvRV(sigmask));
33             return INT2PTR(sigset_t*, tmp);
34             #endif
35             }
36             #define sv_to_sigset(sigmask, name) S_sv_to_sigset(aTHX_ sigmask, name)
37              
38 6           static sigset_t* S_get_sigset(pTHX_ SV* signal, const char* name) {
39 3 100         if (SvROK(signal))
40 1           return sv_to_sigset(signal, name);
41             else {
42 2 100         int signo = (SvIOK(signal) || looks_like_number(signal)) && SvIV(signal) ? SvIV(signal) : whichsig(SvPV_nolen(signal));
    50          
    50          
    50          
    0          
    50          
    50          
43 2           SV* buffer = sv_2mortal(newSVpvn("", 0));
44 2 50         SvGROW(buffer, sizeof(sigset_t));
    50          
45 2 50         sigset_t* ret = (sigset_t*)SvPV_nolen(buffer);
46 2           sigemptyset(ret);
47 2           sigaddset(ret, signo);
48             return ret;
49             }
50             }
51             #define get_sigset(sigmask, name) S_get_sigset(aTHX_ sigmask, name)
52              
53             #define NANO_SECONDS 1000000000
54              
55             static NV timespec_to_nv(struct timespec* time) {
56 4           return time->tv_sec + time->tv_nsec / (double)NANO_SECONDS;
57             }
58              
59             static void nv_to_timespec(NV input, struct timespec* output) {
60 4           output->tv_sec = (time_t) floor(input);
61 4           output->tv_nsec = (long) ((input - output->tv_sec) * NANO_SECONDS);
62             }
63              
64             typedef struct { const char* key; size_t length; int value; } map[];
65              
66             static map clocks = {
67             { STR_WITH_LEN("monotonic") , CLOCK_MONOTONIC },
68             { STR_WITH_LEN("realtime") , CLOCK_REALTIME },
69             #ifdef CLOCK_BOOTTIME
70             { STR_WITH_LEN("boottime") , CLOCK_BOOTTIME },
71             #endif
72             #ifdef CLOCK_REALTIME_ALARM
73             { STR_WITH_LEN("realtime_alarm"), CLOCK_REALTIME_ALARM },
74             #endif
75             #ifdef CLOCK_BOOTTIME_ALARM
76             { STR_WITH_LEN("boottime_alarm"), CLOCK_BOOTTIME_ALARM },
77             #endif
78             };
79              
80 1           static clockid_t S_get_clockid(pTHX_ const char* clock_name) {
81             int i;
82 2 50         for (i = 0; i < sizeof clocks / sizeof *clocks; ++i) {
83 2 100         if (strEQ(clock_name, clocks[i].key))
84 1           return clocks[i].value;
85             }
86 0           Perl_croak(aTHX_ "No such timer '%s' known", clock_name);
87             }
88             #define get_clockid(name) S_get_clockid(aTHX_ name)
89              
90 0           static clockid_t S_get_clock(pTHX_ SV* ref, const char* funcname) {
91             SV* value;
92 0 0         if (!SvROK(ref) || !(value = SvRV(ref)))
    0          
93 0           Perl_croak(aTHX_ "Could not %s: this variable is not a clock", funcname);
94 0 0         return SvIV(value);
95             }
96             #define get_clock(ref, func) S_get_clock(aTHX_ ref, func)
97              
98 6           static SV* S_io_fdopen(pTHX_ int fd, const char* classname, char type) {
99 6           PerlIO* pio = PerlIO_fdopen(fd, "r");
100 6           GV* gv = newGVgen(classname);
101 6           SV* ret = newRV_noinc((SV*)gv);
102 6 50         IO* io = GvIOn(gv);
    50          
    50          
    50          
103 6           HV* stash = gv_stashpv(classname, FALSE);
104 6           IoTYPE(io) = type;
105 6           IoIFP(io) = pio;
106 6           IoOFP(io) = pio;
107 6           sv_bless(ret, stash);
108 6           return ret;
109             }
110             #define io_fdopen(fd, classname, type) S_io_fdopen(aTHX_ fd, classname, type)
111              
112             #define SET_HASH_IMPL(key,value) hv_store(hash, key, sizeof key - 1, value, 0)
113             #define SET_HASH_U(key) SET_HASH_IMPL(#key, newSVuv(buffer.ssi_##key))
114             #define SET_HASH_I(key) SET_HASH_IMPL(#key, newSViv(buffer.ssi_##key))
115              
116 5           static UV S_get_flag(pTHX_ map flags, size_t map_size, SV* flag_name) {
117             int i;
118 6 50         for (i = 0; i < map_size / sizeof *flags; ++i)
119 6 50         if (strEQ(SvPV_nolen(flag_name), flags[i].key))
    100          
120 5           return flags[i].value;
121 0 0         Perl_croak(aTHX_ "No such flag '%s' known", SvPV_nolen(flag_name));
122             }
123             #define get_flag(map, name) S_get_flag(aTHX_ map, sizeof(map), name)
124              
125             static map event_flags = {
126             { STR_WITH_LEN("non-blocking") , EFD_NONBLOCK },
127             { STR_WITH_LEN("semaphore") , EFD_SEMAPHORE },
128             };
129             #define get_event_flag(name) get_flag(event_flags, name)
130              
131 2           static SV* S_new_eventfd(pTHX_ const char* classname, UV initial, int flags) {
132 2           int fd = eventfd(initial, flags);
133 2 50         if (fd < 0)
134 0           die_sys("Can't open eventfd descriptor: %s");
135 2           return io_fdopen(fd, classname, '|');
136             }
137             #define new_eventfd(classname, initial, flags) S_new_eventfd(aTHX_ classname, initial, flags)
138              
139             static map signal_flags = {
140             { STR_WITH_LEN("non-blocking") , SFD_NONBLOCK },
141             };
142             #define get_signal_flag(name) get_flag(signal_flags, name)
143              
144 3           static SV* S_new_signalfd(pTHX_ const char* classname, SV* sigmask, int flags) {
145 3           int fd = signalfd(-1, get_sigset(sigmask, "signalfd"), flags);
146 3 50         if (fd < 0)
147 0           die_sys("Can't open signalfd descriptor: %s");
148 3           return io_fdopen(fd, classname, '<');
149             }
150             #define new_signalfd(classname, sigset, flags) S_new_signalfd(aTHX_ classname, sigset, flags)
151              
152             static map timer_flags = {
153             { STR_WITH_LEN("non-blocking") , TFD_NONBLOCK },
154             };
155             #define get_timer_flag(name) get_flag(timer_flags, name)
156              
157 1           static SV* S_new_timerfd(pTHX_ const char* classname, SV* clock, int flags, const char* funcname) {
158 1 50         clockid_t clock_id = SvROK(clock) ? get_clock(clock, funcname) : get_clockid(SvPV_nolen(clock));
    50          
159 1           int fd = timerfd_create(clock_id, flags);
160 1 50         if (fd < 0)
161 0           die_sys("Can't open timerfd descriptor: %s");
162 1           return io_fdopen(fd, classname, '<');
163             }
164             #define new_timerfd(classname, clock, flags, func) S_new_timerfd(aTHX_ classname, clock, flags, func)
165              
166 13           static int S_interrupted(pTHX_ int value) {
167 13 100         if (value == -1 && errno == EINTR) {
    50          
168 0 0         PERL_ASYNC_CHECK();
169             return 1;
170             }
171             return 0;
172             }
173             #define interrupted(value) S_interrupted(aTHX_ value)
174              
175             MODULE = Linux::FD PACKAGE = Linux::FD
176              
177             BOOT:
178 3           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("IO::Handle"), NULL);
179 3           av_push(get_av("Linux::FD::Event::ISA" , GV_ADD), newSVpvs("IO::Handle"));
180 3           av_push(get_av("Linux::FD::Signal::ISA", GV_ADD), newSVpvs("IO::Handle"));
181 3           av_push(get_av("Linux::FD::Timer::ISA" , GV_ADD), newSVpvs("IO::Handle"));
182              
183             SV*
184             eventfd(initial = 0, ...)
185             UV initial;
186             PREINIT:
187             int i, flags = EFD_CLOEXEC;
188             CODE:
189 5 100         for (i = 1; i < items; i++)
190 3           flags |= get_event_flag(ST(i));
191 2           RETVAL = new_eventfd("Linux::FD::Event", initial, flags);
192             OUTPUT:
193             RETVAL
194              
195             SV*
196             signalfd(sigmask, ...)
197             SV* sigmask;
198             PREINIT:
199             int i, flags = SFD_CLOEXEC;
200             CODE:
201 4 100         for (i = 1; i < items; i++)
202 1           flags |= get_signal_flag(ST(i));
203 3           RETVAL = new_signalfd("Linux::FD::Signal", sigmask, flags);
204             OUTPUT:
205             RETVAL
206              
207             SV*
208             timerfd(clock, ...)
209             SV* clock;
210             PREINIT:
211             int i, flags = TFD_CLOEXEC;
212             CODE:
213 2 100         for (i = 1; i < items; i++)
214 1           flags |= get_timer_flag(ST(i));
215 1           RETVAL = new_timerfd("Linux::FD::Timer", clock, flags, "timerfd");
216             OUTPUT:
217             RETVAL
218              
219             MODULE = Linux::FD PACKAGE = Linux::FD::Event
220              
221             SV*
222             new(classname, initial = 0, ...)
223             const char* classname;
224             UV initial;
225             PREINIT:
226             int i, flags = EFD_CLOEXEC;
227             CODE:
228 0 0         for (i = 2; i < items; i++)
229 0           flags |= get_event_flag(ST(i));
230 0           RETVAL = new_eventfd(classname, initial, flags);
231             OUTPUT:
232             RETVAL
233              
234             UV
235             get(self)
236             SV* self;
237             PREINIT:
238             uint64_t buffer;
239             int ret, events;
240             CODE:
241 6           events = get_fd(self);
242             do {
243 6           ret = read(events, &buffer, sizeof buffer);
244 6 50         } while (interrupted(ret));
245 6 100         if (ret == -1) {
246 3 50         if (errno == EAGAIN)
247 3           XSRETURN_EMPTY;
248             else
249 0           die_sys("Couldn't read from eventfd: %s");
250             }
251 3           RETVAL = buffer;
252             OUTPUT:
253             RETVAL
254              
255             UV
256             add(self, value)
257             SV* self;
258             UV value;
259             PREINIT:
260             uint64_t buffer;
261             int ret, events;
262             CODE:
263 2           events = get_fd(self);
264 2           buffer = value;
265             do {
266 2           ret = write(events, &buffer, sizeof buffer);
267 2 50         } while (interrupted(ret));
268 2 50         if (ret == -1) {
269 0 0         if (errno == EAGAIN)
270 0           XSRETURN_EMPTY;
271             else
272 0           die_sys("Couldn't write to eventfd: %s");
273             }
274             RETVAL = value;
275             OUTPUT:
276             RETVAL
277              
278              
279             MODULE = Linux::FD PACKAGE = Linux::FD::Signal
280              
281             SV*
282             new(classname, sigmask, ...)
283             const char* classname;
284             SV* sigmask;
285             PREINIT:
286             int i, flags = SFD_CLOEXEC;
287             CODE:
288 0 0         for (i = 2; i < items; i++)
289 0           flags |= get_signal_flag(ST(i));
290 0           RETVAL = new_signalfd(classname, sigmask, flags);
291             OUTPUT:
292             RETVAL
293              
294             void set_mask(self, sigmask)
295             SV* self;
296             SV* sigmask;
297             PREINIT:
298             int fd;
299             CODE:
300 0           fd = get_fd(self);
301 0 0         if(signalfd(fd, sv_to_sigset(sigmask, "signalfd"), 0) == -1)
302 0           die_sys("Couldn't set_mask: %s");
303              
304             SV*
305             receive(self)
306             SV* self;
307             PREINIT:
308             struct signalfd_siginfo buffer;
309             int tmp, timer;
310             HV* hash;
311             CODE:
312 2           timer = get_fd(self);
313             do {
314 2           tmp = read(timer, &buffer, sizeof buffer);
315 2 50         } while (interrupted(tmp));
316 2 100         if (tmp == -1) {
317 1 50         if (errno == EAGAIN)
318 1           XSRETURN_EMPTY;
319             else
320 0           die_sys("Couldn't read from signalfd: %s");
321             }
322 1           hash = newHV();
323 1           SET_HASH_U(signo);
324 1           SET_HASH_I(errno);
325 1           SET_HASH_I(code);
326 1           SET_HASH_U(pid);
327 1           SET_HASH_U(uid);
328 1           SET_HASH_I(fd);
329 1           SET_HASH_U(tid);
330 1           SET_HASH_U(band);
331 1           SET_HASH_U(overrun);
332 1           SET_HASH_U(trapno);
333 1           SET_HASH_I(status);
334 1           SET_HASH_I(int);
335 1           SET_HASH_U(ptr);
336 1           SET_HASH_U(utime);
337 1           SET_HASH_U(stime);
338 1           SET_HASH_U(addr);
339 1           RETVAL = newRV_noinc((SV*)hash);
340             OUTPUT:
341             RETVAL
342              
343              
344             MODULE = Linux::FD PACKAGE = Linux::FD::Timer
345              
346             SV*
347             new(classname, clock)
348             const char* classname;
349             SV* clock;
350             PREINIT:
351             int i, flags = TFD_CLOEXEC;
352             CODE:
353 0 0         for (i = 2; i < items; i++)
354 0           flags |= get_timer_flag(ST(i));
355 0           RETVAL = new_timerfd(classname, clock, flags, "Linux::FD::Timer->new");
356             OUTPUT:
357             RETVAL
358              
359             void
360             get_timeout(self)
361             SV* self;
362             PREINIT:
363             int timer;
364             struct itimerspec value;
365             PPCODE:
366 1           timer = get_fd(self);
367 1 50         if (timerfd_gettime(timer, &value) == -1)
368 0           die_sys("Couldn't get_timeout: %s");
369 1 50         mXPUSHn(timespec_to_nv(&value.it_value));
370 1 50         if (GIMME_V == G_ARRAY)
    50          
371 1 50         mXPUSHn(timespec_to_nv(&value.it_interval));
372              
373             SV*
374             set_timeout(self, new_value, new_interval = 0, abstime = 0)
375             SV* self;
376             NV new_value;
377             NV new_interval;
378             IV abstime;
379             PREINIT:
380             int timer;
381             struct itimerspec new_itimer, old_itimer;
382             PPCODE:
383 2           timer = get_fd(self);
384             nv_to_timespec(new_value, &new_itimer.it_value);
385             nv_to_timespec(new_interval, &new_itimer.it_interval);
386 2 50         if (timerfd_settime(timer, (abstime ? TIMER_ABSTIME : 0), &new_itimer, &old_itimer) == -1)
387 0           die_sys("Couldn't set_timeout: %s");
388 2 50         mXPUSHn(timespec_to_nv(&old_itimer.it_value));
389 2 50         if (GIMME_V == G_ARRAY)
    50          
390 0 0         mXPUSHn(timespec_to_nv(&old_itimer.it_interval));
391              
392             IV
393             receive(self)
394             SV* self;
395             PREINIT:
396             uint64_t buffer;
397             int ret, timer;
398             CODE:
399 3           timer = get_fd(self);
400             do {
401 3           ret = read(timer, &buffer, sizeof buffer);
402 3 50         } while (interrupted(ret));
403 3 100         if (ret == -1) {
404 1 50         if (errno == EAGAIN)
405 1           XSRETURN_EMPTY;
406             else
407 0           die_sys("Couldn't read from timerfd: %s");
408             }
409 2           RETVAL = buffer;
410             OUTPUT:
411             RETVAL
412              
413             void
414             clocks(classname)
415             SV* classname;
416             INIT:
417             int i;
418             PPCODE:
419 6 100         for (i = 0; i < sizeof clocks / sizeof *clocks; ++i)
420 5 50         mXPUSHp(clocks[i].key, clocks[i].length);