mirror of
https://github.com/verilator/verilator.git
synced 2026-09-04 00:30:34 +02:00
Internals: V3LockGuard: Add constructor for adopting already locked mutex. (#4476)
This commit is contained in:
+68
-14
@@ -24,13 +24,16 @@
|
||||
constexpr unsigned int V3ThreadPool::FUTUREWAITFOR_MS;
|
||||
|
||||
void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE VL_EXCLUDES(m_mutex)
|
||||
VL_EXCLUDES(m_stoppedJobsMutex) {
|
||||
VL_EXCLUDES(m_stoppedJobsMutex) VL_EXCLUDES(V3MtDisabledLock::instance()) {
|
||||
// At least one thread (main)
|
||||
n = std::max(1u, n);
|
||||
if (n == (m_workers.size() + 1)) { return; }
|
||||
// This function is not thread-safe and can result in race between threads
|
||||
UASSERT(V3MutexConfig::s().lockConfig(),
|
||||
"Mutex config needs to be locked before starting ThreadPool");
|
||||
{
|
||||
V3LockGuard lock{m_mutex};
|
||||
V3LockGuard stoppedJobsLock{m_stoppedJobsMutex};
|
||||
V3LockGuard lock{m_mutex};
|
||||
|
||||
UASSERT(m_queue.empty(), "Resizing busy thread pool");
|
||||
// Shut down old threads
|
||||
@@ -38,6 +41,7 @@ void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE VL_EXCLUDES(m_mutex)
|
||||
m_stoppedJobs = 0;
|
||||
m_cv.notify_all();
|
||||
m_stoppedJobsCV.notify_all();
|
||||
m_exclusiveAccessThreadCV.notify_all();
|
||||
}
|
||||
while (!m_workers.empty()) {
|
||||
m_workers.front().join();
|
||||
@@ -53,6 +57,37 @@ void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE VL_EXCLUDES(m_mutex)
|
||||
}
|
||||
}
|
||||
|
||||
void V3ThreadPool::suspendMultithreading() VL_MT_SAFE VL_EXCLUDES(m_mutex)
|
||||
VL_EXCLUDES(m_stoppedJobsMutex) {
|
||||
V3LockGuard stoppedJobsLock{m_stoppedJobsMutex};
|
||||
if (!m_workers.empty()) { stopOtherThreads(); }
|
||||
|
||||
if (!m_mutex.try_lock()) {
|
||||
v3fatal("Tried to suspend thread pool when other thread uses it.");
|
||||
}
|
||||
V3LockGuard lock{m_mutex, std::adopt_lock_t{}};
|
||||
|
||||
UASSERT(m_queue.empty(), "Thread pool has pending jobs");
|
||||
UASSERT(m_jobsInProgress == 0, "Thread pool has jobs in progress");
|
||||
m_exclusiveAccess = true;
|
||||
m_multithreadingSuspended = true;
|
||||
}
|
||||
|
||||
void V3ThreadPool::resumeMultithreading() VL_MT_SAFE VL_EXCLUDES(m_mutex)
|
||||
VL_EXCLUDES(m_stoppedJobsMutex) {
|
||||
if (!m_mutex.try_lock()) { v3fatal("Tried to resume thread pool when other thread uses it."); }
|
||||
{
|
||||
V3LockGuard lock{m_mutex, std::adopt_lock_t{}};
|
||||
UASSERT(m_multithreadingSuspended, "Multithreading is not suspended");
|
||||
m_multithreadingSuspended = false;
|
||||
m_exclusiveAccess = false;
|
||||
}
|
||||
if (!m_workers.empty()) {
|
||||
V3LockGuard stoppedJobsLock{m_stoppedJobsMutex};
|
||||
resumeOtherThreads();
|
||||
}
|
||||
}
|
||||
|
||||
void V3ThreadPool::startWorker(V3ThreadPool* selfThreadp, int id) VL_MT_SAFE {
|
||||
selfThreadp->workerJobLoop(id);
|
||||
}
|
||||
@@ -74,10 +109,14 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
|
||||
|
||||
job = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
++m_jobsInProgress;
|
||||
}
|
||||
|
||||
// Execute the job
|
||||
job();
|
||||
// Note that a context switch can happen here. This means `m_jobsInProgress` could still
|
||||
// contain old value even after the job promise has been fulfilled.
|
||||
--m_jobsInProgress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,25 +129,22 @@ bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMut
|
||||
|
||||
void V3ThreadPool::waitForResumeRequest() VL_REQUIRES(m_stoppedJobsMutex) {
|
||||
++m_stoppedJobs;
|
||||
m_stoppedJobsCV.notify_all();
|
||||
m_stoppedJobsCV.wait(m_stoppedJobsMutex, [&]() VL_REQUIRES(m_stoppedJobsMutex) {
|
||||
return !m_stopRequested.load();
|
||||
});
|
||||
m_exclusiveAccessThreadCV.notify_one();
|
||||
m_stoppedJobsCV.wait(m_stoppedJobsMutex,
|
||||
[&]() VL_REQUIRES(m_stoppedJobsMutex) { return !m_stopRequested; });
|
||||
--m_stoppedJobs;
|
||||
m_stoppedJobsCV.notify_all();
|
||||
}
|
||||
|
||||
void V3ThreadPool::stopOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex)
|
||||
VL_REQUIRES(m_stoppedJobsMutex) {
|
||||
m_stopRequested = true;
|
||||
++m_stoppedJobs;
|
||||
m_stoppedJobsCV.notify_all();
|
||||
m_cv.notify_all();
|
||||
m_stoppedJobsCV.wait(m_stoppedJobsMutex, [&]() VL_REQUIRES(m_stoppedJobsMutex) {
|
||||
// count also the main thread
|
||||
return m_stoppedJobs == (m_workers.size() + 1);
|
||||
{
|
||||
V3LockGuard lock{m_mutex};
|
||||
m_cv.notify_all();
|
||||
}
|
||||
m_exclusiveAccessThreadCV.wait(m_stoppedJobsMutex, [&]() VL_REQUIRES(m_stoppedJobsMutex) {
|
||||
return m_stoppedJobs == m_workers.size();
|
||||
});
|
||||
--m_stoppedJobs;
|
||||
}
|
||||
|
||||
void V3ThreadPool::selfTestMtDisabled() {
|
||||
@@ -171,7 +207,25 @@ void V3ThreadPool::selfTest() {
|
||||
{
|
||||
const V3MtDisabledLockGuard mtDisabler{v3MtDisabledLock()};
|
||||
selfTestMtDisabled();
|
||||
{
|
||||
V3LockGuard lock{V3ThreadPool::s().m_mutex};
|
||||
UASSERT(V3ThreadPool::s().m_multithreadingSuspended,
|
||||
"Multithreading should be suspended at this point");
|
||||
}
|
||||
}
|
||||
{
|
||||
V3LockGuard lock{V3ThreadPool::s().m_mutex};
|
||||
UASSERT(!V3ThreadPool::s().m_multithreadingSuspended,
|
||||
"Multithreading should not be suspended at this point");
|
||||
}
|
||||
}
|
||||
|
||||
V3MtDisabledLock V3MtDisabledLock::s_mtDisabledLock;
|
||||
|
||||
void V3MtDisabledLock::lock() VL_ACQUIRE() VL_MT_SAFE {
|
||||
V3ThreadPool::s().suspendMultithreading();
|
||||
}
|
||||
|
||||
void V3MtDisabledLock::unlock() VL_RELEASE() VL_MT_SAFE {
|
||||
V3ThreadPool::s().resumeMultithreading();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user