From 85d13bc41737851b7d665a883188205a072a32ca Mon Sep 17 00:00:00 2001 From: beatles Date: Sat, 28 Jun 2008 12:56:32 +0000 Subject: [PATCH] - If a player, including a monitor, stuck at the time of sending messages to the wire, which means that the giant lock was locked, it might have prevented other players from processing until timeout occurred. This issue has been fixed. (Closes #12555) --- changelog | 4 ++++ shogi_server/player.rb | 62 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/changelog b/changelog index 2211823..244cc29 100644 --- a/changelog +++ b/changelog @@ -5,6 +5,10 @@ record file instead of after the last move, which allows other applications watching a game to recognize players in the middle of the game. (Closes #12821) + - If a player, including a monitor, stuck at the time of sending + messages to the wire, which means that the giant lock was locked, + it might have prevented other players from processing until + timeout occurred. This issue has been fixed. (Closes #12555) 2008-06-27 Daigo Moriwaki diff --git a/shogi_server/player.rb b/shogi_server/player.rb index c9777e1..b6d5404 100644 --- a/shogi_server/player.rb +++ b/shogi_server/player.rb @@ -110,7 +110,8 @@ class Player < BasicPlayer @sente = nil @socket_buffer = [] @main_thread = Thread::current - @mutex_write_guard = Mutex.new + @write_queue = Queue.new + start_write_thread end attr_accessor :socket, :status @@ -124,7 +125,8 @@ class Player < BasicPlayer @game.kill(self) end finish - Thread::kill(@main_thread) if @main_thread + Thread::kill(@main_thread) if @main_thread + Thread::kill(@write_thread) if @write_thread end def finish @@ -133,28 +135,50 @@ class Player < BasicPlayer log_message(sprintf("user %s finish", @name)) begin # @socket.close if (! @socket.closed?) + write_safe(nil) + @write_thread.join rescue log_message(sprintf("user %s finish failed", @name)) end end end - def write_safe(str) - @mutex_write_guard.synchronize do - begin - if @socket.closed? - log_warning("%s's socket has been closed." % [@name]) - return - end - if r = select(nil, [@socket], nil, 20) - r[1].first.write(str) - else - log_error("Sending a message to #{@name} timed up.") + def start_write_thread + @write_thread = Thread.start do + Thread.pass + while !@socket.closed? + str = "" + begin + begin + timeout(5) do + str = @write_queue.deq + end + if str == nil + break + end + rescue TimeoutError + next + end + if r = select(nil, [@socket], nil, 20) + r[1].first.write(str) + else + log_error("Sending a message to #{@name} timed up.") + end + rescue Exception => ex + log_error("Failed to send a message to #{@name}. #{ex.class}: #{ex.message}\t#{ex.backtrace[0]}") end - rescue Exception => ex - log_error("Failed to send a message to #{@name}. #{ex.class}: #{ex.message}\t#{ex.backtrace[0]}") - end - end + end # while loop + log_message("terminated %s's write thread" % [@name]) + end # thread + rescue + + end + + # + # Note that sending a message is included in the giant lock. + # + def write_safe(str) + @write_queue.enq str end def to_s @@ -176,6 +200,10 @@ class Player < BasicPlayer str = gets_safe(@socket, (@socket_buffer.empty? ? Default_Timeout : 1)) ) $mutex.lock begin + if !@write_thread.alive? + log_error("%s's write thread is dead. Aborting..." % [@name]) + return + end if (@game && @game.turn?(self)) @socket_buffer << str str = @socket_buffer.shift -- 2.11.0