No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Probe the material hash table
- MaterialInfo* mi = ThreadsMgr[pos.thread()].materialTable.get_material_info(pos);
+ MaterialInfo* mi = Threads[pos.thread()].materialTable.get_material_info(pos);
bonus += mi->material_value();
// If we have a specialized evaluation function for the current material
}
// Probe the pawn hash table
- ei.pi = ThreadsMgr[pos.thread()].pawnTable.get_pawn_info(pos);
+ ei.pi = Threads[pos.thread()].pawnTable.get_pawn_info(pos);
bonus += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
// Initialize attack and king safety bitboards
Position::init_piece_square_tables();
init_kpk_bitbase();
init_search();
- ThreadsMgr.init_threads();
+ Threads.init();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
<< "[limit = 12] [fen positions file = default] "
<< "[depth, time, perft or node limited = depth]" << endl;
- ThreadsMgr.exit_threads();
+ Threads.exit();
return 0;
}
}
// Prefetch pawn and material hash tables
- ThreadsMgr[threadID].pawnTable.prefetch(st->pawnKey);
- ThreadsMgr[threadID].materialTable.prefetch(st->materialKey);
+ Threads[threadID].pawnTable.prefetch(st->pawnKey);
+ Threads[threadID].materialTable.prefetch(st->materialKey);
// Update incremental scores
st->value += pst_delta(piece, from, to);
SkillLevel = Options["Skill level"].value<int>();
read_evaluation_uci_options(pos.side_to_move());
- ThreadsMgr.read_uci_options();
+ Threads.read_uci_options();
// If needed allocate pawn and material hash tables and adjust TT size
- ThreadsMgr.init_hash_tables();
+ Threads.init_hash_tables();
TT.set_size(Options["Hash"].value<int>());
if (Options["Clear Hash"].value<bool>())
MultiPV = (SkillLevelEnabled ? Max(UCIMultiPV, 4) : UCIMultiPV);
// Wake up needed threads and reset maxPly counter
- for (int i = 0; i < ThreadsMgr.active_threads(); i++)
+ for (int i = 0; i < Threads.size(); i++)
{
- ThreadsMgr[i].wake_up();
- ThreadsMgr[i].maxPly = 0;
+ Threads[i].wake_up();
+ Threads[i].maxPly = 0;
}
// Write to log file and keep it open to be accessed during the search
}
// This makes all the threads to go to sleep
- ThreadsMgr.set_active_threads(1);
+ Threads.set_size(1);
// If we are pondering or in infinite search, we shouldn't print the
// best move before we are told to do so.
// Retrieve max searched depth among threads
selDepth = 0;
- for (int i = 0; i < ThreadsMgr.active_threads(); i++)
- if (ThreadsMgr[i].maxPly > selDepth)
- selDepth = ThreadsMgr[i].maxPly;
+ for (int i = 0; i < Threads.size(); i++)
+ if (Threads[i].maxPly > selDepth)
+ selDepth = Threads[i].maxPly;
// Send PV line to GUI and to log file
for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++)
assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
assert(beta > alpha && beta <= VALUE_INFINITE);
assert(PvNode || alpha == beta - 1);
- assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads());
+ assert(pos.thread() >= 0 && pos.thread() < Threads.size());
Move movesSearched[MAX_MOVES];
int64_t nodes;
ss->ply = (ss-1)->ply + 1;
// Used to send selDepth info to GUI
- if (PvNode && ThreadsMgr[threadID].maxPly < ss->ply)
- ThreadsMgr[threadID].maxPly = ss->ply;
+ if (PvNode && Threads[threadID].maxPly < ss->ply)
+ Threads[threadID].maxPly = ss->ply;
if (SpNode)
{
// Step 2. Check for aborted search and immediate draw
if (( StopRequest
- || ThreadsMgr.cutoff_at_splitpoint(threadID)
+ || Threads[threadID].cutoff_occurred()
|| pos.is_draw()
|| ss->ply > PLY_MAX) && !Root)
return VALUE_DRAW;
// Loop through all legal moves until no moves remain or a beta cutoff occurs
while ( bestValue < beta
&& (move = mp.get_next_move()) != MOVE_NONE
- && !ThreadsMgr.cutoff_at_splitpoint(threadID))
+ && !Threads[threadID].cutoff_occurred())
{
assert(move_is_ok(move));
alpha = sp->alpha;
}
- if (value > bestValue && !(SpNode && ThreadsMgr.cutoff_at_splitpoint(threadID)))
+ if (value > bestValue && !(SpNode && Threads[threadID].cutoff_occurred()))
{
bestValue = value;
sp->alpha = value;
}
else if (SpNode)
- sp->betaCutoff = true;
+ sp->is_betaCutoff = true;
if (value == value_mate_in(ss->ply + 1))
ss->mateKiller = move;
// Step 18. Check for split
if ( !Root
&& !SpNode
- && depth >= ThreadsMgr.min_split_depth()
- && ThreadsMgr.active_threads() > 1
+ && depth >= Threads.min_split_depth()
&& bestValue < beta
- && ThreadsMgr.available_thread_exists(threadID)
+ && Threads.available_slave_exists(threadID)
&& !StopRequest
- && !ThreadsMgr.cutoff_at_splitpoint(threadID))
- ThreadsMgr.split<FakeSplit>(pos, ss, &alpha, beta, &bestValue, depth,
- threatMove, moveCount, &mp, PvNode);
+ && !Threads[threadID].cutoff_occurred())
+ Threads.split<FakeSplit>(pos, ss, &alpha, beta, &bestValue, depth,
+ threatMove, moveCount, &mp, PvNode);
}
// Step 19. Check for mate and stalemate
// Step 20. Update tables
// If the search is not aborted, update the transposition table,
// history counters, and killer moves.
- if (!SpNode && !StopRequest && !ThreadsMgr.cutoff_at_splitpoint(threadID))
+ if (!SpNode && !StopRequest && !Threads[threadID].cutoff_occurred())
{
move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove;
vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER
if (SpNode)
{
// Here we have the lock still grabbed
- sp->slaves[threadID] = 0;
+ sp->is_slave[threadID] = false;
sp->nodes += pos.nodes_searched();
lock_release(&(sp->lock));
}
assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
assert(PvNode || alpha == beta - 1);
assert(depth <= 0);
- assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads());
+ assert(pos.thread() >= 0 && pos.thread() < Threads.size());
StateInfo st;
Move ttMove, move;
if (allThreadsShouldExit)
{
assert(!sp);
- threads[threadID].state = THREAD_TERMINATED;
+ threads[threadID].state = Thread::TERMINATED;
return;
}
// If we are not thinking, wait for a condition to be signaled
// instead of wasting CPU time polling for work.
while ( threadID >= activeThreads
- || threads[threadID].state == THREAD_INITIALIZING
- || (useSleepingThreads && threads[threadID].state == THREAD_AVAILABLE))
+ || threads[threadID].state == Thread::INITIALIZING
+ || (useSleepingThreads && threads[threadID].state == Thread::AVAILABLE))
{
assert(!sp || useSleepingThreads);
assert(threadID != 0 || useSleepingThreads);
- if (threads[threadID].state == THREAD_INITIALIZING)
- threads[threadID].state = THREAD_AVAILABLE;
+ if (threads[threadID].state == Thread::INITIALIZING)
+ threads[threadID].state = Thread::AVAILABLE;
// Grab the lock to avoid races with Thread::wake_up()
lock_grab(&threads[threadID].sleepLock);
// If we are master and all slaves have finished do not go to sleep
- for (i = 0; sp && i < activeThreads && !sp->slaves[i]; i++) {}
+ for (i = 0; sp && i < activeThreads && !sp->is_slave[i]; i++) {}
allFinished = (i == activeThreads);
if (allFinished || allThreadsShouldExit)
}
// Do sleep here after retesting sleep conditions
- if (threadID >= activeThreads || threads[threadID].state == THREAD_AVAILABLE)
+ if (threadID >= activeThreads || threads[threadID].state == Thread::AVAILABLE)
cond_wait(&threads[threadID].sleepCond, &threads[threadID].sleepLock);
lock_release(&threads[threadID].sleepLock);
}
// If this thread has been assigned work, launch a search
- if (threads[threadID].state == THREAD_WORKISWAITING)
+ if (threads[threadID].state == Thread::WORKISWAITING)
{
assert(!allThreadsShouldExit);
- threads[threadID].state = THREAD_SEARCHING;
+ threads[threadID].state = Thread::SEARCHING;
// Copy split point position and search stack and call search()
// with SplitPoint template parameter set to true.
else
search<NonPV, true, false>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth);
- assert(threads[threadID].state == THREAD_SEARCHING);
+ assert(threads[threadID].state == Thread::SEARCHING);
- threads[threadID].state = THREAD_AVAILABLE;
+ threads[threadID].state = Thread::AVAILABLE;
// Wake up master thread so to allow it to return from the idle loop in
// case we are the last slave of the split point.
if ( useSleepingThreads
&& threadID != tsp->master
- && threads[tsp->master].state == THREAD_AVAILABLE)
+ && threads[tsp->master].state == Thread::AVAILABLE)
threads[tsp->master].wake_up();
}
// If this thread is the master of a split point and all slaves have
// finished their work at this split point, return from the idle loop.
- for (i = 0; sp && i < activeThreads && !sp->slaves[i]; i++) {}
+ for (i = 0; sp && i < activeThreads && !sp->is_slave[i]; i++) {}
allFinished = (i == activeThreads);
if (allFinished)
// In helpful master concept a master can help only a sub-tree, and
// because here is all finished is not possible master is booked.
- assert(threads[threadID].state == THREAD_AVAILABLE);
+ assert(threads[threadID].state == Thread::AVAILABLE);
- threads[threadID].state = THREAD_SEARCHING;
+ threads[threadID].state = Thread::SEARCHING;
return;
}
}
#include "thread.h"
#include "ucioption.h"
-ThreadsManager ThreadsMgr; // Global object definition
+ThreadsManager Threads; // Global object definition
namespace {
void* init_thread(void* threadID) {
- ThreadsMgr.idle_loop(*(int*)threadID, NULL);
+ Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
DWORD WINAPI init_thread(LPVOID threadID) {
- ThreadsMgr.idle_loop(*(int*)threadID, NULL);
+ Threads.idle_loop(*(int*)threadID, NULL);
return 0;
}
}
+// wake_up() wakes up the thread, normally at the beginning of the search or,
+// if "sleeping threads" is used, when there is some work to do.
+
+void Thread::wake_up() {
+
+ lock_grab(&sleepLock);
+ cond_signal(&sleepCond);
+ lock_release(&sleepLock);
+}
+
+
+// cutoff_occurred() checks whether a beta cutoff has occurred in
+// the thread's currently active split point, or in some ancestor of
+// the current split point.
+
+bool Thread::cutoff_occurred() const {
+
+ for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
+ if (sp->is_betaCutoff)
+ return true;
+ return false;
+}
+
+
+// is_available_to() checks whether the thread is available to help the thread with
+// threadID "master" at a split point. An obvious requirement is that thread must be
+// idle. With more than two threads, this is not by itself sufficient: If the thread
+// is the master of some active split point, it is only available as a slave to the
+// threads which are busy searching the split point at the top of "slave"'s split
+// point stack (the "helpful master concept" in YBWC terminology).
+
+bool Thread::is_available_to(int master) const {
+
+ if (state != AVAILABLE)
+ return false;
+
+ // Make a local copy to be sure doesn't become zero under our feet while
+ // testing next condition and so leading to an out of bound access.
+ int localActiveSplitPoints = activeSplitPoints;
+
+ // No active split points means that the thread is available as a slave for any
+ // other thread otherwise apply the "helpful master" concept if possible.
+ if ( !localActiveSplitPoints
+ || splitPoints[localActiveSplitPoints - 1].is_slave[master])
+ return true;
+
+ return false;
+}
+
+
// read_uci_options() updates number of active threads and other internal
// parameters according to the UCI options values. It is called before
// to start a new search.
// init_threads() is called during startup. Initializes locks and condition
// variables and launches all threads sending them immediately to sleep.
-void ThreadsManager::init_threads() {
+void ThreadsManager::init() {
int arg[MAX_THREADS];
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
- threads[0].state = THREAD_SEARCHING;
+ threads[0].state = Thread::SEARCHING;
// Allocate pawn and material hash tables for main thread
init_hash_tables();
// Create and startup all the threads but the main that is already running
for (int i = 1; i < MAX_THREADS; i++)
{
- threads[i].state = THREAD_INITIALIZING;
+ threads[i].state = Thread::INITIALIZING;
arg[i] = i;
#if !defined(_MSC_VER)
if (!ok)
{
std::cout << "Failed to create thread number " << i << std::endl;
- exit(EXIT_FAILURE);
+ ::exit(EXIT_FAILURE);
}
// Wait until the thread has finished launching and is gone to sleep
- while (threads[i].state == THREAD_INITIALIZING) {}
+ while (threads[i].state == Thread::INITIALIZING) {}
}
}
// exit_threads() is called when the program exits. It makes all the
// helper threads exit cleanly.
-void ThreadsManager::exit_threads() {
+void ThreadsManager::exit() {
// Force the woken up threads to exit idle_loop() and hence terminate
allThreadsShouldExit = true;
if (i != 0)
{
threads[i].wake_up();
- while (threads[i].state != THREAD_TERMINATED) {}
+ while (threads[i].state != Thread::TERMINATED) {}
}
// Now we can safely destroy the locks and wait conditions
}
-// cutoff_at_splitpoint() checks whether a beta cutoff has occurred in
-// the thread's currently active split point, or in some ancestor of
-// the current split point.
-
-bool ThreadsManager::cutoff_at_splitpoint(int threadID) const {
-
- assert(threadID >= 0 && threadID < activeThreads);
-
- SplitPoint* sp = threads[threadID].splitPoint;
-
- for ( ; sp && !sp->betaCutoff; sp = sp->parent) {}
- return sp != NULL;
-}
-
-
-// thread_is_available() checks whether the thread with threadID "slave" is
-// available to help the thread with threadID "master" at a split point. An
-// obvious requirement is that "slave" must be idle. With more than two
-// threads, this is not by itself sufficient: If "slave" is the master of
-// some active split point, it is only available as a slave to the other
-// threads which are busy searching the split point at the top of "slave"'s
-// split point stack (the "helpful master concept" in YBWC terminology).
-
-bool ThreadsManager::thread_is_available(int slave, int master) const {
-
- assert(slave >= 0 && slave < activeThreads);
- assert(master >= 0 && master < activeThreads);
- assert(activeThreads > 1);
-
- if (threads[slave].state != THREAD_AVAILABLE || slave == master)
- return false;
-
- // Make a local copy to be sure doesn't change under our feet
- int localActiveSplitPoints = threads[slave].activeSplitPoints;
-
- // No active split points means that the thread is available as
- // a slave for any other thread.
- if (localActiveSplitPoints == 0 || activeThreads == 2)
- return true;
-
- // Apply the "helpful master" concept if possible. Use localActiveSplitPoints
- // that is known to be > 0, instead of threads[slave].activeSplitPoints that
- // could have been set to 0 by another thread leading to an out of bound access.
- if (threads[slave].splitPoints[localActiveSplitPoints - 1].slaves[master])
- return true;
-
- return false;
-}
-
-
-// available_thread_exists() tries to find an idle thread which is available as
+// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
-bool ThreadsManager::available_thread_exists(int master) const {
+bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
- assert(activeThreads > 1);
for (int i = 0; i < activeThreads; i++)
- if (thread_is_available(i, master))
+ if (i != master && threads[i].is_available_to(master))
return true;
return false;
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
- if ( !available_thread_exists(master)
+ if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
// Initialize the split point object
splitPoint.parent = masterThread.splitPoint;
splitPoint.master = master;
- splitPoint.betaCutoff = false;
+ splitPoint.is_betaCutoff = false;
splitPoint.depth = depth;
splitPoint.threatMove = threatMove;
splitPoint.alpha = *alpha;
splitPoint.nodes = 0;
splitPoint.ss = ss;
for (i = 0; i < activeThreads; i++)
- splitPoint.slaves[i] = 0;
+ splitPoint.is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
// If we are here it means we are not available
- assert(masterThread.state != THREAD_AVAILABLE);
+ assert(masterThread.state != Thread::AVAILABLE);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
- if (thread_is_available(i, master))
+ if (i != master && threads[i].is_available_to(master))
{
- threads[i].state = THREAD_BOOKED;
+ threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
- splitPoint.slaves[i] = 1;
+ splitPoint.is_slave[i] = true;
workersCnt++;
}
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for (i = 0; i < activeThreads; i++)
- if (i == master || splitPoint.slaves[i])
+ if (i == master || splitPoint.is_slave[i])
{
- assert(i == master || threads[i].state == THREAD_BOOKED);
+ assert(i == master || threads[i].state == Thread::BOOKED);
- threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop()
+ threads[i].state = Thread::WORKISWAITING; // This makes the slave to exit from idle_loop()
if (useSleepingThreads && i != master)
threads[i].wake_up();
}
// Explicit template instantiations
-template void ThreadsManager::split<0>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
-template void ThreadsManager::split<1>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
+template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
+template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
volatile Value alpha;
volatile Value bestValue;
volatile int moveCount;
- volatile bool betaCutoff;
- volatile int slaves[MAX_THREADS];
-};
-
-// ThreadState type is used to represent thread's current state
-enum ThreadState
-{
- THREAD_INITIALIZING, // thread is initializing itself
- THREAD_SEARCHING, // thread is performing work
- THREAD_AVAILABLE, // thread is waiting for work
- THREAD_BOOKED, // other thread (master) has booked us as a slave
- THREAD_WORKISWAITING, // master has ordered us to start
- THREAD_TERMINATED // we are quitting and thread is terminated
+ volatile bool is_betaCutoff;
+ volatile bool is_slave[MAX_THREADS];
};
-// We 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.
+/// Thread struct is used to keep together all the thread related stuff like locks,
+/// state and especially split points. 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 {
+
+ enum ThreadState
+ {
+ INITIALIZING, // Thread is initializing itself
+ SEARCHING, // Thread is performing work
+ AVAILABLE, // Thread is waiting for work
+ BOOKED, // Other thread (master) has booked us as a slave
+ WORKISWAITING, // Master has ordered us to start
+ TERMINATED // We are quitting and thread is terminated
+ };
+
+ void wake_up();
+ bool cutoff_occurred() const;
+ bool is_available_to(int master) const;
+
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int maxPly;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
-
- void wake_up() {
- lock_grab(&sleepLock);
- cond_signal(&sleepCond);
- lock_release(&sleepLock);
- }
};
-// ThreadsManager class is used to handle all the threads related stuff like init,
-// starting, parking and, the most important, launching a slave thread at a split
-// point. All the access to shared thread data is done through this class.
+/// ThreadsManager class is used to handle all the threads related stuff like init,
+/// starting, parking and, the most important, launching a slave thread at a split
+/// point. All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
*/
public:
Thread& operator[](int threadID) { return threads[threadID]; }
- void init_threads();
- void exit_threads();
+ void init();
+ void exit();
void init_hash_tables();
int min_split_depth() const { return minimumSplitDepth; }
- int active_threads() const { return activeThreads; }
- void set_active_threads(int cnt) { activeThreads = cnt; }
+ int size() const { return activeThreads; }
+ void set_size(int cnt) { activeThreads = cnt; }
void read_uci_options();
- bool available_thread_exists(int master) const;
- bool thread_is_available(int slave, int master) const;
- bool cutoff_at_splitpoint(int threadID) const;
+ bool available_slave_exists(int master) const;
void idle_loop(int threadID, SplitPoint* sp);
template <bool Fake>
Thread threads[MAX_THREADS];
};
-extern ThreadsManager ThreadsMgr;
+extern ThreadsManager Threads;
#endif // !defined(THREAD_H_INCLUDED)