File Coverage

blib/lib/Linux/Perl/eventfd.pm
Criterion Covered Total %
statement 55 56 98.2
branch 11 12 91.6
condition 10 13 76.9
subroutine 12 12 100.0
pod 4 4 100.0
total 92 97 94.8


line stmt bran cond sub pod time code
1             package Linux::Perl::eventfd;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Linux::Perl::eventfd
8              
9             =head1 SYNOPSIS
10              
11             my $efd = Linux::Perl::eventfd->new(
12             initval => 4,
13             flags => [ 'NONBLOCK', 'CLOEXEC' ], #only on 2.6.27+
14             );
15              
16             #or, e.g., Linux::Perl::eventfd::x86_64
17              
18             my $fd = $efd->fileno();
19              
20             $efd->add(12);
21              
22             my $read = $efd->read();
23              
24             =head1 DESCRIPTION
25              
26             This is an interface to the C/C system call.
27             (C is only called if the given parameters require it.)
28              
29             =cut
30              
31 5     5   125853 use strict;
  5         11  
  5         127  
32 5     5   22 use warnings;
  5         5  
  5         134  
33              
34 5     5   17 use Module::Load;
  5         11  
  5         39  
35              
36 5     5   1217 use Linux::Perl;
  5         10  
  5         109  
37 5     5   24 use Linux::Perl::Constants;
  5         6  
  5         78  
38 5     5   1699 use Linux::Perl::Constants::Fcntl;
  5         11  
  5         105  
39 5     5   1050 use Linux::Perl::Endian;
  5         10  
  5         202  
40              
41             use constant {
42 5         9 PERL_CAN_64BIT => !!do { local $@; eval { pack 'Q', 1 } },
  5         6  
  5         7  
  5         2368  
43 5     5   38 };
  5         19  
44              
45             *flag_CLOEXEC = *Linux::Perl::Constants::Fcntl::flag_CLOEXEC;
46             *flag_NONBLOCK = *Linux::Perl::Constants::Fcntl::flag_NONBLOCK;
47              
48             =head1 METHODS
49              
50             =head2 I->new( %OPTS )
51              
52             %OPTS is:
53              
54             =over
55              
56             =item * C - Optional, as described in the eventfd documentation.
57             Defaults to 0.
58              
59             =item * C - Optional, an array reference of any or all of:
60             C, C, C. See C for
61             more details.
62              
63             Note that, in conformity with Perl convention, this module honors
64             the $^F variable, which in its default configuration causes CLOEXEC
65             even if the flag is not given. To have a non-CLOEXEC eventfd instance,
66             then, set $^F to a high enough value that the eventfd file descriptor
67             will not be an “OS” filehandle, e.g.:
68              
69             my $eventfd = do {
70             local $^F = 1000;
71             Linux::Perl::eventfd->new();
72             };
73              
74             =back
75              
76             =cut
77              
78             sub new {
79 10     10 1 295312 my ($class, %opts) = @_;
80              
81 10         176 local ($!, $^E);
82              
83 10   66     155 my $arch_module = $class->can('NR_eventfd') && $class;
84 10   66     70 $arch_module ||= do {
85 6         1397 require Linux::Perl::ArchLoader;
86 6         29 Linux::Perl::ArchLoader::get_arch_module($class);
87             };
88              
89 10   100     63 my $initval = 0 + ( $opts{'initval'} || 0 );
90              
91 10         28 my $is_cloexec;
92              
93 10         20 my $flags = 0;
94 10 100       27 if ( $opts{'flags'} ) {
95 4         20 for my $fl ( @{ $opts{'flags'} } ) {
  4         35  
96 4 50       101 my $val_cr = $arch_module->can("flag_$fl") or do {
97 0         0 die "unknown flag: “$fl”";
98             };
99 4         27 $flags |= $val_cr->();
100              
101 4 100       24 $is_cloexec = 1 if $fl eq 'CLOEXEC';
102             }
103             }
104              
105 10 100       35 my $call = 'NR_' . ($flags ? 'eventfd2' : 'eventfd');
106              
107 10   100     189 my $fd = Linux::Perl::call( 0 + $arch_module->$call(), $initval, $flags || () );
108              
109             #Force CLOEXEC if the flag was given.
110 10 100       33 local $^F = 0 if $is_cloexec;
111              
112 10         239 open my $fh, '+<&=' . $fd;
113              
114 10         123 return bless [$fh], $arch_module;
115             }
116              
117             =head2 I->fileno()
118              
119             Returns the file descriptor number.
120              
121             =cut
122              
123 10     10 1 159 sub fileno { fileno $_[0][0] }
124              
125             =head2 $val = I->read()
126              
127             Reads a value from the eventfd instance. Sets C<$!> and returns undef
128             on error.
129              
130             =cut
131              
132             my ($big, $low);
133              
134             sub read {
135 8 100   8 1 1759 return undef if !sysread $_[0][0], my $buf, 8;
136              
137 6         13 if (PERL_CAN_64BIT) {
138 6         21 ($big, $low) = (0, unpack('Q', $buf));
139             }
140             else {
141             if (Linux::Perl::Endian::SYSTEM_IS_BIG_ENDIAN) {
142             ($big, $low) = unpack 'NN', $buf;
143             }
144             else {
145             ($low, $big) = unpack 'VV', $buf;
146             }
147              
148             #TODO: Need to test what happens on a 32-bit Perl.
149             die "No 64-bit support! (high=$big, low=$low)" if $big;
150             }
151              
152 6         41 return $low;
153             }
154              
155             =head2 I->add( NUMBER )
156              
157             Adds NUMBER to the counter.
158              
159             =cut
160              
161             my $packed;
162              
163             sub add {
164 8     8 1 4209 if (PERL_CAN_64BIT) {
165 8         25 $packed = pack 'Q', $_[1];
166             }
167             elsif (Linux::Perl::Endian::SYSTEM_IS_BIG_ENDIAN) {
168             $packed = pack 'x4N', $_[1];
169             }
170             else {
171             $packed = pack 'Vx4', $_[1];
172             }
173              
174 8   50     58 return syswrite( $_[0][0], $packed ) && 1;
175             }
176              
177             1;