File Coverage

lib/Linux/FD.xs
Criterion Covered Total %
statement 82 112 73.2
branch 49 94 52.1
condition n/a
subroutine n/a
pod n/a
total 131 206 63.5


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 die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
20              
21             #define NANO_SECONDS 1000000000
22              
23             static NV timespec_to_nv(struct timespec* time) {
24 4           return time->tv_sec + time->tv_nsec / (double)NANO_SECONDS;
25             }
26              
27             typedef struct { const char* key; size_t length; int value; } map[];
28              
29             static map clocks = {
30             { STR_WITH_LEN("monotonic") , CLOCK_MONOTONIC },
31             { STR_WITH_LEN("realtime") , CLOCK_REALTIME },
32             #ifdef CLOCK_BOOTTIME
33             { STR_WITH_LEN("boottime") , CLOCK_BOOTTIME },
34             #endif
35             #ifdef CLOCK_REALTIME_ALARM
36             { STR_WITH_LEN("realtime_alarm"), CLOCK_REALTIME_ALARM },
37             #endif
38             #ifdef CLOCK_BOOTTIME_ALARM
39             { STR_WITH_LEN("boottime_alarm"), CLOCK_BOOTTIME_ALARM },
40             #endif
41             };
42              
43 1           static clockid_t S_get_clock(pTHX_ SV* clock, const char* funcname) {
44 1 50         if (SvROK(clock)) {
45             SV* value;
46 0 0         if (!SvROK(clock) || !(value = SvRV(clock)))
47 0           Perl_croak(aTHX_ "Could not %s: this variable is not a clock", funcname);
48 0           return SvIV(value);
49             } else {
50             int i;
51 1           const char* clock_name = SvPV_nolen(clock);
52 2 50         for (i = 0; i < sizeof clocks / sizeof *clocks; ++i) {
53 2 100         if (strEQ(clock_name, clocks[i].key))
54 1           return clocks[i].value;
55             }
56 0           Perl_croak(aTHX_ "No such timer '%s' known", clock_name);
57             }
58             }
59             #define get_clock(ref, func) S_get_clock(aTHX_ ref, func)
60              
61 6           static SV* S_io_fdopen(pTHX_ int fd, const char* classname, char type) {
62 6           PerlIO* pio = PerlIO_fdopen(fd, "r");
63 6           GV* gv = newGVgen(classname);
64 6           SV* ret = newRV_noinc((SV*)gv);
65 6 50         IO* io = GvIOn(gv);
    50          
    50          
    50          
66 6           HV* stash = gv_stashpv(classname, FALSE);
67 6           IoTYPE(io) = type;
68 6           IoIFP(io) = pio;
69 6           IoOFP(io) = pio;
70 6           sv_bless(ret, stash);
71 6           return ret;
72             }
73             #define io_fdopen(fd, classname, type) S_io_fdopen(aTHX_ fd, classname, type)
74              
75             #define SET_HASH_IMPL(key,value) hv_store(hash, key, sizeof key - 1, value, 0)
76             #define SET_HASH_U(key) SET_HASH_IMPL(#key, newSVuv(buffer.ssi_##key))
77             #define SET_HASH_I(key) SET_HASH_IMPL(#key, newSViv(buffer.ssi_##key))
78              
79 5           static UV S_get_flag(pTHX_ map flags, size_t map_size, SV* flag_name) {
80             int i;
81 6 50         for (i = 0; i < map_size / sizeof *flags; ++i)
82 6 100         if (strEQ(SvPV_nolen(flag_name), flags[i].key))
83 5           return flags[i].value;
84 0           Perl_croak(aTHX_ "No such flag '%s' known", SvPV_nolen(flag_name));
85             }
86             #define get_flag(map, name) S_get_flag(aTHX_ map, sizeof(map), name)
87              
88             static map event_flags = {
89             { STR_WITH_LEN("non-blocking") , EFD_NONBLOCK },
90             { STR_WITH_LEN("semaphore") , EFD_SEMAPHORE },
91             };
92             #define get_event_flag(name) get_flag(event_flags, name)
93              
94 2           static SV* S_new_eventfd(pTHX_ const char* classname, UV initial, int flags) {
95 2           int fd = eventfd(initial, flags);
96 2 50         if (fd < 0)
97 0           die_sys("Can't open eventfd descriptor: %s");
98 2           return io_fdopen(fd, classname, '|');
99             }
100             #define new_eventfd(classname, initial, flags) S_new_eventfd(aTHX_ classname, initial, flags)
101              
102             static map signal_flags = {
103             { STR_WITH_LEN("non-blocking") , SFD_NONBLOCK },
104             };
105             #define get_signal_flag(name) get_flag(signal_flags, name)
106              
107 3           static SV* S_new_signalfd(pTHX_ const char* classname, const sigset_t* sigmask, int flags) {
108 3           int fd = signalfd(-1, sigmask, flags);
109 3 50         if (fd < 0)
110 0           die_sys("Can't open signalfd descriptor: %s");
111 3           return io_fdopen(fd, classname, '<');
112             }
113             #define new_signalfd(classname, sigset, flags) S_new_signalfd(aTHX_ classname, sigset, flags)
114              
115             static map timer_flags = {
116             { STR_WITH_LEN("non-blocking") , TFD_NONBLOCK },
117             };
118             #define get_timer_flag(name) get_flag(timer_flags, name)
119              
120 1           static SV* S_new_timerfd(pTHX_ const char* classname, SV* clock, int flags, const char* funcname) {
121 1           clockid_t clock_id = get_clock(clock, funcname);
122 1           int fd = timerfd_create(clock_id, flags);
123 1 50         if (fd < 0)
124 0           die_sys("Can't open timerfd descriptor: %s");
125 1           return io_fdopen(fd, classname, '<');
126             }
127             #define new_timerfd(classname, clock, flags, func) S_new_timerfd(aTHX_ classname, clock, flags, func)
128              
129 13           static int S_interrupted(pTHX_ int value) {
130 13 100         if (value == -1 && errno == EINTR) {
    50          
131 0 0         PERL_ASYNC_CHECK();
132 0           return 1;
133             }
134             return 0;
135             }
136             #define interrupted(value) S_interrupted(aTHX_ value)
137              
138             #define NEVER (struct timespec) { 0 }
139              
140             typedef int Fd;
141              
142             MODULE = Linux::FD PACKAGE = Linux::FD
143              
144             BOOT:
145 3           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("IO::Handle"), NULL);
146 3           av_push(get_av("Linux::FD::Event::ISA" , GV_ADD), newSVpvs("IO::Handle"));
147 3           av_push(get_av("Linux::FD::Signal::ISA", GV_ADD), newSVpvs("IO::Handle"));
148 3           av_push(get_av("Linux::FD::Timer::ISA" , GV_ADD), newSVpvs("IO::Handle"));
149              
150             SV* eventfd(UV initial = 0, ...)
151             PREINIT:
152             int i, flags = EFD_CLOEXEC;
153             CODE:
154 5 100         for (i = 1; i < items; i++)
155 3           flags |= get_event_flag(ST(i));
156 2           RETVAL = new_eventfd("Linux::FD::Event", initial, flags);
157             OUTPUT:
158             RETVAL
159              
160             SV* signalfd(sigset_t* sigmask, ...)
161             PREINIT:
162             int i, flags = SFD_CLOEXEC;
163             CODE:
164 4 100         for (i = 1; i < items; i++)
165 1           flags |= get_signal_flag(ST(i));
166 3           RETVAL = new_signalfd("Linux::FD::Signal", sigmask, flags);
167             OUTPUT:
168             RETVAL
169              
170             SV* timerfd(SV* clock, ...)
171             PREINIT:
172             int i, flags = TFD_CLOEXEC;
173             CODE:
174 2 100         for (i = 1; i < items; i++)
175 1           flags |= get_timer_flag(ST(i));
176 1           RETVAL = new_timerfd("Linux::FD::Timer", clock, flags, "timerfd");
177             OUTPUT:
178             RETVAL
179              
180             MODULE = Linux::FD PACKAGE = Linux::FD::Event
181              
182             SV* new(const char* classname, UV initial = 0, ...)
183             PREINIT:
184             int i, flags = EFD_CLOEXEC;
185             CODE:
186 0 0         for (i = 2; i < items; i++)
187 0           flags |= get_event_flag(ST(i));
188 0           RETVAL = new_eventfd(classname, initial, flags);
189             OUTPUT:
190             RETVAL
191              
192             UV get(Fd eventfd)
193             PREINIT:
194             uint64_t buffer;
195             int ret;
196             CODE:
197             do {
198 6           ret = read(eventfd, &buffer, sizeof buffer);
199 6 50         } while (interrupted(ret));
200 6 100         if (ret == -1) {
201 3 50         if (errno == EAGAIN)
202 3           XSRETURN_EMPTY;
203             else
204 0           die_sys("Couldn't read from eventfd: %s");
205             }
206 3 50         RETVAL = buffer;
207             OUTPUT:
208             RETVAL
209              
210             UV add(Fd eventfd, UV value)
211             PREINIT:
212             uint64_t buffer;
213             int ret;
214             CODE:
215 2           buffer = value;
216             do {
217 2           ret = write(eventfd, &buffer, sizeof buffer);
218 2 50         } while (interrupted(ret));
219 2 50         if (ret == -1) {
220 0 0         if (errno == EAGAIN)
221 0           XSRETURN_EMPTY;
222             else
223 0           die_sys("Couldn't write to eventfd: %s");
224             }
225             RETVAL = value;
226             OUTPUT:
227             RETVAL
228              
229              
230             MODULE = Linux::FD PACKAGE = Linux::FD::Signal
231              
232             SV* new(const char* classname, sigset_t* sigmask, ...)
233             PREINIT:
234             int i, flags = SFD_CLOEXEC;
235             CODE:
236 0 0         for (i = 2; i < items; i++)
237 0           flags |= get_signal_flag(ST(i));
238 0           RETVAL = new_signalfd(classname, sigmask, flags);
239             OUTPUT:
240             RETVAL
241              
242             void set_mask(Fd fd, sigset_t* sigmask)
243             CODE:
244 0 0         if(signalfd(fd, sigmask, 0) == -1)
245 0           die_sys("Couldn't set_mask: %s");
246              
247             struct signalfd_siginfo receive(Fd fd)
248             PREINIT:
249             int tmp;
250             CODE:
251             do {
252 2           tmp = read(fd, &RETVAL, sizeof(RETVAL));
253 2 50         } while (interrupted(tmp));
254 2 100         if (tmp == -1) {
255 1 50         if (errno == EAGAIN)
256 1           XSRETURN_EMPTY;
257             else
258 0           die_sys("Couldn't read from signalfd: %s");
259             }
260             OUTPUT:
261             RETVAL
262              
263              
264             MODULE = Linux::FD PACKAGE = Linux::FD::Timer
265              
266             SV* new(const char* classname, SV* clock, ...)
267             PREINIT:
268             int i, flags = TFD_CLOEXEC;
269             CODE:
270 0 0         for (i = 2; i < items; i++)
271 0           flags |= get_timer_flag(ST(i));
272 0           RETVAL = new_timerfd(classname, clock, flags, "Linux::FD::Timer->new");
273             OUTPUT:
274             RETVAL
275              
276             void get_timeout(Fd timerfd)
277             PREINIT:
278             struct itimerspec value;
279             PPCODE:
280 1 50         if (timerfd_gettime(timerfd, &value) == -1)
281 0           die_sys("Couldn't get_timeout: %s");
282 1 50         mXPUSHn(timespec_to_nv(&value.it_value));
283 1 50         if (GIMME_V == G_ARRAY)
284 1 50         mXPUSHn(timespec_to_nv(&value.it_interval));
285              
286             SV* set_timeout(Fd timerfd, struct timespec new_value, struct timespec new_interval = NEVER, bool abstime = FALSE)
287             PREINIT:
288             struct itimerspec new_itimer, old_itimer;
289             PPCODE:
290 2           new_itimer.it_value = new_value;
291 2           new_itimer.it_interval = new_interval;
292 2 50         if (timerfd_settime(timerfd, (abstime ? TIMER_ABSTIME : 0), &new_itimer, &old_itimer) == -1)
293 0           die_sys("Couldn't set_timeout: %s");
294 2 50         mXPUSHn(timespec_to_nv(&old_itimer.it_value));
295 2 50         if (GIMME_V == G_ARRAY)
296 0 0         mXPUSHn(timespec_to_nv(&old_itimer.it_interval));
297              
298             IV receive(Fd timerfd)
299             PREINIT:
300             uint64_t buffer;
301             int ret;
302             CODE:
303             do {
304 3           ret = read(timerfd, &buffer, sizeof buffer);
305 3 50         } while (interrupted(ret));
306 3 100         if (ret == -1) {
307 1 50         if (errno == EAGAIN)
308 1           XSRETURN_EMPTY;
309             else
310 0           die_sys("Couldn't read from timerfd: %s");
311             }
312 2 50         RETVAL = buffer;
313             OUTPUT:
314             RETVAL
315              
316             void clocks(SV* classname)
317             INIT:
318             int i;
319             PPCODE:
320 6 100         for (i = 0; i < sizeof clocks / sizeof *clocks; ++i)
321 5 50         mXPUSHp(clocks[i].key, clocks[i].length);