Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/stockfish
Path: blob/master/src/thread.h
393 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifndef THREAD_H_INCLUDED
20
#define THREAD_H_INCLUDED
21
22
#include <atomic>
23
#include <condition_variable>
24
#include <cstddef>
25
#include <cstdint>
26
#include <functional>
27
#include <memory>
28
#include <mutex>
29
#include <vector>
30
31
#include "memory.h"
32
#include "numa.h"
33
#include "position.h"
34
#include "search.h"
35
#include "thread_win32_osx.h"
36
37
namespace Stockfish {
38
39
40
class OptionsMap;
41
using Value = int;
42
43
// Sometimes we don't want to actually bind the threads, but the recipient still
44
// needs to think it runs on *some* NUMA node, such that it can access structures
45
// that rely on NUMA node knowledge. This class encapsulates this optional process
46
// such that the recipient does not need to know whether the binding happened or not.
47
class OptionalThreadToNumaNodeBinder {
48
public:
49
OptionalThreadToNumaNodeBinder(NumaIndex n) :
50
numaConfig(nullptr),
51
numaId(n) {}
52
53
OptionalThreadToNumaNodeBinder(const NumaConfig& cfg, NumaIndex n) :
54
numaConfig(&cfg),
55
numaId(n) {}
56
57
NumaReplicatedAccessToken operator()() const {
58
if (numaConfig != nullptr)
59
return numaConfig->bind_current_thread_to_numa_node(numaId);
60
else
61
return NumaReplicatedAccessToken(numaId);
62
}
63
64
private:
65
const NumaConfig* numaConfig;
66
NumaIndex numaId;
67
};
68
69
// Abstraction of a thread. It contains a pointer to the worker and a native thread.
70
// After construction, the native thread is started with idle_loop()
71
// waiting for a signal to start searching.
72
// When the signal is received, the thread starts searching and when
73
// the search is finished, it goes back to idle_loop() waiting for a new signal.
74
class Thread {
75
public:
76
Thread(Search::SharedState&,
77
std::unique_ptr<Search::ISearchManager>,
78
size_t,
79
OptionalThreadToNumaNodeBinder);
80
virtual ~Thread();
81
82
void idle_loop();
83
void start_searching();
84
void clear_worker();
85
void run_custom_job(std::function<void()> f);
86
87
void ensure_network_replicated();
88
89
// Thread has been slightly altered to allow running custom jobs, so
90
// this name is no longer correct. However, this class (and ThreadPool)
91
// require further work to make them properly generic while maintaining
92
// appropriate specificity regarding search, from the point of view of an
93
// outside user, so renaming of this function is left for whenever that happens.
94
void wait_for_search_finished();
95
size_t id() const { return idx; }
96
97
LargePagePtr<Search::Worker> worker;
98
std::function<void()> jobFunc;
99
100
private:
101
std::mutex mutex;
102
std::condition_variable cv;
103
size_t idx, nthreads;
104
bool exit = false, searching = true; // Set before starting std::thread
105
NativeThread stdThread;
106
NumaReplicatedAccessToken numaAccessToken;
107
};
108
109
110
// ThreadPool struct handles all the threads-related stuff like init, starting,
111
// parking and, most importantly, launching a thread. All the access to threads
112
// is done through this class.
113
class ThreadPool {
114
public:
115
ThreadPool() {}
116
117
~ThreadPool() {
118
// destroy any existing thread(s)
119
if (threads.size() > 0)
120
{
121
main_thread()->wait_for_search_finished();
122
123
threads.clear();
124
}
125
}
126
127
ThreadPool(const ThreadPool&) = delete;
128
ThreadPool(ThreadPool&&) = delete;
129
130
ThreadPool& operator=(const ThreadPool&) = delete;
131
ThreadPool& operator=(ThreadPool&&) = delete;
132
133
void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType);
134
void run_on_thread(size_t threadId, std::function<void()> f);
135
void wait_on_thread(size_t threadId);
136
size_t num_threads() const;
137
void clear();
138
void set(const NumaConfig& numaConfig,
139
Search::SharedState,
140
const Search::SearchManager::UpdateContext&);
141
142
Search::SearchManager* main_manager();
143
Thread* main_thread() const { return threads.front().get(); }
144
uint64_t nodes_searched() const;
145
uint64_t tb_hits() const;
146
Thread* get_best_thread() const;
147
void start_searching();
148
void wait_for_search_finished() const;
149
150
std::vector<size_t> get_bound_thread_count_by_numa_node() const;
151
152
void ensure_network_replicated();
153
154
std::atomic_bool stop, abortedSearch, increaseDepth;
155
156
auto cbegin() const noexcept { return threads.cbegin(); }
157
auto begin() noexcept { return threads.begin(); }
158
auto end() noexcept { return threads.end(); }
159
auto cend() const noexcept { return threads.cend(); }
160
auto size() const noexcept { return threads.size(); }
161
auto empty() const noexcept { return threads.empty(); }
162
163
private:
164
StateListPtr setupStates;
165
std::vector<std::unique_ptr<Thread>> threads;
166
std::vector<NumaIndex> boundThreadToNumaNode;
167
168
uint64_t accumulate(std::atomic<uint64_t> Search::Worker::* member) const {
169
170
uint64_t sum = 0;
171
for (auto&& th : threads)
172
sum += (th->worker.get()->*member).load(std::memory_order_relaxed);
173
return sum;
174
}
175
};
176
177
} // namespace Stockfish
178
179
#endif // #ifndef THREAD_H_INCLUDED
180
181