X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fthread.cpp;h=ef5ae857e30580822b5c05cf2406a413a6eb05f6;hp=6ddbf3f2e85f82ee085cf82db5f46cf76657b78d;hb=be77406a55d1840862237cffe153dc08a83108d3;hpb=8725494966f91af42c77d2f81d2c8a7fe1864316 diff --git a/src/thread.cpp b/src/thread.cpp index 6ddbf3f2..ef5ae857 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -38,9 +38,9 @@ namespace { // when start_routine (and hence virtual idle_loop) is called and when joining. template T* new_thread() { - T* th = new T(); - th->nativeThread = std::thread(&ThreadBase::idle_loop, th); // Will go to sleep - return th; + 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) { @@ -50,7 +50,7 @@ namespace { th->mutex.unlock(); th->notify_one(); - th->nativeThread.join(); // Wait for thread termination + th->join(); // Wait for thread termination delete th; } @@ -61,7 +61,7 @@ namespace { void ThreadBase::notify_one() { - std::unique_lock(this->mutex); + std::unique_lock lk(mutex); sleepCondition.notify_one(); } @@ -144,6 +144,8 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes // Pick and init the next available split point SplitPoint& sp = splitPoints[splitPointsSize]; + sp.spinlock.acquire(); // No contention here until we don't increment splitPointsSize + sp.master = this; sp.parentSplitPoint = activeSplitPoint; sp.slavesMask = 0, sp.slavesMask.set(idx); @@ -160,27 +162,28 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes sp.nodes = 0; sp.cutoff = false; sp.ss = ss; - - // Try to allocate available threads and ask them to start searching setting - // 'searching' flag. This must be done under lock protection to avoid concurrent - // allocation of the same slave by another master. - Threads.spinlock.acquire(); - sp.spinlock.acquire(); - sp.allSlavesSearching = true; // Must be set under lock protection + ++splitPointsSize; activeSplitPoint = &sp; activePosition = nullptr; + // Try to allocate available threads Thread* slave; while ( sp.slavesMask.count() < MAX_SLAVES_PER_SPLITPOINT - && (slave = Threads.available_slave(activeSplitPoint)) != nullptr) + && (slave = Threads.available_slave(&sp)) != nullptr) { - sp.slavesMask.set(slave->idx); - slave->activeSplitPoint = activeSplitPoint; - slave->searching = true; // Slave leaves idle_loop() - slave->notify_one(); // Could be sleeping + slave->spinlock.acquire(); + + if (slave->can_join(activeSplitPoint)) + { + activeSplitPoint->slavesMask.set(slave->idx); + slave->activeSplitPoint = activeSplitPoint; + slave->searching = true; + } + + slave->spinlock.release(); } // Everything is set up. The master thread enters the idle loop, from which @@ -188,7 +191,6 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes // The thread will return from the idle loop when all slaves have finished // their work at this split point. sp.spinlock.release(); - Threads.spinlock.release(); Thread::idle_loop(); // Force a call to base class idle_loop() @@ -198,13 +200,13 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes assert(!searching); assert(!activePosition); + searching = true; + // We have returned from the idle loop, which means that all threads are - // finished. Note that setting 'searching' and decreasing splitPointsSize must - // be done under lock protection to avoid a race with Thread::available_to(). - Threads.spinlock.acquire(); + // finished. Note that decreasing splitPointsSize must be done under lock + // protection to avoid a race with Thread::can_join(). sp.spinlock.acquire(); - searching = true; --splitPointsSize; activeSplitPoint = sp.parentSplitPoint; activePosition = &pos; @@ -213,7 +215,6 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes *bestValue = sp.bestValue; sp.spinlock.release(); - Threads.spinlock.release(); } @@ -250,7 +251,7 @@ void MainThread::idle_loop() { while (!thinking && !exit) { - Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed + sleepCondition.notify_one(); // Wake up the UI thread if needed sleepCondition.wait(lk); } @@ -270,6 +271,15 @@ void MainThread::idle_loop() { } +// MainThread::join() waits for main thread to finish the search + +void MainThread::join() { + + std::unique_lock 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 c'tor because Threads is a // static object and we need a fully initialized engine at this point due to @@ -336,21 +346,12 @@ Thread* ThreadPool::available_slave(const SplitPoint* sp) const { } -// ThreadPool::wait_for_think_finished() waits for main thread to finish the search - -void ThreadPool::wait_for_think_finished() { - - std::unique_lock lk(main()->mutex); - sleepCondition.wait(lk, [&]{ return !main()->thinking; }); -} - - // ThreadPool::start_thinking() wakes up the main thread sleeping in // MainThread::idle_loop() and starts a new search, then returns immediately. void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) { - wait_for_think_finished(); + main()->join(); SearchTime = now(); // As early as possible @@ -372,5 +373,5 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, RootMoves.push_back(RootMove(m)); main()->thinking = true; - main()->notify_one(); // Starts main thread + main()->notify_one(); // Wake up main thread: 'thinking' must be already set }