void resetBetaCounters();
int64_t nodes_searched() const;
void get_beta_counters(Color us, int64_t& our, int64_t& their) const;
- bool idle_thread_exists(int master) const;
+ bool available_thread_exists(int master) const;
bool thread_is_available(int slave, int master) const;
bool thread_should_stop(int threadID) const;
void wake_sleeping_threads();
friend void poll();
int ActiveThreads;
- bool AllThreadsShouldExit, AllThreadsShouldSleep;
- Thread threads[THREAD_MAX];
- SplitPoint SplitPointStack[THREAD_MAX][ACTIVE_SPLIT_POINTS_MAX];
+ volatile bool AllThreadsShouldExit, AllThreadsShouldSleep;
+ Thread threads[MAX_THREADS];
+ SplitPoint SplitPointStack[MAX_THREADS][ACTIVE_SPLIT_POINTS_MAX];
Lock MPLock, IOLock;
pthread_cond_t WaitCond;
pthread_mutex_t WaitLock;
#else
- HANDLE SitIdleEvent[THREAD_MAX];
+ HANDLE SitIdleEvent[MAX_THREADS];
#endif
};
&& bestValue < beta
&& depth >= MinimumSplitDepth
&& Iteration <= 99
- && TM.idle_thread_exists(threadID)
+ && TM.available_thread_exists(threadID)
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, &alpha, &beta, &bestValue, VALUE_NONE,
&& bestValue < beta
&& depth >= MinimumSplitDepth
&& Iteration <= 99
- && TM.idle_thread_exists(threadID)
+ && TM.available_thread_exists(threadID)
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, //FIXME: SMP & futilityValue
void ThreadsManager::resetNodeCounters() {
- for (int i = 0; i < THREAD_MAX; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
threads[i].nodes = 0ULL;
}
void ThreadsManager::resetBetaCounters() {
- for (int i = 0; i < THREAD_MAX; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
threads[i].betaCutOffs[WHITE] = threads[i].betaCutOffs[BLACK] = 0ULL;
}
void ThreadsManager::get_beta_counters(Color us, int64_t& our, int64_t& their) const {
our = their = 0UL;
- for (int i = 0; i < THREAD_MAX; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
{
our += threads[i].betaCutOffs[us];
their += threads[i].betaCutOffs[opposite_color(us)];
void ThreadsManager::idle_loop(int threadID, SplitPoint* waitSp) {
- assert(threadID >= 0 && threadID < THREAD_MAX);
+ assert(threadID >= 0 && threadID < MAX_THREADS);
- threads[threadID].running = true;
-
- while (!AllThreadsShouldExit || threadID == 0)
+ while (true)
{
+ // Slave threads can exit as soon as AllThreadsShouldExit raises,
+ // master should exit as last one.
+ if (AllThreadsShouldExit && !waitSp)
+ {
+ 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 != 0
&& !AllThreadsShouldExit
&& (AllThreadsShouldSleep || threadID >= ActiveThreads))
{
-
- threads[threadID].sleeping = true;
+ threads[threadID].state = THREAD_SLEEPING;
#if !defined(_MSC_VER)
pthread_mutex_lock(&WaitLock);
- if (AllThreadsShouldSleep || threadID >= ActiveThreads)
- pthread_cond_wait(&WaitCond, &WaitLock);
-
+ pthread_cond_wait(&WaitCond, &WaitLock);
pthread_mutex_unlock(&WaitLock);
#else
WaitForSingleObject(SitIdleEvent[threadID], INFINITE);
#endif
+ // State is already changed by wake_sleeping_threads()
+ assert(threads[threadID].state == THREAD_AVAILABLE || threadID >= ActiveThreads);
}
- // Out of the while loop to avoid races in case thread is woken up but
- // while condition still holds true so that is put to sleep again.
- threads[threadID].sleeping = false;
-
// If this thread has been assigned work, launch a search
- if (threads[threadID].workIsWaiting)
+ if (threads[threadID].state == THREAD_WORKISWAITING)
{
- assert(!threads[threadID].idle);
+ threads[threadID].state = THREAD_SEARCHING;
- threads[threadID].workIsWaiting = false;
if (threads[threadID].splitPoint->pvNode)
sp_search_pv(threads[threadID].splitPoint, threadID);
else
sp_search(threads[threadID].splitPoint, threadID);
- threads[threadID].idle = true;
+ assert(threads[threadID].state == THREAD_SEARCHING);
+
+ // If this is a slave thread reset to available, instead
+ // if it is a master thread and all slaves have finished
+ // then leave as is to avoid booking by another master,
+ // we will leave idle loop shortly anyhow.
+ if ( !AllThreadsShouldExit
+ && (!waitSp || waitSp->cpus > 0))
+ threads[threadID].state = THREAD_AVAILABLE;
}
// If this thread is the master of a split point and all threads have
// finished their work at this split point, return from the idle loop.
if (waitSp != NULL && waitSp->cpus == 0)
+ {
+ assert( threads[threadID].state == THREAD_AVAILABLE
+ || threads[threadID].state == THREAD_SEARCHING);
+
+ threads[threadID].state = THREAD_SEARCHING;
return;
+ }
}
-
- threads[threadID].running = false;
}
lock_init(&IOLock, NULL);
// Initialize SplitPointStack locks
- for (int i = 0; i < THREAD_MAX; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++)
{
SplitPointStack[i][j].parent = NULL;
pthread_mutex_init(&WaitLock, NULL);
pthread_cond_init(&WaitCond, NULL);
#else
- for (i = 0; i < THREAD_MAX; i++)
+ for (i = 0; i < MAX_THREADS; i++)
SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
#endif
// Threads will be put to sleep as soon as created
AllThreadsShouldSleep = true;
- // All threads except the main thread should be initialized to idle state
+ // All threads except the main thread should be initialized to THREAD_AVAILABLE
ActiveThreads = 1;
- for (i = 1; i < THREAD_MAX; i++)
- threads[i].idle = true;
+ threads[0].state = THREAD_SEARCHING;
+ for (i = 1; i < MAX_THREADS; i++)
+ threads[i].state = THREAD_AVAILABLE;
// Launch the helper threads
- for (i = 1; i < THREAD_MAX; i++)
+ for (i = 1; i < MAX_THREADS; i++)
{
#if !defined(_MSC_VER)
}
// Wait until the thread has finished launching and is gone to sleep
- while (!threads[i].running || !threads[i].sleeping);
+ while (threads[i].state != THREAD_SLEEPING);
}
}
void ThreadsManager::exit_threads() {
- ActiveThreads = THREAD_MAX; // HACK
+ ActiveThreads = MAX_THREADS; // HACK
AllThreadsShouldSleep = true; // HACK
wake_sleeping_threads();
+
+ // This makes the threads to exit idle_loop()
AllThreadsShouldExit = true;
- for (int i = 1; i < THREAD_MAX; i++)
- {
- threads[i].stopRequest = true;
- while (threads[i].running);
- }
+
+ // Wait for thread termination
+ for (int i = 1; i < MAX_THREADS; i++)
+ while (threads[i].state != THREAD_TERMINATED)
+ threads[i].stopRequest = true;
// Now we can safely destroy the locks
- for (int i = 0; i < THREAD_MAX; i++)
+ for (int i = 0; i < MAX_THREADS; i++)
for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++)
lock_destroy(&(SplitPointStack[i][j].lock));
}
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
- if (!threads[slave].idle || slave == master)
+ if (threads[slave].state != THREAD_AVAILABLE || slave == master)
return false;
// Make a local copy to be sure doesn't change under our feet
}
- // idle_thread_exists() tries to find an idle thread which is available as
+ // available_thread_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
- bool ThreadsManager::idle_thread_exists(int master) const {
+ bool ThreadsManager::available_thread_exists(int master) const {
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
- if ( !idle_thread_exists(master)
+ if ( !available_thread_exists(master)
|| threads[master].activeSplitPoints >= ACTIVE_SPLIT_POINTS_MAX)
{
lock_release(&MPLock);
threads[master].splitPoint = splitPoint;
- // If we are here it means we are not idle
- assert(!threads[master].idle);
+ // If we are here it means we are not available
+ assert(threads[master].state != THREAD_AVAILABLE);
- // Following assert could fail because we could be slave of a master
- // thread that has just raised a stop request. Note that stopRequest
- // can be changed with only splitPoint::lock held, not with MPLock.
- /* assert(!threads[master].stopRequest); */
-
- // Allocate available threads setting idle flag to false
+ // Allocate available threads setting state to THREAD_BOOKED
for (int i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint; i++)
if (thread_is_available(i, master))
{
- threads[i].idle = false;
+ threads[i].state = THREAD_BOOKED;
threads[i].stopRequest = false;
threads[i].splitPoint = splitPoint;
splitPoint->slaves[i] = 1;
assert(splitPoint->cpus > 1);
- // We can release the lock because master and slave threads are already booked
+ // We can release the lock because slave threads are already booked and master is not available
lock_release(&MPLock);
// Tell the threads that they have work to do. This will make them leave
if (i == master || splitPoint->slaves[i])
{
memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 4 * sizeof(SearchStack));
- threads[i].workIsWaiting = true; // This makes the slave to exit from idle_loop()
+
+ assert(i == master || threads[i].state == THREAD_BOOKED);
+
+ threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop()
}
// Everything is set up. The master thread enters the idle loop, from
- // which it will instantly launch a search, because its workIsWaiting
- // slot is 'true'. We send the split point as a second parameter to the
+ // which it will instantly launch a search, because its state is
+ // THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point
// (i.e. when splitPoint->cpus == 0).
*beta = splitPoint->beta;
*bestValue = splitPoint->bestValue;
threads[master].stopRequest = false;
- threads[master].idle = false;
threads[master].activeSplitPoints--;
threads[master].splitPoint = splitPoint->parent;
for (int i = 1; i < ActiveThreads; i++)
{
- assert(threads[i].sleeping == true);
+ assert(threads[i].state == THREAD_SLEEPING);
- threads[i].idle = true;
- threads[i].workIsWaiting = false;
+ threads[i].state = THREAD_AVAILABLE;
}
#if !defined(_MSC_VER)
pthread_cond_broadcast(&WaitCond);
pthread_mutex_unlock(&WaitLock);
#else
- for (int i = 1; i < THREAD_MAX; i++)
+ for (int i = 1; i < MAX_THREADS; i++)
SetEvent(SitIdleEvent[i]);
#endif
- // Wait for the threads to be all woken up
- for (int i = 1; i < ActiveThreads; i++)
- while (threads[i].sleeping);
}
// put_threads_to_sleep() makes all the threads go to sleep just before
- // to leave think(), at the end of the search. threads should have already
+ // to leave think(), at the end of the search. Threads should have already
// finished the job and should be idle.
void ThreadsManager::put_threads_to_sleep() {
assert(!AllThreadsShouldSleep);
+ // This makes the threads to go to sleep
AllThreadsShouldSleep = true;
// Wait for the threads to be all sleeping and reset flags
// to a known state.
for (int i = 1; i < ActiveThreads; i++)
{
- while (!threads[i].sleeping);
-
- assert(threads[i].idle);
- assert(threads[i].running);
- assert(!threads[i].workIsWaiting);
+ while (threads[i].state != THREAD_SLEEPING);
// These two flags can be in a random state
threads[i].stopRequest = threads[i].printCurrentLineRequest = false;
// One shot only
threads[threadID].printCurrentLineRequest = false;
- if (!threads[threadID].idle)
+ if (threads[threadID].state == THREAD_SEARCHING)
{
lock_grab(&IOLock);
cout << "info currline " << (threadID + 1);