File Coverage

blib/lib/Linux/Perl/timerfd.pm
Criterion Covered Total %
statement 73 80 91.2
branch 8 16 50.0
condition 10 14 71.4
subroutine 16 16 100.0
pod 4 4 100.0
total 111 130 85.3


line stmt bran cond sub pod time code
1             package Linux::Perl::timerfd;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Linux::Perl::timerfd
8              
9             =head1 SYNOPSIS
10              
11             my $tfd = Linux::Perl::timerfd->new(
12             clockid => 'REALTIME',
13             flags => [ 'NONBLOCK', 'CLOEXEC' ],
14             );
15              
16             #or, e.g., Linux::Perl::timerfd::x86_64
17              
18             my $fd = $tfd->fileno();
19              
20             ($old_interval, $old_value) = $tfd->settime(
21             interval => $interval_seconds,
22             value => $value_seconds,
23             flags => [ 'ABSTIME', 'CANCEL_ON_SET' ],
24             );
25              
26             my ($interval, $value) = $tfd->gettime();
27              
28             $tfd->set_ticks(12);
29              
30             my $read = $tfd->read();
31              
32             =head1 DESCRIPTION
33              
34             This is an interface to the C family of system calls.
35              
36             This class inherits from L.
37              
38             =cut
39              
40 3     3   181182 use strict;
  3         9  
  3         90  
41 3     3   15 use warnings;
  3         3  
  3         84  
42              
43 3     3   15 use parent 'Linux::Perl::Base::TimerEventFD';
  3         6  
  3         15  
44              
45 3     3   1452 use Call::Context;
  3         792  
  3         99  
46              
47 3     3   1227 use Linux::Perl;
  3         9  
  3         90  
48 3     3   18 use Linux::Perl::Endian;
  3         3  
  3         54  
49 3     3   1107 use Linux::Perl::ParseFlags;
  3         9  
  3         90  
50 3     3   1128 use Linux::Perl::TimeSpec;
  3         18  
  3         120  
51              
52             use constant {
53 3         1920 _clock_REALTIME => 0,
54             _clock_MONOTONIC => 1,
55             _clock_BOOTTIME => 7,
56             _clock_REALTIME_ALARM => 8,
57             _clock_BOOTTIME_ALARM => 9,
58              
59             _ENOTTY => 25, #constant for Linux?
60 3     3   15 };
  3         6  
61              
62             #----------------------------------------------------------------------
63              
64             =head1 METHODS
65              
66             =head2 I->new( %OPTS )
67              
68             %OPTS is:
69              
70             =over
71              
72             =item * C - One of: C, C, C,
73             C, or C. Not all kernel versions support
74             all of these; check C for your system.
75              
76             =item * C - Optional, an array reference of any or all of:
77             C, C.
78              
79             This follows the same practice as L regarding
80             CLOEXEC and C<$^F>.
81              
82             =back
83              
84             =cut
85              
86             sub new {
87 12     12 1 1199710 my ($class, %opts) = @_;
88              
89 12   50     211 my $clockid_str = $opts{'clockid'} || die 'Need “clockid”!';
90 12         303 my $clockid = $class->can("_clock_$clockid_str");
91 12 50       80 if (!$clockid) {
92 0         0 die "Unknown “clockid”: “$clockid_str”!";
93             }
94              
95 12         57 $clockid = $clockid->();
96              
97 12         204 my $flags = Linux::Perl::ParseFlags::parse( $class, $opts{'flags'} );
98              
99 12   66     180 my $arch_module = $class->can('NR_timerfd_create') && $class;
100 12   66     145 $arch_module ||= do {
101 6         1377 require Linux::Perl::ArchLoader;
102 6         53 Linux::Perl::ArchLoader::get_arch_module($class);
103             };
104              
105 12         177 my $fd = Linux::Perl::call( $arch_module->NR_timerfd_create(), 0 + $clockid, $flags );
106              
107             #Force CLOEXEC if the flag was given.
108 12 100       128 local $^F = 0 if $flags & $arch_module->_flag_CLOEXEC();
109              
110 12         427 open my $fh, '+<&=' . $fd;
111              
112 12         299 return bless [$fh], $arch_module;
113             }
114              
115             #----------------------------------------------------------------------
116              
117             =head2 $OBJ = I->settime( %OPTS )
118              
119             =head2 ($old_interval, $old_value) = I->settime( %OPTS )
120              
121             See C for details about what this does.
122              
123             %OPTS is:
124              
125             =over
126              
127             =item * C - in seconds.
128              
129             =item * C - in seconds. Must be falsy if C is falsy.
130             (Rationale: C will ignore C if C
131             is zero. This seems unintuitive, so we avoid that situation
132             altogether.)
133              
134             =item * C - Optional, arrayref. Accepted values are
135             C and C. Your kernel may not support
136             all of these; check C for details.
137              
138             =back
139              
140             In scalar context this returns the object. This facilitates easy
141             setting of the value on instantiation.
142              
143             In list context it returns the previous interval and value.
144              
145             =cut
146              
147             sub settime {
148 6     6 1 91 my ($self, %opts) = @_;
149              
150             my $flags = Linux::Perl::ParseFlags::parse(
151             'Linux::Perl::timerfd::_set_flags',
152 6         36 $opts{'flags'},
153             );
154              
155 6 50       30 if (!$opts{'value'}) {
156 0 0       0 if ($opts{'interval'}) {
157 0         0 die "“interval” is ignored if “value” is 0.";
158             }
159              
160 0         0 $opts{'value'} = 0;
161             }
162              
163 6   100     62 $opts{'interval'} ||= 0;
164              
165 6   100     57 my $int_packed = Linux::Perl::TimeSpec::from_float( $opts{'interval'} || 0 );
166 6   50     36 my $val_packed = Linux::Perl::TimeSpec::from_float( $opts{'value'} || 0 );
167              
168 6         20 my $new_packed = $int_packed . $val_packed;
169 6         25 my $old_packed = ("\0") x length $new_packed;
170              
171 6         65 Linux::Perl::call( $self->NR_timerfd_settime(), 0 + $self->fileno(), 0 + $flags, $new_packed, $old_packed );
172              
173 6 100       57 return wantarray ? _parse_itimerspec($old_packed) : $self;
174             }
175              
176             #----------------------------------------------------------------------
177              
178             =head2 ($old_interval, $old_value) = I->gettime()
179              
180             Returns the old C and C, in seconds.
181              
182             =cut
183              
184             sub gettime {
185 2     2 1 23416 my ($self) = @_;
186              
187 2         15 Call::Context::must_be_list();
188              
189 2         26 my $packed = ( Linux::Perl::TimeSpec::from_float(0) ) x 2;
190              
191 2         24 Linux::Perl::call( $self->NR_timerfd_gettime(), 0 + $self->fileno(), $packed );
192              
193 2         9 return _parse_itimerspec($packed);
194             }
195              
196             #----------------------------------------------------------------------
197              
198             =head2 my $ok_yn = I->set_ticks( $NUM_TICKS )
199              
200             See C (look for C) for details
201             on what this does.
202              
203             This returns truthy if the operation succeeded and falsy if
204             the system does not support this operation. (Any other failure
205             will prompt an exception to be thrown.)
206              
207             =cut
208              
209             # man 2 ioctl_list
210 3     3   24 use constant _TFD_IOC_SET_TICKS => 0x40085400;
  3         6  
  3         996  
211              
212             sub set_ticks {
213 2     2 1 130 my ($self, $num_ticks) = @_;
214              
215 2         21 my $buf = "\0" x 8;
216              
217 2 50       81 if ($self->_PERL_CAN_64BIT()) {
218 2         40 $buf = pack 'Q', $num_ticks;
219             }
220             elsif (Linux::Perl::Endian::SYSTEM_IS_BIG_ENDIAN) {
221             $buf = ("\0" x 4) . pack('N', $num_ticks);
222             }
223             else {
224 0         0 $buf = pack('V', $num_ticks) . ("\0" x 4);
225             }
226              
227 2         32 local $!;
228 2 50       80 return 1 if ioctl( $self->[0], _TFD_IOC_SET_TICKS(), $buf );
229              
230 0 0       0 return !1 if $! == _ENOTTY(); #falsy
231              
232 0         0 die "ioctl($self->[0][0], TFD_IOC_SET_TICKS): $!";
233             }
234              
235             #----------------------------------------------------------------------
236              
237             =head2 $expirations = I->read()
238              
239             See C for details on what this returns.
240             Sets C<$!> and returns undef on error.
241              
242             =cut
243              
244             *read = __PACKAGE__->can('_read');
245              
246             #----------------------------------------------------------------------
247              
248             sub _parse_itimerspec {
249 4     4   13 my ($packed) = @_;
250              
251 4         10 my $tslen = length($packed) / 2;
252 4         69 my ($int, $val) = unpack "a${tslen}a${tslen}", $packed;
253 4         20 $_ = Linux::Perl::TimeSpec::to_float($_) for ($int, $val);
254              
255 4         39 return ($int, $val);
256             }
257              
258             #----------------------------------------------------------------------
259              
260             package Linux::Perl::timerfd::_set_flags;
261              
262             use constant {
263 3         234 _flag_ABSTIME => 1,
264             _flag_CANCEL_ON_SET => 2,
265 3     3   21 };
  3         6  
266              
267             1;