Changeset View
Standalone View
source/ps/ThreadFrontier.h
- This file was added.
/* Copyright (C) 2020 Wildfire Games. | |||||
* This file is part of 0 A.D. | |||||
* | |||||
* 0 A.D. is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* 0 A.D. is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef INCLUDED_THREADFRONTIER | |||||
#define INCLUDED_THREADFRONTIER | |||||
#include <condition_variable> | |||||
#include <mutex> | |||||
/* | |||||
* A ThreadFrontier is similar to a Barrier in that it synchronizes n threads. | |||||
* A frontier has one thread waiting for n other threads to go through the Frontier. | |||||
*/ | |||||
class ThreadFrontier | |||||
{ | |||||
vladislavbelov: You might use `boost::barrier` instead. Also why do you need barriers if you can just return… | |||||
Done Inline ActionsI think I meant to not block the workers and only the main thread... Which TBH is kind of pointless.
That's fair on the part of the worker threads, but the main thread does need to know that all workers have finished working. wraitii: I think I meant to not block the workers and only the main thread... Which TBH is kind of… | |||||
public: | |||||
ThreadFrontier() : m_Expecting(0), m_Count(0) {}; | |||||
Not Done Inline ActionsPrivate stuff should be under public. vladislavbelov: Private stuff should be under public. | |||||
Not Done Inline ActionsMissed this one :) Stan: Missed this one :) | |||||
void Setup(int expect) | |||||
{ | |||||
ENSURE(m_Expecting == 0 && m_Count == 0); | |||||
std::lock_guard<std::mutex> lock(m_Mutex); | |||||
m_Expecting = expect; | |||||
// The frontier is open, call Reset() to close it. | |||||
m_Count = m_Expecting; | |||||
} | |||||
void Reset() | |||||
{ | |||||
m_Count = 0; | |||||
} | |||||
void Watch() | |||||
{ | |||||
std::unique_lock<std::mutex> lock(m_Mutex); | |||||
// If all threads have already gone through the frontier, we can stop watching right away. | |||||
if (m_Count == m_Expecting) | |||||
return; | |||||
m_ConditionVariable.wait(lock, [this] { return m_Count == m_Expecting; }); | |||||
} | |||||
void GoThrough() | |||||
{ | |||||
// Acquire the lock: we must be sure that the watching thread is either not yet in Watch() | |||||
// or is fully in the waiting state. Without this mutex lock, we could notify when the watching thread | |||||
// is in wait() but not yet in the waiting state, thus deadlocking. | |||||
std::lock_guard<std::mutex> lock(m_Mutex); | |||||
// Notify the watching thread if we are the last to go through. | |||||
Not Done Inline ActionsYou don't actually need a separate function to implement waiting. vladislavbelov: You don't actually need a separate function to implement waiting. | |||||
Done Inline ActionsI'm not entirely sure I follow you. The design of this structure is that the main thread calls "Watch", which is blocking, and all workers call "GoThrough", which isn't blocking. This isn't quite similar to a barrier (though I agree that on a second look this seems un-necessary). wraitii: I'm not entirely sure I follow you. The design of this structure is that the main thread calls… | |||||
if (++m_Count == m_Expecting) | |||||
m_ConditionVariable.notify_one(); | |||||
} | |||||
private: | |||||
std::mutex m_Mutex; | |||||
std::condition_variable m_ConditionVariable; | |||||
int m_Expecting; | |||||
int m_Count; | |||||
}; | |||||
#endif // INCLUDED_THREADFRONTIER |
You might use boost::barrier instead. Also why do you need barriers if you can just return the result data and wait for a next task?