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   174549 use strict;
  3         9  
  3         75  
41 3     3   15 use warnings;
  3         6  
  3         87  
42              
43 3     3   12 use parent 'Linux::Perl::Base::TimerEventFD';
  3         6  
  3         21  
44              
45 3     3   1140 use Call::Context;
  3         687  
  3         72  
46              
47 3     3   1353 use Linux::Perl;
  3         6  
  3         72  
48 3     3   18 use Linux::Perl::Endian;
  3         6  
  3         48  
49 3     3   957 use Linux::Perl::ParseFlags;
  3         6  
  3         81  
50 3     3   975 use Linux::Perl::TimeSpec;
  3         6  
  3         93  
51              
52             use constant {
53 3         1827 _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         3  
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 1164536 my ($class, %opts) = @_;
88              
89 12   50     93 my $clockid_str = $opts{'clockid'} || die 'Need “clockid”!';
90 12         192 my $clockid = $class->can("_clock_$clockid_str");
91 12 50       62 if (!$clockid) {
92 0         0 die "Unknown “clockid”: “$clockid_str”!";
93             }
94              
95 12         38 $clockid = $clockid->();
96              
97 12         128 my $flags = Linux::Perl::ParseFlags::parse( $class, $opts{'flags'} );
98              
99 12   66     166 my $arch_module = $class->can('NR_timerfd_create') && $class;
100 12   66     91 $arch_module ||= do {
101 6         1173 require Linux::Perl::ArchLoader;
102 6         41 Linux::Perl::ArchLoader::get_arch_module($class);
103             };
104              
105 12         424 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       105 local $^F = 0 if $flags & $arch_module->_flag_CLOEXEC();
109              
110 12         334 open my $fh, '+<&=' . $fd;
111              
112 12         168 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 57 my ($self, %opts) = @_;
149              
150             my $flags = Linux::Perl::ParseFlags::parse(
151             'Linux::Perl::timerfd::_set_flags',
152 6         27 $opts{'flags'},
153             );
154              
155 6 50       23 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     45 $opts{'interval'} ||= 0;
164              
165 6   100     46 my $int_packed = Linux::Perl::TimeSpec::from_float( $opts{'interval'} || 0 );
166 6   50     21 my $val_packed = Linux::Perl::TimeSpec::from_float( $opts{'value'} || 0 );
167              
168 6         17 my $new_packed = $int_packed . $val_packed;
169 6         15 my $old_packed = ("\0") x length $new_packed;
170              
171 6         50 Linux::Perl::call( $self->NR_timerfd_settime(), 0 + $self->fileno(), 0 + $flags, $new_packed, $old_packed );
172              
173 6 100       33 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 20366 my ($self) = @_;
186              
187 2         11 Call::Context::must_be_list();
188              
189 2         26 my $packed = ( Linux::Perl::TimeSpec::from_float(0) ) x 2;
190              
191 2         23 Linux::Perl::call( $self->NR_timerfd_gettime(), 0 + $self->fileno(), $packed );
192              
193 2         10 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   21 use constant _TFD_IOC_SET_TICKS => 0x40085400;
  3         6  
  3         873  
211              
212             sub set_ticks {
213 2     2 1 74 my ($self, $num_ticks) = @_;
214              
215 2         14 my $buf = "\0" x 8;
216              
217 2 50       59 if ($self->_PERL_CAN_64BIT()) {
218 2         17 $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         28 local $!;
228 2 50       50 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   10 my ($packed) = @_;
250              
251 4         14 my $tslen = length($packed) / 2;
252 4         65 my ($int, $val) = unpack "a${tslen}a${tslen}", $packed;
253 4         23 $_ = Linux::Perl::TimeSpec::to_float($_) for ($int, $val);
254              
255 4         34 return ($int, $val);
256             }
257              
258             #----------------------------------------------------------------------
259              
260             package Linux::Perl::timerfd::_set_flags;
261              
262             use constant {
263 3         189 _flag_ABSTIME => 1,
264             _flag_CANCEL_ON_SET => 2,
265 3     3   21 };
  3         3  
266              
267             1;