NAME
Developer::Dashboard::StreamDrain - the one place a ready handle is read
PURPOSE
Provide the single inner drain step shared by the dashboard's inline streaming child-process readers: read up to 8192 bytes from a handle that IO::Select reported ready, and, when the handle is finished, remove it from the select set and close it.
WHY IT EXISTS
SkillDispatcher and SkillManager each carried a byte-identical copy of this step. Copy-paste was provable rather than inferred: both files carried the same authored # uncoverable condition left annotation on the same end-of-file branch. A bug in that step therefore had to be found and fixed twice, and this project has already paid that cost - the $?-pollution defect in the same family of readers was fixed through two unrelated ticket series before a standing sweep was added to police it.
It exists as its own module rather than as a method on a base class because the callers share nothing else: they differ in timeout policy, in what a timeout does, in where the bytes go, and in where the exit status comes from. Only this step is common, and only this step is shared.
PageRuntime is deliberately NOT a consumer. It had already factored its own read out and guards EINTR, which this helper does not; importing this one would replace a correct drain with a defective one.
WHEN TO USE
Whenever a caller reads a child process's stdout or stderr through IO::Select and wants the established end-of-file handling. Callers keep their own loop, their own timeout policy and their own accounting.
HOW TO USE
use Developer::Dashboard::StreamDrain qw(_drain_ready_handle);
while ( my @ready = $selector->can_read ) {
for my $handle (@ready) {
my $chunk_ref = $self->_drain_ready_handle( $selector, $handle )
or next; # finished: already removed and closed
# ... route ${$chunk_ref} wherever this caller sends it ...
}
}
WHAT USES IT
Developer::Dashboard::SkillDispatcher and Developer::Dashboard::SkillManager.
EXAMPLES
Routing by file descriptor, as SkillDispatcher does when it tees a child's output through to the real STDOUT and STDERR:
my $chunk_ref = $self->_drain_ready_handle( $selector, $fh ) or next;
if ( fileno($fh) == $stdout_fd ) {
print STDOUT ${$chunk_ref};
$stdout_text .= ${$chunk_ref};
next;
}
Accumulating into a per-handle slot, as SkillManager does while reporting progress a line at a time:
my $chunk_ref = $self->_drain_ready_handle( $selector, $handle ) or next;
my $slot = $target_for{ fileno($handle) };
${$slot} .= ${$chunk_ref} if $slot;