File Coverage

lib/POSIX/RT/Timer.xs
Criterion Covered Total %
statement 69 98 70.4
branch 46 94 48.9
condition n/a
subroutine n/a
pod n/a
total 115 192 59.9


line stmt bran cond sub pod time code
1             /*
2             * This software is copyright (c) 2010 by Leon Timmermans .
3             *
4             * This is free software; you can redistribute it and/or modify it under
5             * the same terms as perl itself.
6             *
7             */
8              
9             #define PERL_NO_GET_CONTEXT
10             #define PERL_REENTR_API 1
11              
12             #include "EXTERN.h"
13             #include "perl.h"
14             #include "XSUB.h"
15             #define NEED_mg_findext
16             #include "ppport.h"
17              
18             #include
19             #include
20              
21             #define die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
22              
23             typedef struct { const char* key; STRLEN key_length; clockid_t value; } map[];
24              
25             static map clocks = {
26             { STR_WITH_LEN("realtime") , CLOCK_REALTIME }
27             #ifdef CLOCK_MONOTONIC
28             , { STR_WITH_LEN("monotonic"), CLOCK_MONOTONIC }
29             #endif
30             #ifdef CLOCK_PROCESS_CPUTIME_ID
31             , { STR_WITH_LEN("process"), CLOCK_PROCESS_CPUTIME_ID }
32             #endif
33             #ifdef CLOCK_THREAD_CPUTIME_ID
34             , { STR_WITH_LEN("thread"), CLOCK_THREAD_CPUTIME_ID }
35             #endif
36             #ifdef CLOCK_BOOTTIME
37             , { STR_WITH_LEN("boottime"), CLOCK_BOOTTIME }
38             #endif
39              
40             #ifdef CLOCK_REALTIME_COARSE
41             , { STR_WITH_LEN("realtime_coarse"), CLOCK_REALTIME_COARSE }
42             #endif
43             #ifdef CLOCK_REALTIME_ALARM
44             , { STR_WITH_LEN("realtime_alarm"), CLOCK_REALTIME_ALARM }
45             #endif
46             #ifdef CLOCK_REALTIME_PRECISE
47             , { STR_WITH_LEN("realtime_precise"), CLOCK_REALTIME_PRECISE }
48             #endif
49             #if defined CLOCK_HIGHRES
50             , { STR_WITH_LEN("highres"), CLOCK_HIGHRES }
51             #endif
52             #ifdef CLOCK_MONOTONIC_RAW
53             , { STR_WITH_LEN("monotonic_raw"), CLOCK_MONOTONIC_RAW }
54             #endif
55             #ifdef CLOCK_MONOTONIC_COARSE
56             , { STR_WITH_LEN("monotonic_coarse"), CLOCK_MONOTONIC_COARSE }
57             #endif
58             #ifdef CLOCK_MONOTONIC_PRECISE
59             , { STR_WITH_LEN("monotonic_precise"), CLOCK_MONOTONIC_PRECISE }
60             #endif
61             #if defined CLOCK_PROF
62             , { STR_WITH_LEN("prof"), CLOCK_PROF }
63             #endif
64             #ifdef CLOCK_UPTIME
65             , { STR_WITH_LEN("uptime"), CLOCK_UPTIME }
66             #endif
67             #ifdef CLOCK_UPTIME_PRECISE
68             , { STR_WITH_LEN("uptime_precise"), CLOCK_UPTIME_PRECISE }
69             #endif
70             #ifdef CLOCK_UPTIME_FAST
71             , { STR_WITH_LEN("uptime_fast"), CLOCK_UPTIME_FAST }
72             #endif
73             #ifdef CLOCK_BOOTTIME_ALARM
74             , { STR_WITH_LEN("boottime_alarm"), CLOCK_BOOTTIME_ALARM }
75             #endif
76             #ifdef CLOCK_VIRTUAL
77             , { STR_WITH_LEN("virtual"), CLOCK_VIRTUAL }
78             #endif
79             #ifdef CLOCK_TAI
80             , { STR_WITH_LEN("tai"), CLOCK_TAI }
81             #endif
82             };
83              
84 4           static clockid_t S_get_clockid(pTHX_ SV* clock_name) {
85             int i;
86             STRLEN length;
87 4           const char* clock_ptr = SvPV(clock_name, length);
88 5 50         for (i = 0; i < sizeof clocks / sizeof *clocks; ++i) {
89 5 100         if (clocks[i].key_length == length && strEQ(clock_ptr, clocks[i].key))
    50          
90 4           return clocks[i].value;
91             }
92 0           Perl_croak(aTHX_ "No such timer '%s' known", SvPV_nolen(clock_name));
93             }
94             #define get_clockid(name) S_get_clockid(aTHX_ name)
95              
96             #define NANO_SECONDS 1000000000
97              
98             static NV timespec_to_nv(struct timespec* time) {
99 1           return time->tv_sec + time->tv_nsec / (double)NANO_SECONDS;
100             }
101              
102 5           static void nv_to_timespec(NV input, struct timespec* output) {
103 5           output->tv_sec = (time_t) floor(input);
104 5           output->tv_nsec = (long) ((input - output->tv_sec) * NANO_SECONDS);
105 5           }
106              
107             #if defined(SIGEV_THREAD_ID) && defined(SYS_gettid)
108             #include
109             #define gettid() syscall(SYS_gettid)
110             #ifndef sigev_notify_thread_id
111             #define sigev_notify_thread_id _sigev_un._tid
112             #endif
113             #endif
114              
115             #if defined(_POSIX_CLOCK_SELECTION) && _POSIX_CLOCK_SELECTION >= 0
116 5           static int my_clock_nanosleep(pTHX_ clockid_t clockid, int flags, const struct timespec* request, struct timespec* remain) {
117             int ret;
118 5           ret = clock_nanosleep(clockid, flags, request, remain);
119 5 100         if (ret != 0) {
120 2           errno = ret;
121 2 50         if (ret != EINTR)
122 0           die_sys("Could not sleep: %s");
123             }
124 5           return ret;
125             }
126             #endif
127              
128             #define clock_nanosleep(clockid, flags, request, remain) my_clock_nanosleep(aTHX_ clockid, flags, request, remain)
129              
130             #if defined(USE_ITHREADS) && defined(_POSIX_THREAD_CPUTIME) && _POSIX_THREAD_CPUTIME >= 0
131             static pthread_t* S_get_pthread(pTHX_ SV* thread_handle) {
132             SV* tmp;
133             pthread_t* ret;
134             dSP;
135             ENTER;
136             SAVETMPS;
137             PUSHMARK(SP);
138             PUSHs(thread_handle);
139             PUTBACK;
140             call_method("_handle", G_SCALAR);
141             SPAGAIN;
142             tmp = POPs;
143             ret = INT2PTR(pthread_t* ,SvUV(tmp));
144             FREETMPS;
145             LEAVE;
146             return ret;
147             }
148             #define get_pthread(handle) S_get_pthread(aTHX_ handle)
149             #endif
150              
151             #define undef &PL_sv_undef
152              
153             typedef struct _timer_init {
154             clockid_t clockid;
155             IV signo;
156             IV ident;
157             struct itimerspec itimer;
158             int flags;
159             } timer_init;
160              
161 4           static void S_timer_init_gather(pTHX_ timer_init* result, SV** begin, size_t items) {
162             int i;
163             Zero(result, 1, timer_init);
164             result->clockid = CLOCK_REALTIME;
165 15 100         for(i = 0; i < items; i += 2) {
166             const char* current;
167             STRLEN curlen;
168 11           SV *key = begin[i], *value = begin[i+1];
169 11           current = SvPV(key, curlen);
170 11 100         if (curlen == 5) {
171 6 100         if (strEQ(current, "clock"))
172 1 50         result->clockid = SvROK(value) ? SvUV(SvRV(value)) : get_clockid(value);
173 5 100         else if (strEQ(current, "value"))
174 4           nv_to_timespec(SvNV(value), &result->itimer.it_value);
175 1 50         else if (strEQ(current, "ident"))
176 1           result->ident = SvIV(value);
177             else
178 0           goto fail;
179             }
180 5 100         else if (curlen == 6 && strEQ(current, "signal"))
    50          
181 4 50         result->signo = (SvIOK(value) || looks_like_number(value)) ? SvIV(value) : whichsig(SvPV_nolen(value));
    0          
182 1 50         else if (curlen == 8) {
183 1 50         if (strEQ(current, "interval"))
184 1           nv_to_timespec(SvNV(value), &result->itimer.it_interval);
185 0 0         else if (strEQ(current, "absolute"))
186 0           result->flags |= TIMER_ABSTIME;
187             else
188 0           goto fail;
189             }
190             else
191 0           fail: Perl_croak(aTHX_ "Unknown option '%s'", current);
192             }
193 4           }
194             #define timer_init_gather(init, begin, items) S_timer_init_gather(aTHX_ init, begin, items)
195              
196 4           static timer_t S_timer_new(pTHX_ timer_init* para) {
197             timer_t timer;
198 4           struct sigevent event = { 0 };
199              
200 4 50         if (para->signo < 0)
201 0           Perl_croak(aTHX_ "No valid signal was given");
202              
203             #ifdef gettid
204             event.sigev_notify = SIGEV_THREAD_ID;
205             event.sigev_notify_thread_id = gettid();
206             #else
207             event.sigev_notify = SIGEV_SIGNAL;
208             #endif
209 4           event.sigev_signo = para->signo;
210 4           event.sigev_value.sival_int = para->ident;
211              
212 4 50         if (timer_create(para->clockid, &event, &timer) < 0)
213 0           die_sys("Couldn't create timer: %s");
214 4 50         if (timer_settime(timer, para->flags, ¶->itimer, NULL) < 0)
215 0           die_sys("Couldn't set_time: %s");
216              
217 4           return timer;
218             }
219             #define timer_new(para) S_timer_new(aTHX_ para)
220              
221 1           void timespec_add(struct timespec* left, const struct timespec* right) {
222 1           left->tv_sec += right->tv_sec;
223 1           left->tv_nsec += right->tv_nsec;
224 2 100         while (left->tv_nsec > 1000000000) {
225 1           left->tv_nsec -= 1000000000;
226 1           left->tv_sec++;
227             }
228 1           }
229              
230             static const struct timespec no_time = { 0, 0 };
231              
232             typedef timer_t POSIX__RT__Timer;
233             typedef clockid_t POSIX__RT__Clock;
234              
235             #define XS_unpack_clockid_t(sv) get_clockid(sv)
236              
237             MODULE = POSIX::RT::Timer PACKAGE = POSIX::RT::Timer
238              
239             PROTOTYPES: DISABLED
240              
241             POSIX::RT::Timer new(SV* class, timer_init args, ...)
242             CODE:
243 3           RETVAL = timer_new(&args);
244             OUTPUT:
245             RETVAL
246              
247             UV handle(POSIX::RT::Timer timer)
248             CODE:
249             RETVAL = (UV)timer;
250             OUTPUT:
251             RETVAL
252              
253             void get_timeout(POSIX::RT::Timer timer)
254             PREINIT:
255             struct itimerspec value;
256             PPCODE:
257 0 0         if (timer_gettime(timer, &value) == -1)
258 0           die_sys("Couldn't get_time: %s");
259 0 0         mXPUSHn(timespec_to_nv(&value.it_value));
260 0 0         if (GIMME_V == G_ARRAY)
261 0 0         mXPUSHn(timespec_to_nv(&value.it_interval));
262              
263             void set_timeout(POSIX::RT::Timer timer, struct timespec new_value, struct timespec new_interval = no_time, bool abstime = FALSE)
264             PREINIT:
265             struct itimerspec old_itimer;
266             PPCODE:
267 1           struct itimerspec new_itimer = { new_value, new_interval };
268 1 50         if (timer_settime(timer, (abstime ? TIMER_ABSTIME : 0), &new_itimer, &old_itimer) == -1)
269 0           die_sys("Couldn't set_time: %s");
270 1 50         mXPUSHn(timespec_to_nv(&old_itimer.it_value));
271 1 50         if (GIMME_V == G_ARRAY)
272 0 0         mXPUSHn(timespec_to_nv(&old_itimer.it_interval));
273              
274             IV get_overrun(POSIX::RT::Timer timer)
275             CODE:
276 0           RETVAL = timer_getoverrun(timer);
277 0 0         if (RETVAL == -1)
278 0           die_sys("Couldn't get_overrun: %s");
279             OUTPUT:
280             RETVAL
281              
282             void DESTROY(POSIX::RT::Timer timer)
283             CODE:
284 4           timer_delete(timer);
285              
286             MODULE = POSIX::RT::Timer PACKAGE = POSIX::RT::Clock
287              
288             PROTOTYPES: DISABLED
289              
290             POSIX::RT::Clock new(SV* class, clockid_t clockid = CLOCK_REALTIME)
291             CODE:
292             RETVAL = clockid;
293             OUTPUT:
294             RETVAL
295              
296             UV handle(POSIX::RT::Clock clock)
297             CODE:
298 0 0         RETVAL = (UV)clock;
299             OUTPUT:
300             RETVAL
301              
302             #if defined(_POSIX_CPUTIME) && _POSIX_CPUTIME >= 0
303             POSIX::RT::Clock get_cpuclock(SV* class, SV* pid = undef)
304             CODE:
305 1 50         if (SvOK(pid) && SvROK(pid) && sv_derived_from(pid, "threads")) {
    50          
    0          
306             #if defined(USE_ITHREADS) && defined(_POSIX_THREAD_CPUTIME) && _POSIX_THREAD_CPUTIME >= 0
307             pthread_t* handle = get_pthread(pid);
308             if (pthread_getcpuclockid(*handle, &RETVAL) != 0)
309             die_sys("Could not get cpuclock: %s");
310             #else
311 0           Perl_croak(aTHX_ "Can't get CPU time for threads");
312             #endif
313             }
314             else {
315 1 50         if (clock_getcpuclockid(SvOK(pid) ? SvIV(pid) : 0, &RETVAL) != 0)
    50          
316 0           die_sys("Could not get cpuclock: %s");
317             }
318             OUTPUT:
319             RETVAL
320              
321             #endif
322              
323             void get_clocks(...)
324             PREINIT:
325             size_t i;
326             const size_t max = sizeof clocks / sizeof *clocks;
327             PPCODE:
328 12 100         for (i = 0; i < max; ++i)
329 11 50         mXPUSHp(clocks[i].key, clocks[i].key_length);
330 1           PUTBACK;
331              
332             struct timespec get_time(POSIX::RT::Clock clockid)
333             CODE:
334 8 50         if (clock_gettime(clockid, &RETVAL) == -1)
335 0           die_sys("Couldn't get time: %s");
336             OUTPUT:
337             RETVAL
338              
339             void set_time(POSIX::RT::Clock clockid, struct timespec time)
340             CODE:
341 0 0         if (clock_settime(clockid, &time) == -1)
342 0           die_sys("Couldn't set time: %s");
343              
344             struct timespec get_resolution(POSIX::RT::Clock clockid)
345             CODE:
346 1 50         if (clock_getres(clockid, &RETVAL) == -1)
347 0           die_sys("Couldn't get resolution: %s");
348             OUTPUT:
349             RETVAL
350              
351             POSIX::RT::Timer timer(POSIX::RT::Clock clockid, timer_init args, ...)
352             CODE:
353 1           args.clockid = clockid;
354 1           RETVAL = timer_new(&args);
355             OUTPUT:
356             RETVAL
357              
358             #if defined(_POSIX_CLOCK_SELECTION) && _POSIX_CLOCK_SELECTION >= 0
359             struct timespec sleep(POSIX::RT::Clock clockid, struct timespec time, bool abstime = FALSE)
360             PREINIT:
361             struct timespec remain_time;
362             int flags;
363             CODE:
364 3           flags = abstime ? TIMER_ABSTIME : 0;
365              
366 3 100         if (clock_nanosleep(clockid, flags, &time, &remain_time) == EINTR)
367 1 50         RETVAL = abstime ? time : remain_time;
368             else
369             RETVAL = no_time;
370             OUTPUT:
371             RETVAL
372              
373             NV sleep_deeply(POSIX::RT::Clock clockid, struct timespec time, bool abstime = FALSE)
374             PREINIT:
375             CODE:
376 0 0         if (!abstime) {
377             struct timespec current_time;
378 1 50         if (clock_gettime(clockid, ¤t_time) == -1)
379 0           die_sys("Couldn't get time: %s");
380 1           timespec_add(&time, ¤t_time);
381             }
382 2 100         while (clock_nanosleep(clockid, TIMER_ABSTIME, &time, NULL) == EINTR);
383             RETVAL = 0;
384             OUTPUT:
385             RETVAL
386              
387             #endif