Refactor SelectHandler to use PBot::IRC's select loop

This commit is contained in:
Pragmatic Software 2021-06-20 20:31:47 -07:00
parent 8b093d313c
commit 06d986e11e
2 changed files with 56 additions and 64 deletions

View File

@ -100,7 +100,7 @@ sub initialize {
my ($item, $value) = split /=/, $arg, 2;
if (not defined $item or not defined $value) {
print STDERR "Fatal error: unknown argument `$arg`; arguments must be in the form of `section.key=value` (e.g.: irc.botnick=newnick)\n";
print STDERR "Fatal error: unknown argument `$arg`; arguments must be in the form of `section.key=value` or `path_dir=value` (e.g.: irc.botnick=newnick or data_dir=path)\n";
exit;
}
@ -174,8 +174,11 @@ sub initialize {
exit;
}
# prepare the IRC engine
$self->{irc} = PBot::IRC->new;
# prepare remaining core PBot modules -- do not change this order
$self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf);
$self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf);
$self->{event_dispatcher} = PBot::EventDispatcher->new(pbot => $self, %conf);
$self->{users} = PBot::Users->new(pbot => $self, filename => "$conf{data_dir}/users", %conf);
$self->{antiflood} = PBot::AntiFlood->new(pbot => $self, %conf);
@ -188,7 +191,6 @@ sub initialize {
$self->{functions} = PBot::Functions->new(pbot => $self, %conf);
$self->{refresher} = PBot::Refresher->new(pbot => $self);
$self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => "$conf{data_dir}/ignorelist", %conf);
$self->{irc} = PBot::IRC->new();
$self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self, %conf);
$self->{interpreter} = PBot::Interpreter->new(pbot => $self, %conf);
$self->{lagchecker} = PBot::LagChecker->new(pbot => $self, %conf);
@ -332,16 +334,17 @@ sub exit {
# main loop
sub do_one_loop {
my $self = shift;
$self->{irc}->do_one_loop() if $self->{connected};
$self->{select_handler}->do_select;
my ($self) = @_;
$self->{irc}->do_one_loop();
}
# main entry point
sub start {
my $self = shift;
$self->connect;
while (1) {
$self->connect if not $self->{connected};
$self->do_one_loop;
}
}

View File

@ -1,6 +1,7 @@
# File: SelectHandler.pm
#
# Purpose: Invokes select() system call and handles its events.
# Purpose: Adds/removes file handles to/from PBot::IRC's select loop
# and contains handlers for select events.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
@ -11,87 +12,75 @@ use parent 'PBot::Class';
use PBot::Imports;
use IO::Select;
sub initialize {
my ($self, %conf) = @_;
$self->{select} = IO::Select->new();
$self->{readers} = {};
$self->{buffers} = {};
# nothing to initialize
}
sub add_reader {
my ($self, $handle, $subref) = @_;
$self->{select}->add($handle);
$self->{readers}->{$handle} = $subref;
# add file handle to PBot::IRC's select loop
$self->{pbot}->{irc}->addfh($handle, sub { $self->on_select_read($handle, $subref) }, 'r');
# create read buffer for this handle
$self->{buffers}->{$handle} = '';
}
sub remove_reader {
my ($self, $handle) = @_;
$self->{select}->remove($handle);
delete $self->{readers}->{$handle};
# remove file handle from PBot::IRC's select loop
$self->{pbot}->{irc}->removefh($handle);
# delete this handle's read buffer
delete $self->{buffers}->{$handle};
}
sub do_select {
my ($self) = @_;
sub on_select_read {
my ($self, $handle, $subref) = @_;
# maximum read length
my $length = 8192;
# check if any readers can read
my @ready = $self->{select}->can_read(.1);
# read from handle
my $ret = sysread($handle, my $buf, $length);
foreach my $fh (@ready) {
# read from handle
my $ret = sysread($fh, my $buf, $length);
# error reading
if (not defined $ret) {
$self->{pbot}->{logger}->log("SelectHandler: Error reading $handle: $!\n");
$self->remove_reader($handle);
return;
}
# error reading
if (not defined $ret) {
$self->{pbot}->{logger}->log("SelectHandler: Error reading $fh: $!\n");
$self->remove_reader($fh);
next;
# reader closed
if ($ret == 0) {
# is there anything in reader's buffer?
if (length $self->{buffers}->{$handle}) {
# send buffer to reader subref
$self->{readers}->{$handle}->($self->{buffers}->{$handle});
}
# reader closed
if ($ret == 0) {
# is there anything in reader's buffer?
if (length $self->{buffers}->{$fh}) {
# send buffer to reader subref
$self->{readers}->{$fh}->($self->{buffers}->{$fh});
}
# remove reader
$self->remove_reader($handle);
return;
}
# remove reader
$self->remove_reader($fh);
# accumulate input into reader's buffer
$self->{buffers}->{$handle} .= $buf;
# skip to next reader
next;
}
# if we read less than max length bytes then this is probably
# a complete message so send it to reader now, otherwise we'll
# continue to accumulate input into reader's buffer and then send
# the buffer when reader closes.
#
# FIXME: this should be line-based or some protocol.
# sanity check for missing reader
if (not exists $self->{readers}->{$fh}) {
$self->{pbot}->{logger}->log("Error: no reader for $fh\n");
if ($ret < $length) {
# send reader's buffer to reader's consumer subref
$subref->($self->{buffers}->{$handle});
# skip to next reader
next;
}
# accumulate input into reader's buffer
$self->{buffers}->{$fh} .= $buf;
# if we read less than max length bytes then this is probably
# a complete message so send it to reader now, otherwise we'll
# continue to accumulate input into reader's buffer and then send
# the buffer when reader closes.
if ($ret < $length) {
# send reader's buffer to reader subref
$self->{readers}->{$fh}->($self->{buffers}->{$fh});
# clear out reader's buffer
$self->{buffers}->{$fh} = '';
}
# clear out reader's buffer
$self->{buffers}->{$handle} = '';
}
}