Rev 96 | Rev 169 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
96 | pmbaty | 1 | /* |
2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 |
||
3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) |
||
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
||
5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
||
6 | |||
7 | Stockfish is free software: you can redistribute it and/or modify |
||
8 | it under the terms of the GNU General Public License as published by |
||
9 | the Free Software Foundation, either version 3 of the License, or |
||
10 | (at your option) any later version. |
||
11 | |||
12 | Stockfish is distributed in the hope that it will be useful, |
||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | GNU General Public License for more details. |
||
16 | |||
17 | You should have received a copy of the GNU General Public License |
||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
19 | */ |
||
20 | |||
21 | #include <algorithm> // For std::count |
||
22 | #include <cassert> |
||
23 | |||
24 | #include "movegen.h" |
||
25 | #include "search.h" |
||
26 | #include "thread.h" |
||
27 | #include "uci.h" |
||
154 | pmbaty | 28 | #include "syzygy/tbprobe.h" |
96 | pmbaty | 29 | |
30 | ThreadPool Threads; // Global object |
||
31 | |||
32 | /// Thread constructor launches the thread and then waits until it goes to sleep |
||
33 | /// in idle_loop(). |
||
34 | |||
35 | Thread::Thread() { |
||
36 | |||
37 | resetCalls = exit = false; |
||
38 | maxPly = callsCnt = 0; |
||
154 | pmbaty | 39 | tbHits = 0; |
96 | pmbaty | 40 | history.clear(); |
41 | counterMoves.clear(); |
||
42 | idx = Threads.size(); // Start from 0 |
||
43 | |||
44 | std::unique_lock<Mutex> lk(mutex); |
||
45 | searching = true; |
||
46 | nativeThread = std::thread(&Thread::idle_loop, this); |
||
47 | sleepCondition.wait(lk, [&]{ return !searching; }); |
||
48 | } |
||
49 | |||
50 | |||
51 | /// Thread destructor waits for thread termination before returning |
||
52 | |||
53 | Thread::~Thread() { |
||
54 | |||
55 | mutex.lock(); |
||
56 | exit = true; |
||
57 | sleepCondition.notify_one(); |
||
58 | mutex.unlock(); |
||
59 | nativeThread.join(); |
||
60 | } |
||
61 | |||
62 | |||
63 | /// Thread::wait_for_search_finished() waits on sleep condition |
||
64 | /// until not searching |
||
65 | |||
66 | void Thread::wait_for_search_finished() { |
||
67 | |||
68 | std::unique_lock<Mutex> lk(mutex); |
||
69 | sleepCondition.wait(lk, [&]{ return !searching; }); |
||
70 | } |
||
71 | |||
72 | |||
73 | /// Thread::wait() waits on sleep condition until condition is true |
||
74 | |||
75 | void Thread::wait(std::atomic_bool& condition) { |
||
76 | |||
77 | std::unique_lock<Mutex> lk(mutex); |
||
78 | sleepCondition.wait(lk, [&]{ return bool(condition); }); |
||
79 | } |
||
80 | |||
81 | |||
82 | /// Thread::start_searching() wakes up the thread that will start the search |
||
83 | |||
84 | void Thread::start_searching(bool resume) { |
||
85 | |||
86 | std::unique_lock<Mutex> lk(mutex); |
||
87 | |||
88 | if (!resume) |
||
89 | searching = true; |
||
90 | |||
91 | sleepCondition.notify_one(); |
||
92 | } |
||
93 | |||
94 | |||
95 | /// Thread::idle_loop() is where the thread is parked when it has no work to do |
||
96 | |||
97 | void Thread::idle_loop() { |
||
98 | |||
99 | while (!exit) |
||
100 | { |
||
101 | std::unique_lock<Mutex> lk(mutex); |
||
102 | |||
103 | searching = false; |
||
104 | |||
105 | while (!searching && !exit) |
||
106 | { |
||
107 | sleepCondition.notify_one(); // Wake up any waiting thread |
||
108 | sleepCondition.wait(lk); |
||
109 | } |
||
110 | |||
111 | lk.unlock(); |
||
112 | |||
113 | if (!exit) |
||
114 | search(); |
||
115 | } |
||
116 | } |
||
117 | |||
118 | |||
119 | /// ThreadPool::init() creates and launches requested threads that will go |
||
120 | /// immediately to sleep. We cannot use a constructor because Threads is a |
||
121 | /// static object and we need a fully initialized engine at this point due to |
||
122 | /// allocation of Endgames in the Thread constructor. |
||
123 | |||
124 | void ThreadPool::init() { |
||
125 | |||
126 | push_back(new MainThread); |
||
127 | read_uci_options(); |
||
128 | } |
||
129 | |||
130 | |||
131 | /// ThreadPool::exit() terminates threads before the program exits. Cannot be |
||
132 | /// done in destructor because threads must be terminated before deleting any |
||
133 | /// static objects while still in main(). |
||
134 | |||
135 | void ThreadPool::exit() { |
||
136 | |||
137 | while (size()) |
||
138 | delete back(), pop_back(); |
||
139 | } |
||
140 | |||
141 | |||
142 | /// ThreadPool::read_uci_options() updates internal threads parameters from the |
||
143 | /// corresponding UCI options and creates/destroys threads to match requested |
||
144 | /// number. Thread objects are dynamically allocated. |
||
145 | |||
146 | void ThreadPool::read_uci_options() { |
||
147 | |||
148 | size_t requested = Options["Threads"]; |
||
149 | |||
150 | assert(requested > 0); |
||
151 | |||
152 | while (size() < requested) |
||
153 | push_back(new Thread); |
||
154 | |||
155 | while (size() > requested) |
||
156 | delete back(), pop_back(); |
||
157 | } |
||
158 | |||
159 | |||
160 | /// ThreadPool::nodes_searched() returns the number of nodes searched |
||
161 | |||
154 | pmbaty | 162 | uint64_t ThreadPool::nodes_searched() const { |
96 | pmbaty | 163 | |
154 | pmbaty | 164 | uint64_t nodes = 0; |
96 | pmbaty | 165 | for (Thread* th : *this) |
166 | nodes += th->rootPos.nodes_searched(); |
||
167 | return nodes; |
||
168 | } |
||
169 | |||
170 | |||
154 | pmbaty | 171 | /// ThreadPool::tb_hits() returns the number of TB hits |
172 | |||
173 | uint64_t ThreadPool::tb_hits() const { |
||
174 | |||
175 | uint64_t hits = 0; |
||
176 | for (Thread* th : *this) |
||
177 | hits += th->tbHits; |
||
178 | return hits; |
||
179 | } |
||
180 | |||
181 | |||
96 | pmbaty | 182 | /// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop() |
183 | /// and starts a new search, then returns immediately. |
||
184 | |||
154 | pmbaty | 185 | void ThreadPool::start_thinking(Position& pos, StateListPtr& states, |
186 | const Search::LimitsType& limits) { |
||
96 | pmbaty | 187 | |
188 | main()->wait_for_search_finished(); |
||
189 | |||
154 | pmbaty | 190 | Search::Signals.stopOnPonderhit = Search::Signals.stop = false; |
191 | Search::Limits = limits; |
||
192 | Search::RootMoves rootMoves; |
||
96 | pmbaty | 193 | |
194 | for (const auto& m : MoveList<LEGAL>(pos)) |
||
195 | if ( limits.searchmoves.empty() |
||
196 | || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) |
||
154 | pmbaty | 197 | rootMoves.push_back(Search::RootMove(m)); |
96 | pmbaty | 198 | |
154 | pmbaty | 199 | if (!rootMoves.empty()) |
200 | Tablebases::filter_root_moves(pos, rootMoves); |
||
201 | |||
202 | // After ownership transfer 'states' becomes empty, so if we stop the search |
||
203 | // and call 'go' again without setting a new position states.get() == NULL. |
||
204 | assert(states.get() || setupStates.get()); |
||
205 | |||
206 | if (states.get()) |
||
207 | setupStates = std::move(states); // Ownership transfer, states is now empty |
||
208 | |||
209 | StateInfo tmp = setupStates->back(); |
||
210 | |||
211 | for (Thread* th : Threads) |
||
212 | { |
||
213 | th->maxPly = 0; |
||
214 | th->tbHits = 0; |
||
215 | th->rootDepth = DEPTH_ZERO; |
||
216 | th->rootMoves = rootMoves; |
||
217 | th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); |
||
218 | } |
||
219 | |||
220 | setupStates->back() = tmp; // Restore st->previous, cleared by Position::set() |
||
221 | |||
96 | pmbaty | 222 | main()->start_searching(); |
223 | } |