• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

shogi-server source


Commit MetaInfo

修订版57e00845d62e520576f71c7ed968af274cc5da50 (tree)
时间2008-10-02 00:03:50
作者beatles <beatles@b8c6...>
Commiterbeatles

Log Message

Under debugging

更改概述

差异

--- a/shogi-server
+++ b/shogi-server
@@ -20,6 +20,9 @@
2020
2121 $:.unshift File.dirname(__FILE__)
2222 require 'shogi_server'
23+require 'shogi_server/board' # not autoloaded
24+require 'shogi_server/player'
25+require 'shogi_server/game'
2326
2427 #################################################
2528 # MAIN
@@ -32,7 +35,7 @@ def gets_safe(socket, timeout=nil)
3235 return :timeout
3336 end
3437 rescue Exception => ex
35- log_error("#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}")
38+ log_error("gets_safe: #{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}")
3639 return :exception
3740 end
3841
@@ -113,20 +116,21 @@ def write_pid_file(file)
113116 end
114117
115118 def mutex_watchdog(mutex, sec)
119+ sec = 1 if sec < 1
120+ queue = []
116121 while true
117- begin
118- timeout(sec) do
119- begin
120- mutex.lock
121- ensure
122- mutex.unlock
123- end
122+ if mutex.try_lock
123+ queue.clear
124+ mutex.unlock
125+ else
126+ queue.push(Object.new)
127+ if queue.size > sec
128+ # timeout
129+ log_error("mutex watchdog timeout: %d sec" % [sec])
130+ queue.clear
124131 end
125- sleep(sec)
126- rescue TimeoutError
127- log_error("mutex watchdog timeout")
128- exit(1)
129132 end
133+ sleep(1)
130134 end
131135 end
132136
@@ -182,7 +186,40 @@ def setup_watchdog_for_giant_lock
182186 end
183187 end
184188
189+def setup_floodgate
190+ Thread.start do
191+ Thread.pass
192+ floodgate = ShogiServer::League::Floodgate.new(LEAGUE)
193+ log_message("Flooddgate reloaded. The next match will start at %s." %
194+ [floodgate.next_time])
195+
196+ while (true)
197+ begin
198+ sleep(10)
199+ next if Time.now < floodgate.next_time
200+ LEAGUE.reload
201+ floodgate.match_game
202+ floodgate.charge
203+ next_time = floodgate.next_time
204+ $mutex.synchronize do
205+ Dependencies.clear
206+ end
207+ floodgate = ShogiServer::League::Floodgate.new(LEAGUE, next_time)
208+ log_message("Flooddgate reloaded. The next match will start at %s." %
209+ [floodgate.next_time])
210+ rescue Exception => ex
211+ # ignore errors
212+ log_error("[in Floodgate's thread] #{ex} #{ex.backtrace}")
213+ end
214+ end
215+ end
216+end
217+
185218 def main
219+
220+ [ShogiServer::League::Floodgate, ShogiServer::Pairing].each do |klass|
221+ Dependencies.unloadable klass
222+ end
186223
187224 setup_watchdog_for_giant_lock
188225
@@ -219,12 +256,16 @@ def main
219256 config[:Port] = port
220257 config[:ServerType] = WEBrick::Daemon if $options["daemon"]
221258 config[:Logger] = $logger
222- if $options["pid-file"]
223- pid_file = File.expand_path($options["pid-file"])
224- config[:StartCallback] = Proc.new do
225- write_pid_file(pid_file)
259+
260+ config[:StartCallback] = Proc.new do
261+ if $options["pid-file"]
262+ write_pid_file($options["pid-file"])
226263 end
227- config[:StopCallback] = Proc.new do
264+ setup_floodgate
265+ end
266+
267+ config[:StopCallback] = Proc.new do
268+ if $options["pid-file"]
228269 FileUtils.rm(pid_file, :force => true)
229270 end
230271 end
@@ -234,12 +275,13 @@ def main
234275 trap(signal) do
235276 LEAGUE.shutdown
236277 server.shutdown
278+ # TODO Shutdown Floodgate's thread
237279 end
238280 end
239281 trap("HUP") do
240282 LEAGUE.shutdown
241283 Dependencies.clear
242- LEAGUE.restart
284+ # TODO Restart Floodgate's thread
243285 end
244286 $stderr.puts("server started as a deamon [Revision: #{ShogiServer::Revision}]") if $options["daemon"]
245287 log_message("server started [Revision: #{ShogiServer::Revision}]")
@@ -276,6 +318,10 @@ if ($0 == __FILE__)
276318 TCPSocket.do_not_reverse_lookup = true
277319 Thread.abort_on_exception = $DEBUG ? true : false
278320
321+ begin
279322 LEAGUE = ShogiServer::League::new
280- main
323+ main
324+ rescue Exception => ex
325+ log_error("main: #{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}")
326+ end
281327 end
--- a/shogi_server/board.rb
+++ b/shogi_server/board.rb
@@ -20,19 +20,36 @@
2020 module ShogiServer # for a namespace
2121
2222 class Board
23- def initialize
23+ def initialize(move_count=0)
2424 @sente_hands = Array::new
2525 @gote_hands = Array::new
2626 @history = Hash::new(0)
2727 @sente_history = Hash::new(0)
2828 @gote_history = Hash::new(0)
2929 @array = [[], [], [], [], [], [], [], [], [], []]
30- @move_count = 0
30+ @move_count = move_count
3131 @teban = nil # black => true, white => false
3232 end
3333 attr_accessor :array, :sente_hands, :gote_hands, :history, :sente_history, :gote_history, :teban
3434 attr_reader :move_count
3535
36+ def deep_copy
37+ # return Marshal.load(Marshal.dump(self))
38+ board = Board.new(self.move_count)
39+ board.sente_hands = self.sente_hands.clone
40+ board.gote_hands = self.gote_hands.clone
41+ board.history = self.history.clone
42+ board.history.default = 0
43+ board.sente_history = self.sente_history.clone
44+ board.sente_history.default = 0
45+ board.gote_history = self.gote_history.clone
46+ board.gote_history.default = 0
47+ board.array = []
48+ self.array.each {|a| board.array.push(a.clone)}
49+ board.teban = self.teban
50+ return board
51+ end
52+
3653 def initial
3754 PieceKY::new(self, 1, 1, false)
3855 PieceKE::new(self, 2, 1, false)
@@ -82,10 +99,12 @@ class Board
8299
83100 if ((x0 == 0) || (y0 == 0))
84101 piece = have_piece?(hands, name)
85- return :illegal if (! piece.move_to?(x1, y1, name)) # TODO null check for the piece?
102+ return :illegal if (piece == nil || ! piece.move_to?(x1, y1, name))
86103 piece.move_to(x1, y1)
87104 else
88- return :illegal if (! @array[x0][y0].move_to?(x1, y1, name)) # TODO null check?
105+ if (@array[x0][y0] == nil || !@array[x0][y0].move_to?(x1, y1, name))
106+ return :illegal
107+ end
89108 if (@array[x0][y0].name != name) # promoted ?
90109 @array[x0][y0].promoted = true
91110 end
@@ -169,7 +188,7 @@ class Board
169188
170189 ## case: rival_ou is moving
171190 rival_ou.movable_grids.each do |(cand_x, cand_y)|
172- tmp_board = Marshal.load(Marshal.dump(self))
191+ tmp_board = deep_copy
173192 s = tmp_board.move_to(rival_ou.x, rival_ou.y, cand_x, cand_y, "OU", ! sente)
174193 raise "internal error" if (s != true)
175194 if (! tmp_board.checkmated?(! sente)) # good move
@@ -197,7 +216,7 @@ class Board
197216 end
198217 end
199218 names.map! do |name|
200- tmp_board = Marshal.load(Marshal.dump(self))
219+ tmp_board = deep_copy
201220 s = tmp_board.move_to(x, y, fu_x, fu_y, name, ! sente)
202221 if s == :illegal
203222 s # result
@@ -380,7 +399,7 @@ class Board
380399 return :illegal # can't put on existing piece
381400 end
382401
383- tmp_board = Marshal.load(Marshal.dump(self))
402+ tmp_board = deep_copy
384403 return :illegal if (tmp_board.move_to(x0, y0, x1, y1, name, sente) == :illegal)
385404 return :oute_kaihimore if (tmp_board.checkmated?(sente))
386405 tmp_board.update_sennichite(sente)
--- a/shogi_server/game.rb
+++ b/shogi_server/game.rb
@@ -364,6 +364,7 @@ class Game
364364 end
365365
366366 move_status = @board.handle_one_move(str, @sente == @current_player)
367+ # log_debug("move_status: %s for %s's %s" % [move_status, @sente == @current_player ? "BLACK" : "WHITE", str])
367368
368369 if [:illegal, :uchifuzume, :oute_kaihimore].include?(move_status)
369370 @fh.printf("'ILLEGAL_MOVE(%s)\n", str)
--- a/shogi_server/league.rb
+++ b/shogi_server/league.rb
@@ -17,9 +17,6 @@
1717 ## along with this program; if not, write to the Free Software
1818 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1919
20-#require 'shogi_server/league/floodgate'
21-#require 'shogi_server/league/persistent'
22-
2320 module ShogiServer # for a namespace
2421
2522 ######################################################
@@ -33,8 +30,6 @@ class League
3330 @players = Hash::new
3431 @event = nil
3532 @dir = File.dirname(__FILE__)
36- @floodgate = Floodgate.new(self)
37- #@floodgate.run
3833 end
3934 attr_accessor :players, :games, :event, :dir
4035
@@ -44,12 +39,6 @@ class League
4439 @persistent.save(player)
4540 end
4641 end
47- @floodgate.shutdown
48- end
49-
50- def restart
51- @floodgate = Floodgate.new(self)
52- #@floodgate.run
5342 end
5443
5544 # this should be called just after instanciating a League object.
--- a/shogi_server/league/floodgate.rb
+++ b/shogi_server/league/floodgate.rb
@@ -8,48 +8,30 @@ class League
88 end
99 end
1010
11- def initialize(league)
11+ attr_reader :next_time, :league
12+
13+ def initialize(league, next_time=nil)
1214 @league = league
13- @next_time = nil
15+ @next_time = next_time
1416 charge
1517 end
1618
17- def run
18- @thread = Thread.new do
19- Thread.pass
20- while (true)
21- begin
22- sleep(10)
23- next if Time.now < @next_time
24- @league.reload
25- match_game
26- charge
27- rescue Exception => ex
28- # ignore errors
29- log_error("[in Floodgate's thread] #{ex} #{ex.backtrace}")
30- end
31- end
32- end
33- end
34-
35- def shutdown
36- @thread.kill if @thread
37- end
38-
39- # private
40-
4119 def charge
4220 now = Time.now
43- # if now.min < 30
44- # @next_time = Time.mktime(now.year, now.month, now.day, now.hour, 30)
45- # else
46- # @next_time = Time.mktime(now.year, now.month, now.day, now.hour) + 3600
47- # end
48- # for test
49- if now.sec < 30
50- @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min, 30)
21+ unless $DEBUG
22+ # each 30 minutes
23+ if now.min < 30
24+ @next_time = Time.mktime(now.year, now.month, now.day, now.hour, 30)
25+ else
26+ @next_time = Time.mktime(now.year, now.month, now.day, now.hour) + 3600
27+ end
5128 else
52- @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min) + 60
29+ # for test, each 30 seconds
30+ if now.sec < 30
31+ @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min, 30)
32+ else
33+ @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min) + 60
34+ end
5335 end
5436 end
5537
--- a/shogi_server/player.rb
+++ b/shogi_server/player.rb
@@ -17,6 +17,8 @@
1717 ## along with this program; if not, write to the Free Software
1818 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1919
20+require 'monitor'
21+
2022 module ShogiServer # for a namespace
2123
2224 class BasicPlayer
@@ -110,7 +112,9 @@ class Player < BasicPlayer
110112 @sente = nil
111113 @socket_buffer = []
112114 @main_thread = Thread::current
113- @write_queue = Queue.new
115+ @write_queue = []
116+ @wq_mon = Monitor.new
117+ @wq_cond = @wq_mon.new_cond
114118 @player_logger = nil
115119 start_write_thread
116120 end
@@ -186,16 +190,23 @@ class Player < BasicPlayer
186190 while !@socket.closed?
187191 str = ""
188192 begin
189- begin
190- timeout(5) do
191- str = @write_queue.deq
193+ timeout_flg = false
194+ @wq_mon.synchronize do
195+ if @write_queue.empty?
196+ if @wq_cond.wait(15)
197+ #timeout
198+ timeout_flg = true
199+ # log_debug("write_queue health check timeout")
200+ end
192201 end
193- if str == nil
194- break
202+ if !timeout_flg && !@write_queue.empty?
203+ str = @write_queue.shift
204+ # log_debug("Dequeued %s from %s's write_queue." % [str, @name])
195205 end
196- rescue TimeoutError
197- next
198- end
206+ end # synchronize
207+ next if timeout_flg
208+ break if str == nil # exit
209+
199210 if r = select(nil, [@socket], nil, 20)
200211 r[1].first.write(str)
201212 log(:info, :out, str)
@@ -216,7 +227,12 @@ class Player < BasicPlayer
216227 # Note that sending a message is included in the giant lock.
217228 #
218229 def write_safe(str)
219- @write_queue.enq str
230+ @wq_mon.synchronize do
231+ # log_debug("Enqueuing %s..." % [str])
232+ @write_queue.push(str)
233+ # log_debug("Enqueued %s into %s's write_queue." % [str, @name])
234+ @wq_cond.broadcast
235+ end
220236 end
221237
222238 def to_s
--- /dev/null
+++ b/shogi_server/timeout_queue.rb
@@ -0,0 +1,84 @@
1+# queue = Queue.new
2+# timeout(5) do
3+# queue.deq
4+# end
5+#
6+# is not good since not all of stdlib is safe with respect to
7+# asynchronous exceptions.
8+# This class is a safe implementation.
9+# See: http://www.ruby-forum.com/topic/107864
10+#
11+
12+require 'thread'
13+require 'monitor'
14+
15+module ShogiServer
16+
17+class TimeoutQueue
18+ def initialize
19+ @lock = Mutex.new
20+ @messages = []
21+ @readers = []
22+ end
23+
24+ def enq(msg)
25+ @lock.synchronize do
26+ unless @readers.empty?
27+ @readers.pop << msg
28+ else
29+ @messages.push msg
30+ end
31+ end
32+ end
33+
34+ #
35+ # @param timeout
36+ # @return nil if timeout
37+ #
38+ def deq(timeout=5)
39+ timeout_thread = nil
40+ mon = nil
41+ empty_cond = nil
42+
43+ begin
44+ reader = nil
45+ @lock.synchronize do
46+ unless @messages.empty?
47+ # fast path
48+ return @messages.shift
49+ else
50+ reader = Queue.new
51+ @readers.push reader
52+ if timeout
53+ mon = Monitor.new
54+ empty_cond = mon.new_cond
55+
56+ timeout_thread = Thread.new do
57+ mon.synchronize do
58+ if empty_cond.wait(timeout)
59+ # timeout
60+ @lock.synchronize do
61+ @readers.delete reader
62+ reader << nil
63+ end
64+ else
65+ # timeout_thread was waked up before timeout
66+ end
67+ end
68+ end # thread
69+ end
70+ end
71+ end
72+ # either timeout or writer will send to us
73+ return reader.shift
74+ ensure
75+ # (try to) clean up timeout thread
76+ if timeout_thread
77+ mon.synchronize { empty_cond.signal }
78+ Thread.pass
79+ end
80+ end
81+ end
82+end
83+
84+end