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   7415 use strict;
  26         172  
  26         892  
4              
5 26     26   115 use vars qw($VERSION @ISA);
  26         36  
  26         1301  
6             $VERSION = '1.365'; # NOTE - Should be #.### (three decimal places)
7              
8 26     26   135 use Carp qw( croak carp );
  26         39  
  26         1234  
9 26     26   120 use POE qw(Wheel Driver::SysRW Filter::Line);
  26         31  
  26         208  
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 9195 my $type = shift;
38 158         636 my %params = @_;
39              
40 158 100 100     945 croak "wheels no longer require a kernel reference as their first parameter"
41             if (@_ && (ref($_[0]) eq 'POE::Kernel'));
42              
43 156 50       315 croak "$type requires a working Kernel" unless defined $poe_kernel;
44              
45 156         161 my ($in_handle, $out_handle);
46 156 100       327 if (defined $params{Handle}) {
47 148 50       292 carp "Ignoring InputHandle parameter (Handle parameter takes precedence)"
48             if defined $params{InputHandle};
49 148 50       254 carp "Ignoring OutputHandle parameter (Handle parameter takes precedence)"
50             if defined $params{OutputHandle};
51 148         285 $in_handle = $out_handle = delete $params{Handle};
52             }
53             else {
54 8 100       271 croak "Handle or InputHandle required"
55             unless defined $params{InputHandle};
56 6 100       278 croak "Handle or OutputHandle required"
57             unless defined $params{OutputHandle};
58 4         7 $in_handle = delete $params{InputHandle};
59 4         8 $out_handle = delete $params{OutputHandle};
60             }
61              
62 152         151 my ($in_filter, $out_filter);
63 152 100       296 if (defined $params{Filter}) {
64 109 50       214 carp "Ignoring InputFilter parameter (Filter parameter takes precedence)"
65             if (defined $params{InputFilter});
66 109 50       216 carp "Ignoring OutputFilter parameter (Filter parameter takes precedence)"
67             if (defined $params{OutputFilter});
68 109         160 $in_filter = $out_filter = delete $params{Filter};
69             }
70             else {
71 43         52 $in_filter = delete $params{InputFilter};
72 43         65 $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     119 unless (defined $in_filter and defined $out_filter) {
77 43         182 my $new_filter = POE::Filter::Line->new();
78 43 50       90 $in_filter = $new_filter unless defined $in_filter;
79 43 50       95 $out_filter = $new_filter unless defined $out_filter;
80             }
81             }
82              
83 152         238 my $driver = delete $params{Driver};
84 152 100       390 $driver = POE::Driver::SysRW->new() unless defined $driver;
85              
86 152         153 { my $mark_errors = 0;
  152         177  
87 152 100 100     806 if (defined($params{HighMark}) xor defined($params{LowMark})) {
    100          
88 4         450 carp "HighMark and LowMark parameters require each-other";
89 4         196 $mark_errors++;
90             }
91             # Then they both exist, and they must be checked.
92             elsif (defined $params{HighMark}) {
93 12 50 33     53 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     45 unless (($params{HighMark} > 0) and ($params{LowMark} > 0)) {
98 6         665 carp "HighMark and LowMark parameters must be above 0";
99 6         261 $mark_errors++;
100             }
101             }
102 152 100 100     428 if (defined $params{HighEvent} and not defined $params{HighMark}) {
103 6         714 carp "HighEvent requires a corresponding HighMark";
104 6         246 $mark_errors++;
105             }
106 152 100 100     600 if (defined($params{LowMark}) xor defined($params{LowEvent})) {
107 8         826 carp "LowMark and LowEvent parameters require each-other";
108 8         297 $mark_errors++;
109             }
110 152 100       2738 croak "Water mark errors" if $mark_errors;
111             }
112              
113 134         650 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       347 if (scalar keys %params) {
139 0         0 carp(
140             "unknown parameters in $type constructor call: ",
141             join(', ', keys %params)
142             );
143             }
144              
145 134         330 $self->_define_read_state();
146 132         304 $self->_define_write_state();
147              
148 132         495 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   176 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         180 my $driver = $self->[DRIVER_BOTH];
161 136         878 my $high_mark = $self->[WATERMARK_WRITE_MARK_HIGH];
162 136         156 my $low_mark = $self->[WATERMARK_WRITE_MARK_LOW];
163 136         172 my $event_error = \$self->[EVENT_ERROR];
164 136         162 my $event_flushed = \$self->[EVENT_FLUSHED];
165 136         165 my $event_high = \$self->[WATERMARK_WRITE_EVENT_HIGH];
166 136         182 my $event_low = \$self->[WATERMARK_WRITE_EVENT_LOW];
167 136         204 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         143 my $is_in_high_water_state = \$self->[WATERMARK_WRITE_STATE];
173 136         163 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   228 0 && CRIMSON_SCOPE_HACK('<');
181             # subroutine starts here
182 217         441 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
183              
184 217         714 $$driver_buffered_out_octets = $driver->flush($handle);
185              
186             # When you can't write, nothing else matters.
187 217 50       586 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     901 if ($$is_in_high_water_state) {
    50          
202 4 50       11 if ( $$driver_buffered_out_octets <= $low_mark ) {
203 4         11 $$is_in_high_water_state = 0;
204 4 50       18 $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       480 unless ($$driver_buffered_out_octets) {
228 217         669 $k->select_pause_write($handle);
229 217 100       947 $$event_flushed && $k->call($me, $$event_flushed, $unique_id);
230             }
231             }
232 136         1177 );
233              
234 136         403 $poe_kernel->select_write($self->[HANDLE_OUTPUT], $self->[STATE_WRITE]);
235              
236             # Pause the write select immediately, unless output is pending.
237 136 50       561 $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   166 my $self = shift;
247              
248             # Register the select-read handler.
249              
250 138 100       404 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         158 my $driver = $self->[DRIVER_BOTH];
256 132         176 my $input_filter = \$self->[FILTER_INPUT];
257 132         177 my $event_input = \$self->[EVENT_INPUT];
258 132         160 my $event_error = \$self->[EVENT_ERROR];
259 132         181 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     1004 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   315 0 && CRIMSON_SCOPE_HACK('<');
274              
275             # The actual code starts here.
276 285         632 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
277 285 100       874 if (defined(my $raw_input = $driver->get($handle))) {
278 221         730 $$input_filter->get_one_start($raw_input);
279 221         212 while (1) {
280 434         1157 my $next_rec = $$input_filter->get_one();
281 434 100       1457 last unless @$next_rec;
282 213         354 foreach my $cooked_input (@$next_rec) {
283 213         688 $k->call($me, $$event_input, $cooked_input, $unique_id);
284             }
285             }
286             }
287             else {
288 64 50       368 $$event_error and $k->call(
289             $me, $$event_error, 'read', ($!+0), $!, $unique_id
290             );
291 64         181 $k->select_read($handle);
292             }
293             }
294 132         1225 );
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         449 $poe_kernel->select_read($self->[HANDLE_INPUT], $self->[STATE_READ]);
326             }
327             # undefine the select, just in case
328             else {
329 6         26 $poe_kernel->select_read($self->[HANDLE_INPUT])
330             }
331             }
332              
333             #------------------------------------------------------------------------------
334             # Redefine events.
335              
336             sub event {
337 4     4 1 30 my $self = shift;
338 4 50       15 push(@_, undef) if (scalar(@_) & 1);
339              
340 4         9 my ($redefine_read, $redefine_write) = (0, 0);
341              
342 4         13 while (@_) {
343 14         887 my ($name, $event) = splice(@_, 0, 2);
344              
345 14 100       51 if ($name eq 'InputEvent') {
    100          
    100          
    100          
    50          
346 2         4 $self->[EVENT_INPUT] = $event;
347 2         5 $redefine_read = 1;
348             }
349             elsif ($name eq 'ErrorEvent') {
350 4         6 $self->[EVENT_ERROR] = $event;
351 4         9 $redefine_read = $redefine_write = 1;
352             }
353             elsif ($name eq 'FlushedEvent') {
354 4         8 $self->[EVENT_FLUSHED] = $event;
355 4         9 $redefine_write = 1;
356             }
357             elsif ($name eq 'HighEvent') {
358 2 50       781 if (defined $self->[WATERMARK_WRITE_MARK_HIGH]) {
359 2         4 $self->[WATERMARK_WRITE_EVENT_HIGH] = $event;
360 2         5 $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       5 if (defined $self->[WATERMARK_WRITE_MARK_LOW]) {
368 2         3 $self->[WATERMARK_WRITE_EVENT_LOW] = $event;
369 2         5 $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       16 $self->_define_read_state() if $redefine_read;
381 4 50       49 $self->_define_write_state() if $redefine_write;
382             }
383              
384             #------------------------------------------------------------------------------
385              
386             sub DESTROY {
387 134     134   4720 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       353 if ($self->[HANDLE_INPUT]) {
392 134         476 $poe_kernel->select_read($self->[HANDLE_INPUT]);
393 132         216 $self->[HANDLE_INPUT] = undef;
394             }
395              
396 132 50       332 if ($self->[HANDLE_OUTPUT]) {
397 132         416 $poe_kernel->select_write($self->[HANDLE_OUTPUT]);
398 132         240 $self->[HANDLE_OUTPUT] = undef;
399             }
400              
401 132 100       3619 if ($self->[STATE_READ]) {
402 130         455 $poe_kernel->state($self->[STATE_READ]);
403 130         196 $self->[STATE_READ] = undef;
404             }
405              
406 132 50       301 if ($self->[STATE_WRITE]) {
407 132         699 $poe_kernel->state($self->[STATE_WRITE]);
408 132         180 $self->[STATE_WRITE] = undef;
409             }
410              
411 132         431 &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 6907 my ($self, @chunks) = @_;
421              
422 247         373 my $old_buffered_out_octets = $self->[DRIVER_BUFFERED_OUT_OCTETS];
423 247         878 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     1497 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     981 if ($new_buffered_out_octets and !$old_buffered_out_octets) {
440 219         801 $poe_kernel->select_resume_write($self->[HANDLE_OUTPUT]);
441             }
442              
443             # If the high watermark has been reached, return true.
444 247 100 100     720 if (
445             $self->[WATERMARK_WRITE_MARK_HIGH] and
446             $new_buffered_out_octets >= $self->[WATERMARK_WRITE_MARK_HIGH]
447             ) {
448 8         22 return $self->[WATERMARK_WRITE_STATE] = 1;
449             }
450              
451 239         705 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   52 my ($self, $buf) = @_;
461              
462 41         57 my $old_input_filter = $self->[FILTER_INPUT];
463              
464             # If the new filter implements "get_one", use that.
465 41 50 33     312 if (
466             $old_input_filter->can('get_one') and
467             $old_input_filter->can('get_one_start')
468             ) {
469 41 100       124 if (defined $buf) {
470 9         17 $self->[FILTER_INPUT]->get_one_start($buf);
471 9         21 while ($self->[FILTER_INPUT] == $old_input_filter) {
472 19         46 my $next_rec = $self->[FILTER_INPUT]->get_one();
473 19 100       28 last unless @$next_rec;
474 18         29 foreach my $cooked_input (@$next_rec) {
475 18         41 $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 62 my ($self, $new_filter) = @_;
503 32         122 my $buf = $self->[FILTER_INPUT]->get_pending();
504 32         67 $self->[FILTER_INPUT] = $self->[FILTER_OUTPUT] = $new_filter;
505              
506 32         121 $self->_transfer_input_buffer($buf);
507             }
508              
509             # Redefine input and/or output filters separately.
510             sub set_input_filter {
511 9     9 1 10 my ($self, $new_filter) = @_;
512 9         24 my $buf = $self->[FILTER_INPUT]->get_pending();
513 9         11 $self->[FILTER_INPUT] = $new_filter;
514              
515 9         41 $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 8 my ($self, $new_filter) = @_;
522 9         36 $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 37 my $self = shift;
529 2         13 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 4 my $self = shift;
536 2         8 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 359 $_[0]->[DRIVER_BUFFERED_OUT_OCTETS];
593             }
594              
595             sub get_driver_out_messages {
596 4     4 1 26 $_[0]->[DRIVER_BOTH]->get_out_messages_buffered();
597             }
598              
599             # Get the wheel's ID.
600             sub ID {
601 12     12 1 94 return $_[0]->[UNIQUE_ID];
602             }
603              
604             # Pause the wheel's input watcher.
605             sub pause_input {
606 2     2 1 717 my $self = shift;
607 2 50       10 return unless defined $self->[HANDLE_INPUT];
608 2         11 $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 21 my $self = shift;
614 2 50       9 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 29 my $self = shift;
633 2 50       8 return unless defined $self->[HANDLE_INPUT];
634 2         4 eval { local $^W = 0; shutdown($self->[HANDLE_INPUT], 0) };
  2         8  
  2         33  
635 2         9 $poe_kernel->select_read($self->[HANDLE_INPUT], undef);
636             }
637              
638             # Shutdown the socket for writing.
639             sub shutdown_output {
640 2     2 1 13 my $self = shift;
641 2 50       9 return unless defined $self->[HANDLE_OUTPUT];
642 2         5 eval { local $^W=0; shutdown($self->[HANDLE_OUTPUT], 1) };
  2         6  
  2         8  
643 2         14 $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__