OSDN Git Service

* [shogi-server] Support a graceful shutdown. (Closes #38544)
[shogi-server/shogi-server.git] / shogi_server / league / floodgate_thread.rb
1 require 'shogi_server'
2 require 'shogi_server/league/floodgate'
3
4 module ShogiServer
5
6   class SetupFloodgate
7     # Constructor.
8     # @param game_names an array of game name strings
9     #
10     def initialize(game_names)
11       @game_names = game_names
12       @thread = nil
13     end
14
15     # Return the most recent Floodgate instance
16     #
17     def next_league(leagues)
18       floodgate = leagues.min {|a,b| a.next_time <=> b.next_time}
19       return floodgate
20     end
21
22     def floodgate_reload_log(leagues)
23       floodgate = next_league(leagues)
24       diff = floodgate.next_time - Time.now
25       log_message("Floodgate reloaded. The next match will start at %s in %d seconds" % 
26                   [floodgate.next_time, diff])
27     end
28
29     def mk_leagues
30       leagues = @game_names.collect do |game_name|
31         ShogiServer::League::Floodgate.new($league, 
32                                            {:game_name => game_name})
33       end
34       leagues.delete_if do |floodgate|
35         ret = false
36         unless floodgate.next_time 
37           log_error("Unsupported game name: %s" % floodgate.game_name)
38           ret = true
39         end
40         ret
41       end
42       if leagues.empty?
43         log_error("No valid Floodgate game names found")
44         return [] # will exit the thread
45       end
46       floodgate_reload_log(leagues)
47       return leagues
48     end
49
50     def wait_next_floodgate(floodgate)
51       diff = floodgate.next_time - Time.now
52       if diff > 0
53         sleep(diff/2)
54         return true
55       end
56       return false
57     end
58
59     def reload_shogi_server
60       $mutex.synchronize do
61         log_message("Reloading source...")
62         ShogiServer.reload
63       end
64     end
65
66     def start_games(floodgate)
67       $league.reload
68       floodgate.match_game
69     end
70
71     # Regenerate floodgate instances from next_instances for the next matches.
72     # @param next_instances array of [game_name, next_time]
73     #
74     def regenerate_leagues(next_instances)
75       leagues = next_instances.collect do |prev|
76         log_message("Regenerating a floodgate league...: %s %s %s %s %d %d" %
77                     [prev.game_name, prev.next_time, prev.pairing_factory, prev.sacrifice, prev.max_moves, prev.least_time_per_move])
78         floodgate = ShogiServer::League::Floodgate.new($league, 
79                       {:game_name       => prev.game_name,       :next_time => prev.next_time,
80                        :pairing_factory => prev.pairing_factory, :sacrifice => prev.sacrifice,
81                        :max_moves       => prev.max_moves,       :least_time_per_move => prev.least_time_per_move})
82         floodgate
83       end
84       floodgate_reload_log(leagues)
85       return leagues
86     end
87
88     def start
89       return nil if @game_names.nil? || @game_names.empty?
90
91       log_message("Set up floodgate games: %s" % [@game_names.join(",")])
92       @thread = Thread.start(@game_names) do |game_names|
93         Thread.pass
94         leagues = mk_leagues
95         if leagues.nil? || leagues.empty?
96           return # exit from this thread
97         end
98
99         while (true)
100           begin
101             floodgate = next_league(leagues)
102             next if wait_next_floodgate(floodgate)
103
104             next_instances = leagues.collect do |fg|
105               unless (fg.next_time - Time.now) > 0
106                 if ShogiServer::available?
107                   start_games(fg)
108                 else
109                   log_message("The STOP file prevents from starting new Floodgate matches")
110                 end
111                 fg.charge # updates next_time
112               end
113               fg
114             end
115
116             reload_shogi_server
117
118             # Regenerate floodgate instances after ShogiServer.realod
119             leagues = regenerate_leagues(next_instances)
120           rescue Exception => ex 
121             # ignore errors
122             log_error("[in Floodgate's thread] #{ex} #{ex.backtrace}")
123           end
124         end # infinite loop
125       end # Thread
126
127       return @thread
128     end # def start
129
130     def kill
131       @thread.kill if @thread
132     end
133
134   end # class SetupFloodgate
135
136 end # module ShogiServer