1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-29 16:23:09 +00:00

Retire ThreadBase

Now that we don't have anymore TimerThread, there is
no need of this long class hierarchy.

Also assorted reformatting while there.

To verify no regression, passed at STC with 7 threads:
LLR: 2.97 (-2.94,2.94) [-5.00,0.00]
Total: 30990 W: 4945 L: 4942 D: 21103

No functional change.
This commit is contained in:
Marco Costalba 2015-11-05 08:40:23 +01:00
parent ce84ab6e9d
commit 76ed0ab501
3 changed files with 73 additions and 129 deletions

View file

@ -220,11 +220,11 @@ uint64_t Search::perft(Position& pos, Depth depth) {
template uint64_t Search::perft<true>(Position&, Depth);
/// MainThread::think() is called by the main thread when the program receives
/// MainThread::search() is called by the main thread when the program receives
/// the UCI 'go' command. It searches from root position and at the end prints
/// the "bestmove" to output.
void MainThread::think() {
void MainThread::search() {
Color us = rootPos.side_to_move();
Time.init(Limits, us, rootPos.game_ply());
@ -299,7 +299,7 @@ void MainThread::think() {
}
}
search(true); // Let's start searching!
Thread::search(); // Let's start searching!
}
// When playing in 'nodes as time' mode, subtract the searched nodes from
@ -324,7 +324,7 @@ void MainThread::think() {
// Wait until all threads have finished
for (Thread* th : Threads)
if (th != this)
th->wait_while(th->searching);
th->join();
// Check if there are threads with a better score than main thread.
Thread* bestThread = this;
@ -351,11 +351,12 @@ void MainThread::think() {
// repeatedly with increasing depth until the allocated thinking time has been
// consumed, user stops the search, or the maximum search depth is reached.
void Thread::search(bool isMainThread) {
void Thread::search() {
Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2)
Value bestValue, alpha, beta, delta;
Move easyMove = MOVE_NONE;
bool isMainThread = (this == Threads.main());
std::memset(ss-2, 0, 5 * sizeof(Stack));
@ -532,9 +533,6 @@ void Thread::search(bool isMainThread) {
}
}
searching = false;
notify_one(); // Wake up main thread if is sleeping waiting for us
if (!isMainThread)
return;
@ -583,15 +581,15 @@ namespace {
ss->ply = (ss-1)->ply + 1;
// Check for available remaining time
if (thisThread->resetCallsCnt.load(std::memory_order_relaxed))
if (thisThread->resetCalls.load(std::memory_order_relaxed))
{
thisThread->resetCallsCnt = false;
thisThread->resetCalls = false;
thisThread->callsCnt = 0;
}
if (++thisThread->callsCnt > 4096)
{
for (Thread* th : Threads)
th->resetCallsCnt = true;
th->resetCalls = true;
check_time();
}

View file

@ -29,71 +29,60 @@ using namespace Search;
ThreadPool Threads; // Global object
namespace {
// Thread constructor makes some init and launches the thread that will go to
// sleep in idle_loop().
// Helpers to launch a thread after creation and joining before delete. Outside the
// Thread constructor and destructor because the object must be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() {
std::thread* th = new T;
*th = std::thread(&T::idle_loop, (T*)th); // Will go to sleep
return (T*)th;
}
void delete_thread(ThreadBase* th) {
th->mutex.lock();
th->exit = true; // Search must be already finished
th->mutex.unlock();
th->notify_one();
th->join(); // Wait for thread termination
delete th;
}
Thread::Thread() {
searching = true; // Avoid a race with start_thinking()
exit = resetCalls = false;
maxPly = callsCnt = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Starts from 0
std::thread::operator=(std::thread(&Thread::idle_loop, this));
}
// ThreadBase::notify_one() wakes up the thread when there is some work to do
// Thread destructor waits for thread termination before deleting
void ThreadBase::notify_one() {
Thread::~Thread() {
mutex.lock();
exit = true; // Search must be already finished
mutex.unlock();
notify_one();
std::thread::join(); // Wait for thread termination
}
// Thread::join() waits for the thread to finish searching
void Thread::join() {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !searching; });
}
// Thread::notify_one() wakes up the thread when there is some work to do
void Thread::notify_one() {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.notify_one();
}
// ThreadBase::wait() set the thread to sleep until 'condition' turns true
// Thread::wait() set the thread to sleep until 'condition' turns true
void ThreadBase::wait(std::atomic_bool& condition) {
void Thread::wait(std::atomic_bool& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return bool(condition); });
}
// ThreadBase::wait_while() set the thread to sleep until 'condition' turns false
void ThreadBase::wait_while(std::atomic_bool& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !condition; });
}
// Thread constructor makes some init but does not launch any execution thread,
// which will be started only when the constructor returns.
Thread::Thread() {
searching = resetCallsCnt = false;
maxPly = callsCnt = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Starts from 0
}
// Thread::idle_loop() is where the thread is parked when it has no work to do
void Thread::idle_loop() {
@ -102,8 +91,13 @@ void Thread::idle_loop() {
{
std::unique_lock<Mutex> lk(mutex);
searching = false;
while (!searching && !exit)
{
sleepCondition.notify_one(); // Wake up main thread if needed
sleepCondition.wait(lk);
}
lk.unlock();
@ -113,40 +107,6 @@ void Thread::idle_loop() {
}
// MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. The main thread will launch all the slave threads.
void MainThread::idle_loop() {
while (!exit)
{
std::unique_lock<Mutex> lk(mutex);
thinking = false;
while (!thinking && !exit)
{
sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.wait(lk);
}
lk.unlock();
if (!exit)
think();
}
}
// MainThread::join() waits for main thread to finish thinking
void MainThread::join() {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !thinking; });
}
// ThreadPool::init() is called at startup to create and launch requested threads,
// that will go immediately to sleep. We cannot use a constructor because Threads
// is a static object and we need a fully initialized engine at this point due to
@ -154,7 +114,7 @@ void MainThread::join() {
void ThreadPool::init() {
push_back(new_thread<MainThread>());
push_back(new MainThread);
read_uci_options();
}
@ -165,7 +125,7 @@ void ThreadPool::init() {
void ThreadPool::exit() {
for (Thread* th : *this)
delete_thread(th);
delete th;
clear(); // Get rid of stale pointers
}
@ -184,11 +144,11 @@ void ThreadPool::read_uci_options() {
assert(requested > 0);
while (size() < requested)
push_back(new_thread<Thread>());
push_back(new Thread);
while (size() > requested)
{
delete_thread(back());
delete back();
pop_back();
}
}
@ -210,7 +170,8 @@ int64_t ThreadPool::nodes_searched() {
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
StateStackPtr& states) {
main()->join();
for (Thread* th : Threads)
th->join();
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
@ -229,6 +190,6 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
main()->rootMoves.push_back(RootMove(m));
main()->thinking = true;
main()->notify_one(); // Wake up main thread: 'thinking' must be already set
main()->searching = true;
main()->notify_one(); // Wake up main thread: 'searching' must be already set
}

View file

@ -35,41 +35,30 @@
#include "thread_win32.h"
/// ThreadBase struct is the base of the hierarchy from where we derive all the
/// specialized thread classes.
/// Thread struct keeps together all the thread related stuff. We also use
/// per-thread pawn and material hash tables so that once we get a pointer to an
/// entry its life time is unlimited and we don't have to care about someone
/// changing the entry under our feet.
struct ThreadBase : public std::thread {
ThreadBase() { exit = false; }
virtual ~ThreadBase() = default;
virtual void idle_loop() = 0;
void notify_one();
void wait(std::atomic_bool& b);
void wait_while(std::atomic_bool& b);
Mutex mutex;
ConditionVariable sleepCondition;
std::atomic_bool exit;
};
/// Thread struct keeps together all the thread related stuff like locks, state,
/// history and countermoves tables. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet.
struct Thread : public ThreadBase {
struct Thread : public std::thread {
Thread();
virtual void idle_loop();
void search(bool isMainThread = false);
virtual ~Thread();
virtual void search();
void idle_loop();
void join();
void notify_one();
void wait(std::atomic_bool& b);
std::atomic_bool exit, searching, resetCalls;
Mutex mutex;
ConditionVariable sleepCondition;
Pawns::Table pawnsTable;
Material::Table materialTable;
Endgames endgames;
size_t idx, PVIdx;
int maxPly, callsCnt;
std::atomic_bool searching, resetCallsCnt;
Position rootPos;
Search::RootMoveVector rootMoves;
@ -83,11 +72,7 @@ struct Thread : public ThreadBase {
/// MainThread is a derived classes used to characterize the the main one
struct MainThread : public Thread {
MainThread() { thinking = true; } // Avoid a race with start_thinking()
virtual void idle_loop();
void join();
void think();
std::atomic_bool thinking;
virtual void search();
};