Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/thread.h
376 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 "numa.h"
32
#include "position.h"
33
#include "search.h"
34
#include "thread_win32_osx.h"
35
36
namespace Stockfish {
37
38
39
class OptionsMap;
40
using Value = int;
41
42
// Sometimes we don't want to actually bind the threads, but the recipient still
43
// needs to think it runs on *some* NUMA node, such that it can access structures
44
// that rely on NUMA node knowledge. This class encapsulates this optional process
45
// such that the recipient does not need to know whether the binding happened or not.
46
class OptionalThreadToNumaNodeBinder {
47
public:
48
OptionalThreadToNumaNodeBinder(NumaIndex n) :
49
numaConfig(nullptr),
50
numaId(n) {}
51
52
OptionalThreadToNumaNodeBinder(const NumaConfig& cfg, NumaIndex n) :
53
numaConfig(&cfg),
54
numaId(n) {}
55
56
NumaReplicatedAccessToken operator()() const {
57
if (numaConfig != nullptr)
58
return numaConfig->bind_current_thread_to_numa_node(numaId);
59
else
60
return NumaReplicatedAccessToken(numaId);
61
}
62
63
private:
64
const NumaConfig* numaConfig;
65
NumaIndex numaId;
66
};
67
68
// Abstraction of a thread. It contains a pointer to the worker and a native thread.
69
// After construction, the native thread is started with idle_loop()
70
// waiting for a signal to start searching.
71
// When the signal is received, the thread starts searching and when
72
// the search is finished, it goes back to idle_loop() waiting for a new signal.
73
class Thread {
74
public:
75
Thread(Search::SharedState&,
76
std::unique_ptr<Search::ISearchManager>,
77
size_t,
78
OptionalThreadToNumaNodeBinder);
79
virtual ~Thread();
80
81
void idle_loop();
82
void start_searching();
83
void clear_worker();
84
void run_custom_job(std::function<void()> f);
85
86
void ensure_network_replicated();
87
88
// Thread has been slightly altered to allow running custom jobs, so
89
// this name is no longer correct. However, this class (and ThreadPool)
90
// require further work to make them properly generic while maintaining
91
// appropriate specificity regarding search, from the point of view of an
92
// outside user, so renaming of this function is left for whenever that happens.
93
void wait_for_search_finished();
94
size_t id() const { return idx; }
95
96
std::unique_ptr<Search::Worker> worker;
97
std::function<void()> jobFunc;
98
99
private:
100
std::mutex mutex;
101
std::condition_variable cv;
102
size_t idx, nthreads;
103
bool exit = false, searching = true; // Set before starting std::thread
104
NativeThread stdThread;
105
NumaReplicatedAccessToken numaAccessToken;
106
};
107
108
109
// ThreadPool struct handles all the threads-related stuff like init, starting,
110
// parking and, most importantly, launching a thread. All the access to threads
111
// is done through this class.
112
class ThreadPool {
113
public:
114
ThreadPool() {}
115
116
~ThreadPool() {
117
// destroy any existing thread(s)
118
if (threads.size() > 0)
119
{
120
main_thread()->wait_for_search_finished();
121
122
threads.clear();
123
}
124
}
125
126
ThreadPool(const ThreadPool&) = delete;
127
ThreadPool(ThreadPool&&) = delete;
128
129
ThreadPool& operator=(const ThreadPool&) = delete;
130
ThreadPool& operator=(ThreadPool&&) = delete;
131
132
void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType);
133
void run_on_thread(size_t threadId, std::function<void()> f);
134
void wait_on_thread(size_t threadId);
135
size_t num_threads() const;
136
void clear();
137
void set(const NumaConfig& numaConfig,
138
Search::SharedState,
139
const Search::SearchManager::UpdateContext&);
140
141
Search::SearchManager* main_manager();
142
Thread* main_thread() const { return threads.front().get(); }
143
uint64_t nodes_searched() const;
144
uint64_t tb_hits() const;
145
Thread* get_best_thread() const;
146
void start_searching();
147
void wait_for_search_finished() const;
148
149
std::vector<size_t> get_bound_thread_count_by_numa_node() const;
150
151
void ensure_network_replicated();
152
153
std::atomic_bool stop, abortedSearch, increaseDepth;
154
155
auto cbegin() const noexcept { return threads.cbegin(); }
156
auto begin() noexcept { return threads.begin(); }
157
auto end() noexcept { return threads.end(); }
158
auto cend() const noexcept { return threads.cend(); }
159
auto size() const noexcept { return threads.size(); }
160
auto empty() const noexcept { return threads.empty(); }
161
162
private:
163
StateListPtr setupStates;
164
std::vector<std::unique_ptr<Thread>> threads;
165
std::vector<NumaIndex> boundThreadToNumaNode;
166
167
uint64_t accumulate(std::atomic<uint64_t> Search::Worker::* member) const {
168
169
uint64_t sum = 0;
170
for (auto&& th : threads)
171
sum += (th->worker.get()->*member).load(std::memory_order_relaxed);
172
return sum;
173
}
174
};
175
176
} // namespace Stockfish
177
178
#endif // #ifndef THREAD_H_INCLUDED
179
180