From 7598bc6f7529b746f09262c6f0553cce796e2f18 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 15 Feb 2011 22:18:52 +0100 Subject: [PATCH] ircsocket: don't shutdown on error Instead, signal the faulty state and re-raise on the next select. This is to improve the bot behavior if the socket error happens in a separate thread while the main loop is still processing input. Such a situation can be given for example during a netsplit. In some cases (e.g. the bot is on many channels) the processing of the netsplit events can take a long time, so that the server will disconnect the bot for ping timeout. While the bot has not realized it has been disconnected (not got to the :ERROR message from the server yet, and the socket still has (buffered) data), some action (e.g. an rss update) causes the bot to try to talk to the server, resulting in a broken pipe. At this point, with the old code, the socket would be disconnected without the bot realizing it, causing it to fail to reconnect. --- lib/rbot/ircsocket.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/rbot/ircsocket.rb b/lib/rbot/ircsocket.rb index 652b1f6d..7bc06fe6 100644 --- a/lib/rbot/ircsocket.rb +++ b/lib/rbot/ircsocket.rb @@ -276,6 +276,7 @@ module Irc # create a new Irc::Socket def initialize(server_list, host, opts={}) @server_list = server_list.dup + @error = nil @server_uri = nil @conn_count = 0 @host = host @@ -333,6 +334,7 @@ module Irc @sock.extend(MonitorMixin) @sendq = MessageQueue.new @qthread = Thread.new { writer_loop } + @error = nil end # used to send lines to the remote IRCd by skipping the queue @@ -347,11 +349,11 @@ module Irc end end + # when an error occurs, keep in mind that it happened and raise, + # but wait for the main thread to take the appropriate action def handle_socket_error(string, e) + @error = e error "#{string} failed: #{e.pretty_inspect}" - # We assume that an error means that there are connection - # problems and that we should reconnect, so we - shutdown raise SocketError.new(e.inspect) end @@ -385,9 +387,15 @@ module Irc @sock.flush end - # Wraps Kernel.select on the socket + # Wraps Kernel.select on the socket. If there is nothing pending, + # but the socket is in error status, raise an appropriate SocketError. + # This allows us to trap write errors that happened from a separate + # thread, but manage them from the main loop def select(timeout=nil) - Kernel.select([@sock], nil, nil, timeout) + sel = Kernel.select([@sock], nil, nil, timeout) + return sel if sel + raise SocketError.new(@error.inspect) if @error + return nil end # shutdown the connection to the server -- 2.32.0.93.g670b81a890