From: Marco Costalba Date: Mon, 8 Aug 2011 11:03:16 +0000 (+0100) Subject: Move idle_loop() under Thread X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=ba85c59d96d962dddaa0f1a2608ebea2e8ae694b Move idle_loop() under Thread This greatly removes clutter from the difficult idle_loop() function No functional change. Signed-off-by: Marco Costalba --- diff --git a/src/search.cpp b/src/search.cpp index c4e354e4..49074cee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -2130,50 +2130,56 @@ split_point_start: // At split points actual search starts from here } // namespace -// ThreadsManager::idle_loop() is where the threads are parked when they have no work -// to do. The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint -// object for which the current thread is the master. +// Little helper used by idle_loop() to check that all the slaves of a +// master thread have finished searching. -void ThreadsManager::idle_loop(int threadID, SplitPoint* sp) { +static bool all_slaves_finished(SplitPoint* sp) { - assert(threadID >= 0 && threadID < MAX_THREADS); + assert(sp); + + for (int i = 0; i < Threads.size(); i++) + if (sp->is_slave[i]) + return false; + + return true; +} - int i; - bool allFinished; + +// Thread::idle_loop() is where the thread is parked when it has no work to do. +// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object +// for which the thread is the master. + +void Thread::idle_loop(SplitPoint* sp) { while (true) { - // Slave threads can exit as soon as allThreadsShouldExit flag raises, - // master should exit as last one. - if (allThreadsShouldExit) - { - assert(!sp); - threads[threadID].state = Thread::TERMINATED; - return; - } - // If we are not searching, 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)) + while ( do_sleep + || do_terminate + || (Threads.use_sleeping_threads() && state == Thread::AVAILABLE)) { - assert(!sp || useSleepingThreads); - assert(threadID != 0 || useSleepingThreads); + assert(!sp || Threads.use_sleeping_threads()); + assert(threadID != 0 || Threads.use_sleeping_threads()); - if (threads[threadID].state == Thread::INITIALIZING) - threads[threadID].state = Thread::AVAILABLE; + // Slave thread should exit as soon as do_terminate flag raises + if (do_terminate) + { + assert(!sp); + state = Thread::TERMINATED; + return; + } + + if (state == Thread::INITIALIZING) + state = Thread::AVAILABLE; // Grab the lock to avoid races with Thread::wake_up() - lock_grab(&threads[threadID].sleepLock); + lock_grab(&sleepLock); // If we are master and all slaves have finished don't go to sleep - for (i = 0; sp && i < activeThreads && !sp->is_slave[i]; i++) {} - allFinished = (i == activeThreads); - - if (allFinished || allThreadsShouldExit) + if (sp && all_slaves_finished(sp)) { - lock_release(&threads[threadID].sleepLock); + lock_release(&sleepLock); break; } @@ -2181,22 +2187,22 @@ void ThreadsManager::idle_loop(int threadID, SplitPoint* sp) { // particular we need to avoid a deadlock in case a master thread has, // in the meanwhile, allocated us and sent the wake_up() call before we // had the chance to grab the lock. - if (threadID >= activeThreads || threads[threadID].state == Thread::AVAILABLE) - cond_wait(&threads[threadID].sleepCond, &threads[threadID].sleepLock); + if (do_sleep || state == Thread::AVAILABLE) + cond_wait(&sleepCond, &sleepLock); - lock_release(&threads[threadID].sleepLock); + lock_release(&sleepLock); } // If this thread has been assigned work, launch a search - if (threads[threadID].state == Thread::WORKISWAITING) + if (state == Thread::WORKISWAITING) { - assert(!allThreadsShouldExit); + assert(!do_terminate); - threads[threadID].state = Thread::SEARCHING; + state = Thread::SEARCHING; // Copy split point position and search stack and call search() SearchStack ss[PLY_MAX_PLUS_2]; - SplitPoint* tsp = threads[threadID].splitPoint; + SplitPoint* tsp = splitPoint; Position pos(*tsp->pos, threadID); memcpy(ss, tsp->ss - 1, 4 * sizeof(SearchStack)); @@ -2211,24 +2217,21 @@ void ThreadsManager::idle_loop(int threadID, SplitPoint* sp) { else assert(false); - assert(threads[threadID].state == Thread::SEARCHING); + assert(state == Thread::SEARCHING); - threads[threadID].state = Thread::AVAILABLE; + 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 + if ( Threads.use_sleeping_threads() && threadID != tsp->master - && threads[tsp->master].state == Thread::AVAILABLE) - threads[tsp->master].wake_up(); + && 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->is_slave[i]; i++) {} - allFinished = (i == activeThreads); - - if (allFinished) + if (sp && all_slaves_finished(sp)) { // Because sp->is_slave[] is reset under lock protection, // be sure sp->lock has been released before to return. diff --git a/src/thread.cpp b/src/thread.cpp index 20a431b4..5c10eee0 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -35,7 +35,7 @@ namespace { extern "C" { DWORD WINAPI start_routine(LPVOID threadID) { - Threads.idle_loop(*(int*)threadID, NULL); + Threads[*(int*)threadID].idle_loop(NULL); return 0; } @@ -43,7 +43,7 @@ namespace { extern "C" { void* start_routine(void* threadID) { - Threads.idle_loop(*(int*)threadID, NULL); + Threads[*(int*)threadID].idle_loop(NULL); return NULL; } @@ -111,7 +111,22 @@ void ThreadsManager::read_uci_options() { maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value(); minimumSplitDepth = Options["Minimum Split Depth"].value() * ONE_PLY; useSleepingThreads = Options["Use Sleeping Threads"].value(); - activeThreads = Options["Threads"].value(); + + set_size(Options["Threads"].value()); +} + + +// set_size() changes the number of active threads and raises do_sleep flag for +// all the unused threads that will go immediately to sleep. + +void ThreadsManager::set_size(int cnt) { + + assert(cnt > 0 && cnt <= MAX_THREADS); + + activeThreads = cnt; + + for (int i = 0; i < MAX_THREADS; i++) + threads[i].do_sleep = !(i < activeThreads); } @@ -120,14 +135,10 @@ void ThreadsManager::read_uci_options() { void ThreadsManager::init() { - int threadID[MAX_THREADS]; - - // This flag is needed to properly end the threads when program exits - allThreadsShouldExit = false; - // Threads will sent to sleep as soon as created, only main thread is kept alive - activeThreads = 1; + set_size(1); threads[0].state = Thread::SEARCHING; + threads[0].threadID = 0; // Allocate pawn and material hash tables for main thread init_hash_tables(); @@ -149,13 +160,13 @@ void ThreadsManager::init() { for (int i = 1; i < MAX_THREADS; i++) { threads[i].state = Thread::INITIALIZING; - threadID[i] = i; + threads[i].threadID = i; #if defined(_MSC_VER) - bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL); + bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threads[i].threadID , 0, NULL) != NULL); #else pthread_t pthreadID; - bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0); + bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threads[i].threadID) == 0); pthread_detach(pthreadID); #endif if (!ok) @@ -174,14 +185,12 @@ void ThreadsManager::init() { void ThreadsManager::exit() { - // Force the woken up threads to exit idle_loop() and hence terminate - allThreadsShouldExit = true; - for (int i = 0; i < MAX_THREADS; i++) { - // Wake up all the threads and wait for termination + // Wake up all the slave threads and wait for termination if (i != 0) { + threads[i].do_terminate = true; threads[i].wake_up(); while (threads[i].state != Thread::TERMINATED) {} } @@ -318,7 +327,7 @@ Value ThreadsManager::split(Position& pos, SearchStack* ss, Value alpha, Value b // 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. - idle_loop(master, sp); + masterThread.idle_loop(sp); // In helpful master concept a master can help only a sub-tree, and // because here is all finished is not possible master is booked. diff --git a/src/thread.h b/src/thread.h index cdb0d9b8..3a2d8538 100644 --- a/src/thread.h +++ b/src/thread.h @@ -77,16 +77,20 @@ struct Thread { void wake_up(); bool cutoff_occurred() const; bool is_available_to(int master) const; + void idle_loop(SplitPoint* sp); SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS]; MaterialInfoTable materialTable; PawnInfoTable pawnTable; + int threadID; int maxPly; Lock sleepLock; WaitCondition sleepCond; volatile ThreadState state; SplitPoint* volatile splitPoint; volatile int activeSplitPoints; + volatile bool do_sleep; + volatile bool do_terminate; }; @@ -105,13 +109,13 @@ public: void exit(); void init_hash_tables(); + bool use_sleeping_threads() const { return useSleepingThreads; } int min_split_depth() const { return minimumSplitDepth; } int size() const { return activeThreads; } - void set_size(int cnt) { activeThreads = cnt; } + void set_size(int cnt); void read_uci_options(); bool available_slave_exists(int master) const; - void idle_loop(int threadID, SplitPoint* sp); template Value split(Position& pos, SearchStack* ss, Value alpha, Value beta, Value bestValue, @@ -123,7 +127,6 @@ private: int maxThreadsPerSplitPoint; int activeThreads; bool useSleepingThreads; - volatile bool allThreadsShouldExit; }; extern ThreadsManager Threads;