File Coverage

blib/lib/Linux/Perl/aio.pm
Criterion Covered Total %
statement 81 92 88.0
branch 15 24 62.5
condition 8 10 80.0
subroutine 15 15 100.0
pod 4 4 100.0
total 123 145 84.8


line stmt bran cond sub pod time code
1             package Linux::Perl::aio;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Linux:Perl::aio - asynchronous I/O
8              
9             =head1 SYNOPSIS
10              
11             #Platform-specific invocation uses e.g.:
12             # Linux::Perl::aio::x86_64->new(...)
13             # Linux::Perl::aio::Control::x86_64->new(...)
14              
15             my $aio = Linux::Perl::aio->new(16);
16              
17             my $ctrl = Linux::Perl::aio::Control->new(
18             $filehandle,
19             \$buffer,
20             lio_opcode => 'PREAD',
21             );
22              
23             #Multiple $ctrl objects can be submitted in a list.
24             $aio->submit($ctrl);
25              
26             my @events = $aio->getevents( $min, $max, $timeout );
27              
28             =head1 DESCRIPTION
29              
30             This module provides support for the kernel-level AIO interface.
31              
32             DESTROY handlers are provided for automatic reaping of unused
33             instances/contexts.
34              
35             This module is EXPERIMENTAL. For now only the C architecture
36             is supported; others may follow, though 32-bit architectures would
37             take a bit more work.
38              
39             =cut
40              
41 2     2   3813 use strict;
  2         14  
  2         83  
42 2     2   14 use warnings;
  2         4  
  2         114  
43              
44 2     2   962 use Linux::Perl ();
  2         5  
  2         31  
45 2     2   720 use Linux::Perl::TimeSpec ();
  2         5  
  2         35  
46              
47 2     2   9 use constant io_event_sizeof => 32; #4 x uint64_t
  2         4  
  2         1025  
48              
49             =head1 METHODS
50              
51             =head2 I->new( NR_EVENTS )
52              
53             Calls C with the referred number of events to create
54             an AIO context. An object of CLASS is returned.
55              
56             =cut
57              
58             sub new {
59 6     6 1 5211 my ( $class, $nr_events ) = @_;
60              
61 6 50       23 die "Need number of events!" if !$nr_events;
62              
63 6 100       45 if (!$class->can('NR_io_setup')) {
64 3         486 require Linux::Perl::ArchLoader;
65 3         13 $class = Linux::Perl::ArchLoader::get_arch_module($class);
66             }
67              
68 6         52 my $context = pack $class->_context_template();
69              
70 6         34 Linux::Perl::call( $class->NR_io_setup(), 0 + $nr_events, $context );
71              
72 6         33 $context = $class->unpack_context($context);
73              
74 6         18 return bless \$context, $class;
75             }
76              
77             =head2 I->create_control( FILEHANDLE, BUFFER_SR, %OPTS )
78              
79             Returns an instance of the relevant L
80             subclass for your architecture.
81              
82             FILEHANDLE is a Perl filehandle object, and BUFFER_SR is a reference
83             to the buffer string. This buffer must be pre-initialized to at least
84             the needed/desired length.
85              
86             %OPTS is:
87              
88             =over
89              
90             =item * C: Required, one of: C, C, C,
91             C, C, C, C.
92              
93             =item * C: The byte offset in BUFFER_SR at which to start
94             the I/O operation. Defaults to 0.
95              
96             =item * C: The number of bytes on which to operate. This value
97             plus C must be less than the length of BUFFER_SR. Defaults
98             to length(BUFFER_SR) minus C.
99              
100             =item * C: Optional, an array reference of any or all of: C,
101             C, C, C, C. Not supported in all kernel versions;
102             in fact, support seems more the exception than the rule!
103             See the kernel documentation (e.g., C) for details on
104             what these flags mean and whether your system supports them.
105              
106             =item * C: Optional. See the kernel’s documentation.
107              
108             =item * C: Optional, an eventfd file descriptor
109             (i.e., unsigned integer) to receive updates when aio events are finished.
110             (See L for one way of making this work.)
111              
112             =back
113              
114             For more information, consult the definition and documentation
115             for struct C. (cf. F)
116              
117             =cut
118              
119             sub create_control {
120 10     10 1 6675 my $self = shift;
121 10         25 my $class = ref $self;
122              
123 10         19 my $rcolon = rindex($class, ':');
124              
125 10         29 substr($class, $rcolon - 1, 0) = '::Control';
126              
127 10         66 return $class->new(@_);
128             }
129              
130             =head2 $num = I->submit( CTRL1, CTRL2, .. )
131              
132             Calls C. Each CTRL* is an instance of
133             L and represets an I/O request.
134              
135             The return value is the number of control objects submitted.
136              
137             =cut
138              
139              
140             sub submit {
141 8     8 1 49 my ( $self, @control_objs ) = @_;
142              
143 8         18 my $ptrs = join( q<>, map { $_->pointer() } @control_objs );
  10         24  
144              
145 8         46 return Linux::Perl::call( $self->NR_io_submit(), 0 + $$self, 0 + @control_objs, $ptrs );
146             }
147              
148             =head2 @events = I->getevents( MIN, MAX, TIMEOUT )
149              
150             Calls C with the relevant minimum, maximum, and timeout
151             values. (TIMEOUT can be a float.)
152              
153             If more than one event is requested (i.e., MAX > 1), then list
154             context is required.
155              
156             The return is a list of hash references; each hash reference has the following
157             values as in the kernel C struct:
158              
159             =over
160              
161             =item * C
162              
163             =item * C (corresponds to the Control instance C)
164              
165             =item * C
166              
167             =item * C
168              
169             =back
170              
171             =cut
172              
173             sub getevents {
174 8     8 1 4972 my ( $self, $min_events, $max_events, $timeout ) = @_;
175              
176             #If they only asked for one, then allow scalar context.
177 8 100       28 if ($max_events > 1) {
178 2         810 require Call::Context;
179 2         432 Call::Context::must_be_list();
180             }
181              
182 8 50       34 if (!$max_events) {
183 0         0 die '$max_events must be >0!';
184             }
185              
186 8         24 my $buf = "\0" x ( $max_events * io_event_sizeof() );
187              
188 8         58 my $evts = Linux::Perl::call(
189             $self->NR_io_getevents(),
190             $$self,
191             0 + $min_events,
192             0 + $max_events,
193             $buf,
194             Linux::Perl::TimeSpec::from_float($timeout),
195             );
196              
197 8         15 my @events;
198 8         24 for my $idx ( 0 .. ( $evts - 1 ) ) {
199 10         38 my @data = unpack $self->io_event_pack(), substr( $buf, $idx * io_event_sizeof(), io_event_sizeof() );
200 10         17 my %event;
201 10         28 @event{ $self->io_event_keys() } = @data;
202 10         31 push @events, \%event;
203             }
204              
205 8 50       38 return wantarray ? @events : $events[0];
206             }
207              
208             sub DESTROY {
209 6     6   4243 my ($self) = @_;
210              
211 6         50 Linux::Perl::call( $self->NR_io_destroy(), 0 + $$self);
212              
213 6         125 return;
214             }
215              
216             #----------------------------------------------------------------------
217              
218             package Linux::Perl::aio::Control;
219              
220             =encoding utf-8
221              
222             =head1 NAME
223              
224             Linux::Perl::aio::Control
225              
226             =head1 SYNOPSIS
227              
228             my $ctrl = Linux::Perl::aio::Control->new(
229             $filehandle,
230             \$buffer,
231             lio_opcode => 'PREAD',
232             buffer_offset => 4,
233             nbytes => 2,
234             );
235              
236             =head1 DESCRIPTION
237              
238             This class encapsulates a kernel C struct, i.e., an I/O request.
239              
240             You should not instantiate it directly; instead, use
241             L’s C method.
242              
243             =cut
244              
245             use constant {
246 2         1304 _RWF_HIPRI => 1,
247             _RWF_DSYNC => 2,
248             _RWF_SYNC => 4,
249             _RWF_NOWAIT => 8,
250             _RWF_APPEND => 16,
251              
252             _IOCB_CMD_PREAD => 0,
253             _IOCB_CMD_PWRITE => 1,
254             _IOCB_CMD_FSYNC => 2,
255             _IOCB_CMD_FDSYNC => 3,
256              
257             #experimental
258             #_IOCB_CMD_PREADX => 4,
259             #_IOCB_CMD_POLL => 5,
260              
261             _IOCB_CMD_NOOP => 6,
262             _IOCB_CMD_PREADV => 7,
263             _IOCB_CMD_PWRITEV => 8,
264              
265             _IOCB_FLAG_RESFD => 1,
266 2     2   19 };
  2         3  
267              
268             =head1 METHODS
269              
270             =head2 I->new( FILEHANDLE, BUFFER_SR, %OPTS )
271              
272             =cut
273              
274             sub new {
275 10     10   61 my ( $class, $fh, $buf_sr, %args ) = @_;
276              
277 10 50       39 my $opcode = $args{'lio_opcode'} or do {
278 0         0 die "Need “lio_opcode”!";
279             };
280              
281 10 50       80 my $opcode_cr = $class->can("_IOCB_CMD_$opcode") or do {
282 0         0 die "Unknown “lio_opcode” ($opcode)";
283             };
284              
285 10         19 my %opts;
286 10         51 @opts{'nbytes', 'buffer_offset'} = @args{'nbytes', 'buffer_offset'};
287              
288 10         29 $opts{'lio_opcode'} = 0 + $opcode_cr->();
289 10         33 $opts{'fildes'} = fileno $fh;
290 10         13 $opts{'reserved2'} = 0;
291 10         36 $opts{'reqprio'} = $args{'reqprio'};
292              
293 10 50       30 if ($args{'rw_flags'}) {
294 0         0 my $flag = 0;
295 0         0 for my $flag_name ( @{ $args{'rw_flags'} } ) {
  0         0  
296 0 0       0 my $num = $class->can("_RWF_$flag_name") or do {
297 0         0 die "Unknown -rw_flags- value ($flag_name)";
298             };
299 0         0 $flag |= 0 + $num->();
300             }
301              
302 0         0 $opts{'rw_flags'} = $flag;
303             }
304              
305 10 100       27 if (defined $args{'eventfd'}) {
306 2         7 $opts{'flags'} = _IOCB_FLAG_RESFD;
307 2         4 $opts{'resfd'} = $args{'eventfd'};
308             }
309              
310 10         56 my $buf_ptr = $class->unpack_pointer( pack 'P', $$buf_sr );
311              
312 10   100     52 my $buffer_offset = $opts{'buffer_offset'} || 0;
313              
314 10 100       29 if ( $opts{'buffer_offset'} ) {
315 4   66     22 $opts{'nbytes'} ||= length($$buf_sr) - $opts{'buffer_offset'};
316              
317 4         10 $buf_ptr += $opts{'buffer_offset'};
318             }
319             else {
320 6   66     23 $opts{'nbytes'} ||= length $$buf_sr;
321             }
322              
323 10 50       53 if ( $opts{'nbytes'} + $buffer_offset > length $$buf_sr ) {
324 0         0 die sprintf( "nbytes($opts{'nbytes'}) + buffer_offset($buffer_offset) > buffer_length(%d)", length $$buf_sr );
325             }
326              
327 10         18 $opts{'buf'} = $buf_ptr;
328              
329 10   100     35 $_ ||= 0 for @opts{ $class->iocb_keys() };
330              
331 10         53 my $packed = pack $class->iocb_pack(), @opts{ $class->iocb_keys() };
332 10         47 my $ptr = pack 'P', $packed;
333              
334             #We need $packed not to be garbage-collected.
335 10         35 return bless [ \$packed, $buf_sr, $ptr, $class->unpack_pointer($ptr) ], $class;
336             }
337              
338             =head2 $sref = I->buffer_sr()
339              
340             Returns the string buffer reference given originally to C.
341              
342             =cut
343              
344 4     4   32009 sub buffer_sr { return $_[0][1] }
345              
346             =head2 $sref = I->pointer()
347              
348             Returns the internal C’s memory address as an octet string.
349              
350             =cut
351              
352 10     10   41 sub pointer { return $_[0][2] }
353              
354             =head2 $sref = I->id()
355              
356             Returns the internal C’s ID.
357              
358             =cut
359              
360 4     4   83 sub id { return $_[0][3] }
361              
362             1;