Skip to content

Commit

Permalink
Retire ThreadBase
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mcostalba committed Nov 13, 2015
1 parent ce84ab6 commit 76ed0ab
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 119 deletions.
20 changes: 9 additions & 11 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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));

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
Expand Down
123 changes: 42 additions & 81 deletions src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,68 +29,57 @@ 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() {

std::unique_lock<Mutex> lk(mutex);
sleepCondition.notify_one();
}
mutex.lock();
exit = true; // Search must be already finished
mutex.unlock();

notify_one();
std::thread::join(); // Wait for thread termination
}

// ThreadBase::wait() set the thread to sleep until 'condition' turns true

void ThreadBase::wait(std::atomic_bool& condition) {
// Thread::join() waits for the thread to finish searching
void Thread::join() {

std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return bool(condition); });
sleepCondition.wait(lk, [&]{ return !searching; });
}


// ThreadBase::wait_while() set the thread to sleep until 'condition' turns false
void ThreadBase::wait_while(std::atomic_bool& condition) {
// 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.wait(lk, [&]{ return !condition; });
sleepCondition.notify_one();
}


// Thread constructor makes some init but does not launch any execution thread,
// which will be started only when the constructor returns.
// Thread::wait() set the thread to sleep until 'condition' turns true

Thread::Thread() {
void Thread::wait(std::atomic_bool& condition) {

searching = resetCallsCnt = false;
maxPly = callsCnt = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Starts from 0
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return bool(condition); });
}


Expand All @@ -102,59 +91,30 @@ void Thread::idle_loop() {
{
std::unique_lock<Mutex> lk(mutex);

while (!searching && !exit)
sleepCondition.wait(lk);
searching = false;

lk.unlock();

if (!exit && searching)
search();
}
}


// 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)
while (!searching && !exit)
{
sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.notify_one(); // Wake up main thread if needed
sleepCondition.wait(lk);
}

lk.unlock();

if (!exit)
think();
if (!exit && searching)
search();
}
}


// 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
// allocation of Endgames in the Thread constructor.

void ThreadPool::init() {

push_back(new_thread<MainThread>());
push_back(new MainThread);
read_uci_options();
}

Expand All @@ -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
}
Expand All @@ -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();
}
}
Expand All @@ -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;
Expand All @@ -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
}
39 changes: 12 additions & 27 deletions src/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
struct Thread : public std::thread {

ThreadBase() { exit = false; }
virtual ~ThreadBase() = default;
virtual void idle_loop() = 0;
Thread();
virtual ~Thread();
virtual void search();
void idle_loop();
void join();
void notify_one();
void wait(std::atomic_bool& b);
void wait_while(std::atomic_bool& b);

std::atomic_bool exit, searching, resetCalls;
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 {

Thread();
virtual void idle_loop();
void search(bool isMainThread = false);

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;
Expand All @@ -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();
};


Expand Down

0 comments on commit 76ed0ab

Please sign in to comment.