File Coverage

blib/lib/POE/Wheel/ReadWrite.pm
Criterion Covered Total %
statement 194 255 76.0
branch 84 144 58.3
condition 30 44 68.1
subroutine 25 32 78.1
pod 20 21 95.2
total 353 496 71.1


line stmt bran cond sub pod time code
1             package POE::Wheel::ReadWrite;
2              
3 26     26   8700 use strict;
  26         45  
  26         1069  
4              
5 26     26   126 use vars qw($VERSION @ISA);
  26         37  
  26         1560  
6             $VERSION = '1.367'; # NOTE - Should be #.### (three decimal places)
7              
8 26     26   121 use Carp qw( croak carp );
  26         47  
  26         1445  
9 26     26   127 use POE qw(Wheel Driver::SysRW Filter::Line);
  26         44  
  26         257  
10             push @ISA, qw(POE::Wheel);
11              
12             # Offsets into $self.
13             sub HANDLE_INPUT () { 0 }
14             sub HANDLE_OUTPUT () { 1 }
15             sub FILTER_INPUT () { 2 }
16             sub FILTER_OUTPUT () { 3 }
17             sub DRIVER_BOTH () { 4 }
18             sub EVENT_INPUT () { 5 }
19             sub EVENT_ERROR () { 6 }
20             sub EVENT_FLUSHED () { 7 }
21             sub WATERMARK_WRITE_MARK_HIGH () { 8 }
22             sub WATERMARK_WRITE_MARK_LOW () { 9 }
23             sub WATERMARK_WRITE_EVENT_HIGH () { 10 }
24             sub WATERMARK_WRITE_EVENT_LOW () { 11 }
25             sub WATERMARK_WRITE_STATE () { 12 }
26             sub DRIVER_BUFFERED_OUT_OCTETS () { 13 }
27             sub STATE_WRITE () { 14 }
28             sub STATE_READ () { 15 }
29             sub UNIQUE_ID () { 16 }
30             sub AUTOFLUSH () { 17 }
31              
32 0     0 0 0 sub CRIMSON_SCOPE_HACK ($) { 0 }
33              
34             #------------------------------------------------------------------------------
35              
36             sub new {
37 158     158 1 8493 my $type = shift;
38 158         713 my %params = @_;
39              
40 158 100 100     1093 croak "wheels no longer require a kernel reference as their first parameter"
41             if (@_ && (ref($_[0]) eq 'POE::Kernel'));
42              
43 156 50       334 croak "$type requires a working Kernel" unless defined $poe_kernel;
44              
45 156         162 my ($in_handle, $out_handle);
46 156 100       338 if (defined $params{Handle}) {
47 148 50       452 carp "Ignoring InputHandle parameter (Handle parameter takes precedence)"
48             if defined $params{InputHandle};
49 148 50       276 carp "Ignoring OutputHandle parameter (Handle parameter takes precedence)"
50             if defined $params{OutputHandle};
51 148         293 $in_handle = $out_handle = delete $params{Handle};
52             }
53             else {
54 8 100       268 croak "Handle or InputHandle required"
55             unless defined $params{InputHandle};
56 6 100       270 croak "Handle or OutputHandle required"
57             unless defined $params{OutputHandle};
58 4         10 $in_handle = delete $params{InputHandle};
59 4         16 $out_handle = delete $params{OutputHandle};
60             }
61              
62 152         161 my ($in_filter, $out_filter);
63 152 100       316 if (defined $params{Filter}) {
64 109 50       869 carp "Ignoring InputFilter parameter (Filter parameter takes precedence)"
65             if (defined $params{InputFilter});
66 109 50       209 carp "Ignoring OutputFilter parameter (Filter parameter takes precedence)"
67             if (defined $params{OutputFilter});
68 109         209 $in_filter = $out_filter = delete $params{Filter};
69             }
70             else {
71 43         66 $in_filter = delete $params{InputFilter};
72 43         56 $out_filter = delete $params{OutputFilter};
73              
74             # If neither Filter, InputFilter or OutputFilter is defined, then
75             # they default to POE::Filter::Line.
76 43 50 33     126 unless (defined $in_filter and defined $out_filter) {
77 43         199 my $new_filter = POE::Filter::Line->new();
78 43 50       102 $in_filter = $new_filter unless defined $in_filter;
79 43 50       112 $out_filter = $new_filter unless defined $out_filter;
80             }
81             }
82              
83 152         257 my $driver = delete $params{Driver};
84 152 100       429 $driver = POE::Driver::SysRW->new() unless defined $driver;
85              
86 152         155 { my $mark_errors = 0;
  152         181  
87 152 100 100     874 if (defined($params{HighMark}) xor defined($params{LowMark})) {
    100          
88 4         515 carp "HighMark and LowMark parameters require each-other";
89 4         204 $mark_errors++;
90             }
91             # Then they both exist, and they must be checked.
92             elsif (defined $params{HighMark}) {
93 12 50 33     54 unless (defined($params{HighMark}) and defined($params{LowMark})) {
94 0         0 carp "HighMark and LowMark parameters must both be defined";
95 0         0 $mark_errors++;
96             }
97 12 100 100     54 unless (($params{HighMark} > 0) and ($params{LowMark} > 0)) {
98 6         783 carp "HighMark and LowMark parameters must be above 0";
99 6         350 $mark_errors++;
100             }
101             }
102 152 100 100     473 if (defined $params{HighEvent} and not defined $params{HighMark}) {
103 6         703 carp "HighEvent requires a corresponding HighMark";
104 6         232 $mark_errors++;
105             }
106 152 100 100     664 if (defined($params{LowMark}) xor defined($params{LowEvent})) {
107 8         850 carp "LowMark and LowEvent parameters require each-other";
108 8         355 $mark_errors++;
109             }
110 152 100       2065 croak "Water mark errors" if $mark_errors;
111             }
112              
113 134         748 my $self = bless [
114             $in_handle, # HANDLE_INPUT
115             $out_handle, # HANDLE_OUTPUT
116             $in_filter, # FILTER_INPUT
117             $out_filter, # FILTER_OUTPUT
118             $driver, # DRIVER_BOTH
119             delete $params{InputEvent}, # EVENT_INPUT
120             delete $params{ErrorEvent}, # EVENT_ERROR
121             delete $params{FlushedEvent}, # EVENT_FLUSHED
122             # Water marks.
123             delete $params{HighMark}, # WATERMARK_WRITE_MARK_HIGH
124             delete $params{LowMark}, # WATERMARK_WRITE_MARK_LOW
125             delete $params{HighEvent}, # WATERMARK_WRITE_EVENT_HIGH
126             delete $params{LowEvent}, # WATERMARK_WRITE_EVENT_LOW
127             0, # WATERMARK_WRITE_STATE
128             # Driver statistics.
129             0, # DRIVER_BUFFERED_OUT_OCTETS
130             # Dynamic state names.
131             undef, # STATE_WRITE
132             undef, # STATE_READ
133             # Unique ID.
134             &POE::Wheel::allocate_wheel_id(), # UNIQUE_ID
135             delete $params{AutoFlush}, # AUTOFLUSH
136             ], $type;
137              
138 134 50       390 if (scalar keys %params) {
139 0         0 carp(
140             "unknown parameters in $type constructor call: ",
141             join(', ', keys %params)
142             );
143             }
144              
145 134         381 $self->_define_read_state();
146 132         349 $self->_define_write_state();
147              
148 132         585 return $self;
149             }
150              
151             #------------------------------------------------------------------------------
152             # Redefine the select-write handler. This uses stupid closure tricks
153             # to prevent keeping extra references to $self around.
154              
155             sub _define_write_state {
156 136     136   180 my $self = shift;
157              
158             # Read-only members. If any of these change, then the write state
159             # is invalidated and needs to be redefined.
160 136         202 my $driver = $self->[DRIVER_BOTH];
161 136         160 my $high_mark = $self->[WATERMARK_WRITE_MARK_HIGH];
162 136         168 my $low_mark = $self->[WATERMARK_WRITE_MARK_LOW];
163 136         179 my $event_error = \$self->[EVENT_ERROR];
164 136         183 my $event_flushed = \$self->[EVENT_FLUSHED];
165 136         182 my $event_high = \$self->[WATERMARK_WRITE_EVENT_HIGH];
166 136         200 my $event_low = \$self->[WATERMARK_WRITE_EVENT_LOW];
167 136         205 my $unique_id = $self->[UNIQUE_ID];
168              
169             # Read/write members. These are done by reference, to avoid pushing
170             # $self into the anonymous sub. Extra copies of $self are bad and
171             # can prevent wheels from destructing properly.
172 136         167 my $is_in_high_water_state = \$self->[WATERMARK_WRITE_STATE];
173 136         184 my $driver_buffered_out_octets = \$self->[DRIVER_BUFFERED_OUT_OCTETS];
174              
175             # Register the select-write handler.
176              
177             $poe_kernel->state(
178             $self->[STATE_WRITE] = ref($self) . "($unique_id) -> select write",
179             sub { # prevents SEGV
180 217     217   256 0 && CRIMSON_SCOPE_HACK('<');
181             # subroutine starts here
182 217         493 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
183              
184 217         857 $$driver_buffered_out_octets = $driver->flush($handle);
185              
186             # When you can't write, nothing else matters.
187 217 50       792 if ($!) {
188 0 0       0 $$event_error && $k->call(
189             $me, $$event_error, 'write', ($!+0), $!, $unique_id
190             );
191 0         0 $k->select_write($handle);
192             }
193              
194             # Could write, or perhaps couldn't but only because the
195             # filehandle's buffer is choked.
196             else {
197              
198             # In high water state? Check for low water. High water
199             # state will never be set if $event_low is undef, so don't
200             # bother checking its definedness here.
201 217 100 66     1031 if ($$is_in_high_water_state) {
    50          
202 4 50       12 if ( $$driver_buffered_out_octets <= $low_mark ) {
203 4         9 $$is_in_high_water_state = 0;
204 4 50       23 $k->call( $me, $$event_low, $unique_id ) if defined $$event_low;
205             }
206             }
207              
208             # Not in high water state. Check for high water. Needs to
209             # also check definedness of $$driver_buffered_out_octets.
210             # Although we know this ahead of time and could probably
211             # optimize it away with a second state definition, it would
212             # be best to wait until ReadWrite stabilizes. That way
213             # there will be only half as much code to maintain.
214             elsif (
215             $high_mark and
216             ( $$driver_buffered_out_octets >= $high_mark )
217             ) {
218 0         0 $$is_in_high_water_state = 1;
219 0 0       0 $k->call( $me, $$event_high, $unique_id ) if defined $$event_high;
220             }
221             }
222              
223             # All chunks written; fire off a "flushed" event. This
224             # occurs independently, so it's possible to get a low-water
225             # call and a flushed call at the same time (if the low mark
226             # is 1).
227 217 50       481 unless ($$driver_buffered_out_octets) {
228 217         880 $k->select_pause_write($handle);
229 217 100       994 $$event_flushed && $k->call($me, $$event_flushed, $unique_id);
230             }
231             }
232 136         1200 );
233              
234 136         457 $poe_kernel->select_write($self->[HANDLE_OUTPUT], $self->[STATE_WRITE]);
235              
236             # Pause the write select immediately, unless output is pending.
237 136 50       691 $poe_kernel->select_pause_write($self->[HANDLE_OUTPUT])
238             unless ($self->[DRIVER_BUFFERED_OUT_OCTETS]);
239             }
240              
241             #------------------------------------------------------------------------------
242             # Redefine the select-read handler. This uses stupid closure tricks
243             # to prevent keeping extra references to $self around.
244              
245             sub _define_read_state {
246 138     138   185 my $self = shift;
247              
248             # Register the select-read handler.
249              
250 138 100       1456 if (defined $self->[EVENT_INPUT]) {
251              
252             # If any of these change, then the read state is invalidated and
253             # needs to be redefined.
254              
255 132         206 my $driver = $self->[DRIVER_BOTH];
256 132         206 my $input_filter = \$self->[FILTER_INPUT];
257 132         177 my $event_input = \$self->[EVENT_INPUT];
258 132         174 my $event_error = \$self->[EVENT_ERROR];
259 132         223 my $unique_id = $self->[UNIQUE_ID];
260              
261             # If the filter can get_one, then define the input state in terms
262             # of get_one_start() and get_one().
263              
264 132 50 33     1106 if (
265             $$input_filter->can('get_one') and
266             $$input_filter->can('get_one_start')
267             ) {
268             $poe_kernel->state(
269             $self->[STATE_READ] = ref($self) . "($unique_id) -> select read",
270             sub {
271              
272             # Protects against coredump on older perls.
273 285     285   359 0 && CRIMSON_SCOPE_HACK('<');
274              
275             # The actual code starts here.
276 285         712 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
277 285 100       1062 if (defined(my $raw_input = $driver->get($handle))) {
278 221         930 $$input_filter->get_one_start($raw_input);
279 221         252 while (1) {
280 434         1299 my $next_rec = $$input_filter->get_one();
281 434 100       1602 last unless @$next_rec;
282 213         443 foreach my $cooked_input (@$next_rec) {
283 213         747 $k->call($me, $$event_input, $cooked_input, $unique_id);
284             }
285             }
286             }
287             else {
288 64 50       420 $$event_error and $k->call(
289             $me, $$event_error, 'read', ($!+0), $!, $unique_id
290             );
291 64         191 $k->select_read($handle);
292             }
293             }
294 132         1508 );
295             }
296              
297             # Otherwise define the input state in terms of the older, less
298             # robust, yet faster get().
299              
300             else {
301             $poe_kernel->state(
302             $self->[STATE_READ] = ref($self) . "($unique_id) -> select read",
303             sub {
304              
305             # Protects against coredump on older perls.
306 0     0   0 0 && CRIMSON_SCOPE_HACK('<');
307              
308             # The actual code starts here.
309 0         0 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
310 0 0       0 if (defined(my $raw_input = $driver->get($handle))) {
311 0         0 foreach my $cooked_input (@{$$input_filter->get($raw_input)}) {
  0         0  
312 0         0 $k->call($me, $$event_input, $cooked_input, $unique_id);
313             }
314             }
315             else {
316 0 0       0 $$event_error and $k->call(
317             $me, $$event_error, 'read', ($!+0), $!, $unique_id
318             );
319 0         0 $k->select_read($handle);
320             }
321             }
322 0         0 );
323             }
324             # register the state's select
325 132         508 $poe_kernel->select_read($self->[HANDLE_INPUT], $self->[STATE_READ]);
326             }
327             # undefine the select, just in case
328             else {
329 6         58 $poe_kernel->select_read($self->[HANDLE_INPUT])
330             }
331             }
332              
333             #------------------------------------------------------------------------------
334             # Redefine events.
335              
336             sub event {
337 4     4 1 36 my $self = shift;
338 4 50       17 push(@_, undef) if (scalar(@_) & 1);
339              
340 4         7 my ($redefine_read, $redefine_write) = (0, 0);
341              
342 4         13 while (@_) {
343 14         38 my ($name, $event) = splice(@_, 0, 2);
344              
345 14 100       1049 if ($name eq 'InputEvent') {
    100          
    100          
    100          
    50          
346 2         5 $self->[EVENT_INPUT] = $event;
347 2         7 $redefine_read = 1;
348             }
349             elsif ($name eq 'ErrorEvent') {
350 4         9 $self->[EVENT_ERROR] = $event;
351 4         12 $redefine_read = $redefine_write = 1;
352             }
353             elsif ($name eq 'FlushedEvent') {
354 4         10 $self->[EVENT_FLUSHED] = $event;
355 4         13 $redefine_write = 1;
356             }
357             elsif ($name eq 'HighEvent') {
358 2 50       8 if (defined $self->[WATERMARK_WRITE_MARK_HIGH]) {
359 2         5 $self->[WATERMARK_WRITE_EVENT_HIGH] = $event;
360 2         6 $redefine_write = 1;
361             }
362             else {
363 0         0 carp "Ignoring HighEvent (there is no high watermark set)";
364             }
365             }
366             elsif ($name eq 'LowEvent') {
367 2 50       8 if (defined $self->[WATERMARK_WRITE_MARK_LOW]) {
368 2         3 $self->[WATERMARK_WRITE_EVENT_LOW] = $event;
369 2         7 $redefine_write = 1;
370             }
371             else {
372 0         0 carp "Ignoring LowEvent (there is no high watermark set)";
373             }
374             }
375             else {
376 0         0 carp "ignoring unknown ReadWrite parameter '$name'";
377             }
378             }
379              
380 4 50       20 $self->_define_read_state() if $redefine_read;
381 4 50       27 $self->_define_write_state() if $redefine_write;
382             }
383              
384             #------------------------------------------------------------------------------
385              
386             sub DESTROY {
387 134     134   3590 my $self = shift;
388              
389             # Turn off the select. This is a problem if a wheel is being
390             # swapped, since it will turn off selects for the other wheel.
391 134 50       480 if ($self->[HANDLE_INPUT]) {
392 134         697 $poe_kernel->select_read($self->[HANDLE_INPUT]);
393 132         219 $self->[HANDLE_INPUT] = undef;
394             }
395              
396 132 50       379 if ($self->[HANDLE_OUTPUT]) {
397 132         397 $poe_kernel->select_write($self->[HANDLE_OUTPUT]);
398 132         230 $self->[HANDLE_OUTPUT] = undef;
399             }
400              
401 132 100       4978 if ($self->[STATE_READ]) {
402 130         507 $poe_kernel->state($self->[STATE_READ]);
403 130         203 $self->[STATE_READ] = undef;
404             }
405              
406 132 50       324 if ($self->[STATE_WRITE]) {
407 132         366 $poe_kernel->state($self->[STATE_WRITE]);
408 132         226 $self->[STATE_WRITE] = undef;
409             }
410              
411 132         444 &POE::Wheel::free_wheel_id($self->[UNIQUE_ID]);
412             }
413              
414             #------------------------------------------------------------------------------
415             # TODO - We set the high/low watermark state here, but we don't fire
416             # events for it. My assumption is that the return value tells us
417             # all we want to know.
418              
419             sub put {
420 247     247 1 7800 my ($self, @chunks) = @_;
421              
422 247         696 my $old_buffered_out_octets = $self->[DRIVER_BUFFERED_OUT_OCTETS];
423 247         1011 my $new_buffered_out_octets =
424             $self->[DRIVER_BUFFERED_OUT_OCTETS] =
425             $self->[DRIVER_BOTH]->put($self->[FILTER_OUTPUT]->put(\@chunks));
426              
427 247 0 33     899 if (
      33        
428             $self->[AUTOFLUSH] &&
429             $new_buffered_out_octets and !$old_buffered_out_octets
430             ) {
431 0         0 $old_buffered_out_octets = $new_buffered_out_octets;
432 0         0 $self->flush();
433 0         0 $new_buffered_out_octets = $self->[DRIVER_BUFFERED_OUT_OCTETS];
434             }
435              
436             # Resume write-ok if the output buffer gets data. This avoids
437             # redundant calls to select_resume_write(), which is probably a good
438             # thing.
439 247 100 66     1040 if ($new_buffered_out_octets and !$old_buffered_out_octets) {
440 219         852 $poe_kernel->select_resume_write($self->[HANDLE_OUTPUT]);
441             }
442              
443             # If the high watermark has been reached, return true.
444 247 100 100     832 if (
445             $self->[WATERMARK_WRITE_MARK_HIGH] and
446             $new_buffered_out_octets >= $self->[WATERMARK_WRITE_MARK_HIGH]
447             ) {
448 8         30 return $self->[WATERMARK_WRITE_STATE] = 1;
449             }
450              
451 239         825 return $self->[WATERMARK_WRITE_STATE] = 0;
452             }
453              
454             #------------------------------------------------------------------------------
455             # Redefine filter. -PG / Now that there are two filters internally,
456             # one input and one output, make this set both of them at the same
457             # time. -RCC
458              
459             sub _transfer_input_buffer {
460 41     41   84 my ($self, $buf) = @_;
461              
462 41         87 my $old_input_filter = $self->[FILTER_INPUT];
463              
464             # If the new filter implements "get_one", use that.
465 41 50 33     429 if (
466             $old_input_filter->can('get_one') and
467             $old_input_filter->can('get_one_start')
468             ) {
469 41 100       173 if (defined $buf) {
470 9         31 $self->[FILTER_INPUT]->get_one_start($buf);
471 9         30 while ($self->[FILTER_INPUT] == $old_input_filter) {
472 19         67 my $next_rec = $self->[FILTER_INPUT]->get_one();
473 19 100       52 last unless @$next_rec;
474 18         31 foreach my $cooked_input (@$next_rec) {
475 18         60 $poe_kernel->call(
476             $poe_kernel->get_active_session(),
477             $self->[EVENT_INPUT],
478             $cooked_input, $self->[UNIQUE_ID]
479             );
480             }
481             }
482             }
483             }
484              
485             # Otherwise use the old behavior.
486             else {
487 0 0       0 if (defined $buf) {
488 0         0 foreach my $cooked_input (@{$self->[FILTER_INPUT]->get($buf)}) {
  0         0  
489 0         0 $poe_kernel->call(
490             $poe_kernel->get_active_session(),
491             $self->[EVENT_INPUT],
492             $cooked_input, $self->[UNIQUE_ID]
493             );
494             }
495             }
496             }
497             }
498              
499             # Set input and output filters.
500              
501             sub set_filter {
502 32     32 1 61 my ($self, $new_filter) = @_;
503 32         144 my $buf = $self->[FILTER_INPUT]->get_pending();
504 32         86 $self->[FILTER_INPUT] = $self->[FILTER_OUTPUT] = $new_filter;
505              
506 32         184 $self->_transfer_input_buffer($buf);
507             }
508              
509             # Redefine input and/or output filters separately.
510             sub set_input_filter {
511 9     9 1 18 my ($self, $new_filter) = @_;
512 9         36 my $buf = $self->[FILTER_INPUT]->get_pending();
513 9         14 $self->[FILTER_INPUT] = $new_filter;
514              
515 9         63 $self->_transfer_input_buffer($buf);
516             }
517              
518             # No closures need to be redefined or anything. All the previously
519             # put stuff has been serialized already.
520             sub set_output_filter {
521 9     9 1 16 my ($self, $new_filter) = @_;
522 9         49 $self->[FILTER_OUTPUT] = $new_filter;
523             }
524              
525             # Get the current input filter; used for accessing the filter's custom
526             # methods, as in: $wheel->get_input_filter()->filter_method();
527             sub get_input_filter {
528 2     2 1 47 my $self = shift;
529 2         16 return $self->[FILTER_INPUT];
530             }
531              
532             # Get the current input filter; used for accessing the filter's custom
533             # methods, as in: $wheel->get_input_filter()->filter_method();
534             sub get_output_filter {
535 2     2 1 7 my $self = shift;
536 2         10 return $self->[FILTER_OUTPUT];
537             }
538              
539             # Set the high water mark.
540              
541             sub set_high_mark {
542 0     0 1 0 my ($self, $new_high_mark) = @_;
543              
544 0 0       0 unless (defined $self->[WATERMARK_WRITE_MARK_HIGH]) {
545 0         0 carp "Ignoring high mark (must be initialized in constructor first)";
546 0         0 return;
547             }
548              
549 0 0       0 unless (defined $new_high_mark) {
550 0         0 carp "New high mark is undefined. Ignored";
551 0         0 return;
552             }
553              
554 0 0       0 unless ($new_high_mark > $self->[WATERMARK_WRITE_MARK_LOW]) {
555 0         0 carp "New high mark would not be greater than low mark. Ignored";
556 0         0 return;
557             }
558              
559 0         0 $self->[WATERMARK_WRITE_MARK_HIGH] = $new_high_mark;
560 0         0 $self->_define_write_state();
561             }
562              
563             sub set_low_mark {
564 0     0 1 0 my ($self, $new_low_mark) = @_;
565              
566 0 0       0 unless (defined $self->[WATERMARK_WRITE_MARK_LOW]) {
567 0         0 carp "Ignoring low mark (must be initialized in constructor first)";
568 0         0 return;
569             }
570              
571 0 0       0 unless (defined $new_low_mark) {
572 0         0 carp "New low mark is undefined. Ignored";
573 0         0 return;
574             }
575              
576 0 0       0 unless ($new_low_mark > 0) {
577 0         0 carp "New low mark would be less than one. Ignored";
578 0         0 return;
579             }
580              
581 0 0       0 unless ($new_low_mark < $self->[WATERMARK_WRITE_MARK_HIGH]) {
582 0         0 carp "New low mark would not be less than high high mark. Ignored";
583 0         0 return;
584             }
585              
586 0         0 $self->[WATERMARK_WRITE_MARK_LOW] = $new_low_mark;
587 0         0 $self->_define_write_state();
588             }
589              
590             # Return driver statistics.
591             sub get_driver_out_octets {
592 56     56 1 306 $_[0]->[DRIVER_BUFFERED_OUT_OCTETS];
593             }
594              
595             sub get_driver_out_messages {
596 4     4 1 21 $_[0]->[DRIVER_BOTH]->get_out_messages_buffered();
597             }
598              
599             # Get the wheel's ID.
600             sub ID {
601 12     12 1 90 return $_[0]->[UNIQUE_ID];
602             }
603              
604             # Pause the wheel's input watcher.
605             sub pause_input {
606 2     2 1 851 my $self = shift;
607 2 50       11 return unless defined $self->[HANDLE_INPUT];
608 2         15 $poe_kernel->select_pause_read( $self->[HANDLE_INPUT] );
609             }
610              
611             # Resume the wheel's input watcher.
612             sub resume_input {
613 2     2 1 25 my $self = shift;
614 2 50       13 return unless defined $self->[HANDLE_INPUT];
615 2         15 $poe_kernel->select_resume_read( $self->[HANDLE_INPUT] );
616             }
617              
618             # Return the wheel's input handle
619             sub get_input_handle {
620 0     0 1 0 my $self = shift;
621 0         0 return $self->[HANDLE_INPUT];
622             }
623              
624             # Return the wheel's output handle
625             sub get_output_handle {
626 0     0 1 0 my $self = shift;
627 0         0 return $self->[HANDLE_OUTPUT];
628             }
629              
630             # Shutdown the socket for reading.
631             sub shutdown_input {
632 2     2 1 36 my $self = shift;
633 2 50       11 return unless defined $self->[HANDLE_INPUT];
634 2         4 eval { local $^W = 0; shutdown($self->[HANDLE_INPUT], 0) };
  2         13  
  2         55  
635 2         27 $poe_kernel->select_read($self->[HANDLE_INPUT], undef);
636             }
637              
638             # Shutdown the socket for writing.
639             sub shutdown_output {
640 2     2 1 15 my $self = shift;
641 2 50       13 return unless defined $self->[HANDLE_OUTPUT];
642 2         4 eval { local $^W=0; shutdown($self->[HANDLE_OUTPUT], 1) };
  2         8  
  2         11  
643 2         26 $poe_kernel->select_write($self->[HANDLE_OUTPUT], undef);
644             }
645              
646             # Flush the output handle
647             sub flush {
648 0     0 1   my $self = shift;
649 0 0         return unless defined $self->[HANDLE_OUTPUT];
650 0           $poe_kernel->call($poe_kernel->get_active_session(),
651             $self->[STATE_WRITE], $self->[HANDLE_OUTPUT]);
652             }
653              
654             1;
655              
656             __END__