Internals: Split Mutex class used in verilated code and verilator (#4048)

This commit is contained in:
Kamil Rakoczy 2023-04-11 13:23:24 +02:00 committed by GitHub
parent 0307d59c1f
commit b6dcec2710
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 249 additions and 74 deletions

View File

@ -30,6 +30,13 @@
#ifndef VERILATOR_VERILATED_H_
#define VERILATOR_VERILATED_H_
#define VERILATOR_VERILATED_H_INTERNAL_
#ifdef VERILATOR_INTERNAL_
// This file contains definition of VerilationMutex that should
// only be used by verilated code. Verilator itself should use
// mutex from V3Mutex.h. Make sure this file isn't included in
// verilator code.
#error "verilated.h should only be included in verilated code"
#endif
// clang-format off
#include "verilatedos.h"

View File

@ -92,6 +92,7 @@ endif
# -lfl not needed as Flex invoked with %nowrap option
LIBS = $(CFG_LIBS) -lm
CPPFLAGS += -DVERILATOR_INTERNAL_
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool

View File

@ -40,7 +40,7 @@ template <typename T>
class V3ConfigWildcardResolver final {
using Map = std::map<const std::string, T>;
mutable VerilatedMutex m_mutex; // protects members
mutable V3Mutex m_mutex; // protects members
Map m_mapWildcard VL_GUARDED_BY(m_mutex); // Wildcard strings to entities
Map m_mapResolved VL_GUARDED_BY(m_mutex); // Resolved strings to converged entities
public:
@ -50,21 +50,21 @@ public:
/// Update into maps from other
void update(const V3ConfigWildcardResolver& other) VL_MT_SAFE_EXCLUDES(m_mutex)
VL_EXCLUDES(other.m_mutex) {
VerilatedLockGuard lock{m_mutex};
VerilatedLockGuard otherLock{other.m_mutex};
V3LockGuard lock{m_mutex};
V3LockGuard otherLock{other.m_mutex};
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
}
// Access and create a (wildcard) entity
T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
V3LockGuard lock{m_mutex};
// Don't store into wildcards if the name is not a wildcard string
return m_mapWildcard[name];
}
// Access an entity and resolve wildcards that match it
T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
V3LockGuard lock{m_mutex};
// Lookup if it was resolved before, typically not
auto it = m_mapResolved.find(name);
if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second;
@ -85,7 +85,7 @@ public:
}
// Flush on update
void flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
V3LockGuard lock{m_mutex};
m_mapResolved.clear();
}
};

View File

@ -23,6 +23,7 @@
#include "V3Ast.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3Mutex.h"
//######################################################################

View File

@ -20,7 +20,7 @@
#include "config_build.h"
#include "verilatedos.h"
#include "verilated_threads.h"
#include "V3Mutex.h"
// Limited V3 headers here - this is a base class for Vlc etc
#include "V3String.h"
@ -315,7 +315,7 @@ private:
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
public:
VerilatedMutex m_mutex; // Make sure only single thread is in class
V3RecursiveMutex m_mutex; // Make sure only single thread is in class
string msgPrefix() VL_REQUIRES(m_mutex); // returns %Error/%Warn
string warnMore() VL_REQUIRES(m_mutex);
@ -408,57 +408,57 @@ public:
static void debugDefault(int level) VL_MT_UNSAFE { s().debugDefault(level); }
static int debugDefault() VL_MT_SAFE { return s().debugDefault(); }
static void errorLimit(int level) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().errorLimit(level);
}
static int errorLimit() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().errorLimit();
}
static void warnFatal(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().warnFatal(flag);
}
static bool warnFatal() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().warnFatal();
}
// returns %Error/%Warn
static string msgPrefix() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().msgPrefix();
}
static int errorCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().errorCount();
}
static bool pretendError(int errorCode) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().pretendError(errorCode);
}
static int warnCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().warnCount();
}
static bool errorContexted() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().errorContexted();
}
static void errorContexted(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().errorContexted(flag);
}
static void describedEachWarn(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().describedEachWarn(code, flag);
}
// METHODS
static void incErrors() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().incErrors();
}
static void incWarnings() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().incWarnings();
}
static void init();
@ -468,20 +468,20 @@ public:
static void abortIfWarnings();
// Suppress next %Warn if user has it off
static void suppressThisWarning() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().suppressThisWarning();
}
static void pretendError(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().pretendError(code, flag);
}
static string lineStr(const char* filename, int lineno) VL_PURE;
static V3ErrorCode errorCode() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().errorCode();
}
static void errorExitCb(V3ErrorGuarded::ErrorExitCb cb) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().errorExitCb(cb);
}
@ -492,7 +492,7 @@ public:
// streamed directly to cerr.
// Use with caution as this function isn't MT_SAFE.
static string warnMoreStandalone() VL_EXCLUDES(s().m_mutex) VL_MT_UNSAFE {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().warnMore();
}
// This function marks place in error message from which point message
@ -508,18 +508,18 @@ public:
// Internals for v3error()/v3fatal() macros only
// Error end takes the string stream to output, be careful to seek() as needed
static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().v3errorPrep(code);
}
static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
return s().v3errorStr();
}
static void vlAbort();
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE {
const VerilatedLockGuard guard{s().m_mutex};
const V3RecursiveLockGuard guard{s().m_mutex};
s().v3errorEnd(sstr, extra);
}
// We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal',

View File

@ -108,7 +108,7 @@ class V3FileDependImp final {
};
// MEMBERS
VerilatedMutex m_mutex; // Protects members
V3Mutex m_mutex; // Protects members
std::set<string> m_filenameSet VL_GUARDED_BY(m_mutex); // Files generated (elim duplicates)
std::set<DependFile> m_filenameList; // Files sourced/generated
@ -123,7 +123,7 @@ class V3FileDependImp final {
public:
// ACCESSOR METHODS
void addSrcDepend(const string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
const auto itFoundPair = m_filenameSet.insert(filename);
if (itFoundPair.second) {
DependFile df{filename, false};
@ -132,7 +132,7 @@ public:
}
}
void addTgtDepend(const string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
const auto itFoundPair = m_filenameSet.insert(filename);
if (itFoundPair.second) m_filenameList.insert(DependFile{filename, true});
}
@ -957,7 +957,7 @@ void V3OutCFile::putsGuard() {
class VIdProtectImp final {
// MEMBERS
VerilatedMutex m_mutex; // Protects members
V3Mutex m_mutex; // Protects members
std::map<const std::string, std::string> m_nameMap; // Map of old name into new name
std::unordered_set<std::string> m_newIdSet VL_GUARDED_BY(m_mutex); // Which new names exist
protected:
@ -979,7 +979,7 @@ public:
// METHODS
string passthru(const string& old) VL_MT_SAFE_EXCLUDES(m_mutex) {
if (!v3Global.opt.protectIds()) return old;
const VerilatedLockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
const auto it = m_nameMap.find(old);
if (it != m_nameMap.end()) {
// No way to go back and correct the older crypt name
@ -993,7 +993,7 @@ public:
}
string protectIf(const string& old, bool doIt) VL_MT_SAFE_EXCLUDES(m_mutex) {
if (!v3Global.opt.protectIds() || old.empty() || !doIt) return old;
const VerilatedLockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
const auto it = m_nameMap.find(old);
if (it != m_nameMap.end()) {
return it->second;

View File

@ -89,7 +89,7 @@ void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
FileLineSingleton::msgEnSetIdx_t FileLineSingleton::addMsgEnBitSet(const MsgEnBitSet& bitSet)
VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
V3LockGuard lock{m_mutex};
const auto pair = m_internedMsgEnIdxs.emplace(bitSet, 0);
msgEnSetIdx_t& idx = pair.first->second;
if (pair.second) {
@ -418,7 +418,7 @@ string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) {
}
};
string FileLine::warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE {
const VerilatedLockGuard guard{V3Error::s().m_mutex};
const V3RecursiveLockGuard guard{V3Error::s().m_mutex};
return warnOther();
}

View File

@ -20,10 +20,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "verilated_threads.h"
#include "V3Error.h"
#include "V3LangCode.h"
#include "V3Mutex.h"
#include <atomic>
#include <bitset>
@ -52,7 +51,7 @@ class FileLineSingleton final {
using MsgEnBitSet = std::bitset<V3ErrorCode::_ENUM_MAX>;
// MEMBERS
VerilatedMutex m_mutex; // protects members
V3Mutex m_mutex; // protects members
std::map<const std::string, fileNameIdx_t> m_namemap; // filenameno for each filename
std::deque<string> m_names; // filename text for each filenameno
std::deque<V3LangCode> m_languages; // language for each filenameno

164
src/V3Mutex.h Normal file
View File

@ -0,0 +1,164 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Thread pool for Verilator itself
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2023 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilator Mutex and LockGuard used only in verilator.
///
/// This file defines Mutex and LockGuard that is used in verilator
/// source code. It shouldn't be used by verilated code.
/// In contrast to VerilatedMutex that is used by verilated code,
/// this mutex can be configured and disabled when verilation is using
/// single thread.
///
/// This implementation also allows using different base mutex class in
/// wrapper V3Mutex class.
///
//*************************************************************************
#ifndef VERILATOR_V3MUTEX_H_
#define VERILATOR_V3MUTEX_H_ 1
#include "verilatedos.h"
#include <cstdlib>
#include <functional>
#include <mutex>
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding
// MutexConfig class that allows to configure how mutex and lockgurads behave
// once configured and locked, it cannot be changed. Configuration and lock needs to be
// done before starting any additional threads.
class V3MutexConfig final {
private:
// Allows to disable mutexes and lockguards.
// Use carefully as it can cause undefined behavior when used inappropriately.
// All mutexes needs to be unlocked.
bool m_enable = false; // Allows locking on mutexes, default don't lock mutexes
bool m_lockConfig = false; // After set, configuration cannot be changed
V3MutexConfig() = default;
~V3MutexConfig() = default;
public:
static V3MutexConfig& s() VL_MT_SAFE {
static V3MutexConfig s;
return s;
}
// configures class
void configure(bool enable) VL_MT_UNSAFE {
if (!s().m_lockConfig) {
s().m_enable = enable;
s().m_lockConfig = true;
} else {
// requires <iostream>
// avoided to reduce compile time
// std::cerr << "%Error: V3Mutex already configured." << std::endl;
std::abort();
}
}
bool lockConfig() VL_MT_SAFE { return m_lockConfig; }
bool enable() VL_MT_SAFE { return m_enable; }
};
/// Mutex, wrapped to allow -fthread_safety checks
template <typename T>
class VL_CAPABILITY("mutex") V3MutexImp final {
private:
T m_mutex; // Mutex
public:
/// Construct mutex (without locking it)
V3MutexImp() = default;
~V3MutexImp() = default;
const V3MutexImp& operator!() const { return *this; } // For -fthread_safety
/// Acquire/lock mutex
void lock() VL_ACQUIRE() VL_MT_SAFE {
if (V3MutexConfig::s().enable()) {
// Try to acquire the lock by spinning. If the wait is short,
// avoids a trap to the OS plus OS scheduler overhead.
if (VL_LIKELY(try_lock())) return; // Short circuit loop
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(try_lock())) return;
VL_CPU_RELAX();
}
// Spinning hasn't worked, pay the cost of blocking.
m_mutex.lock();
}
}
/// Release/unlock mutex
void unlock() VL_RELEASE() VL_MT_SAFE {
if (V3MutexConfig::s().enable()) { m_mutex.unlock(); }
}
/// Try to acquire mutex. Returns true on success, and false on failure.
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE {
return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true;
}
/// Acquire/lock mutex and check for stop request
/// It tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex.
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
/// limitations it needs to be placed here.
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
VL_ACQUIRE() VL_MT_SAFE {
if (V3MutexConfig::s().enable()) {
while (true) {
checkStopRequestFunction();
if (m_mutex.try_lock()) return;
VL_CPU_RELAX();
}
}
}
};
/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
template <typename T>
class VL_SCOPED_CAPABILITY V3LockGuardImp final {
VL_UNCOPYABLE(V3LockGuardImp);
private:
T& m_mutexr;
public:
/// Construct and hold given mutex lock until destruction or unlock()
explicit V3LockGuardImp(T& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
m_mutexr.lock();
}
/// Destruct and unlock the mutex
~V3LockGuardImp() VL_RELEASE() { m_mutexr.unlock(); }
/// Lock the mutex
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
/// Unlock the mutex
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
/// Acquire/lock mutex and check for stop request.
/// It tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex.
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
/// limitations it needs to be placed here.
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
VL_ACQUIRE() VL_MT_SAFE {
m_mutexr.lockCheckStopRequest(checkStopRequestFunction);
}
};
using V3Mutex = V3MutexImp<std::mutex>;
using V3RecursiveMutex = V3MutexImp<std::recursive_mutex>;
using V3LockGuard = V3LockGuardImp<V3Mutex>;
using V3RecursiveLockGuard = V3LockGuardImp<V3RecursiveMutex>;
#endif // guard

View File

@ -2232,8 +2232,10 @@ V3Number& V3Number::opNToI(const V3Number& lhs) {
const string& str = lhs.toString();
for (size_t n = 0; n < str.length(); ++n) {
const char c = str[str.length() - 1 - n];
for (size_t cbit = 0; cbit < 8; ++cbit)
setBit(n * 8 + cbit, VL_BITISSET_I(c, cbit) ? 1 : 0);
for (size_t cbit = 0; cbit < 8; ++cbit) {
const std::bitset<8> sbit{static_cast<unsigned long long>(c)};
setBit(n * 8 + cbit, sbit.test(cbit) ? 1 : 0);
}
}
return *this;
}

View File

@ -896,8 +896,8 @@ string V3Options::version() VL_PURE {
}
string V3Options::protectKeyDefaulted() VL_MT_SAFE {
static VerilatedMutex mutex;
const VerilatedLockGuard lock{mutex};
static V3Mutex mutex;
const V3LockGuard lock{mutex};
if (m_protectKey.empty()) {
// Create a key with a human-readable symbol-like name.
// This conversion drops ~2 bits of entropy out of 256, shouldn't matter.

View File

@ -22,6 +22,7 @@
#include "V3Error.h"
#include "V3LangCode.h"
#include "V3Mutex.h"
#include <map>
#include <set>

View File

@ -28,8 +28,6 @@
#include "config_build.h"
#include "verilatedos.h"
#include "verilated_threads.h"
// Limited V3 headers here - this is a base class for Vlc etc
#include "V3Os.h"
#include "V3String.h"

View File

@ -25,8 +25,10 @@ constexpr unsigned int V3ThreadPool::FUTUREWAITFOR_MS;
void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE {
// This function is not thread-safe and can result in race between threads
VerilatedLockGuard lock{m_mutex};
VerilatedLockGuard stoppedJobsLock{m_stoppedJobsMutex};
UASSERT(V3MutexConfig::s().lockConfig(),
"Mutex config needs to be locked before starting ThreadPool");
V3LockGuard lock{m_mutex};
V3LockGuard stoppedJobsLock{m_stoppedJobsMutex};
UASSERT(m_queue.empty(), "Resizing busy thread pool");
// Shut down old threads
m_shutdown = true;
@ -57,7 +59,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
waitIfStopRequested();
job_t job;
{
VerilatedLockGuard lock(m_mutex);
V3LockGuard lock(m_mutex);
m_cv.wait(lock, [&]() VL_REQUIRES(m_mutex) {
return !m_queue.empty() || m_shutdown || m_stopRequested;
});
@ -82,7 +84,7 @@ void V3ThreadPool::pushJob<void>(std::shared_ptr<std::promise<void>>& prom,
f();
prom->set_value();
} else {
const VerilatedLockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
m_queue.push([prom, f] {
f();
prom->set_value();
@ -95,7 +97,7 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA
if (willExecuteSynchronously()) {
exclusiveAccessJob();
} else {
VerilatedLockGuard stoppedJobLock{m_stoppedJobsMutex};
V3LockGuard stoppedJobLock{m_stoppedJobsMutex};
// if some other job already requested exclusive access
// wait until it stops
if (stopRequested()) { waitStopRequested(stoppedJobLock); }
@ -110,14 +112,13 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA
}
bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE {
VerilatedLockGuard stoppedJobLock(m_stoppedJobsMutex);
V3LockGuard stoppedJobLock(m_stoppedJobsMutex);
if (!stopRequested()) return false;
waitStopRequested(stoppedJobLock);
return true;
}
void V3ThreadPool::waitStopRequested(VerilatedLockGuard& stoppedJobLock)
VL_REQUIRES(m_stoppedJobsMutex) {
void V3ThreadPool::waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex) {
++m_stoppedJobs;
m_stoppedJobsCV.notify_all();
m_stoppedJobsCV.wait(
@ -126,8 +127,8 @@ void V3ThreadPool::waitStopRequested(VerilatedLockGuard& stoppedJobLock)
m_stoppedJobsCV.notify_all();
}
void V3ThreadPool::waitOtherThreads(VerilatedLockGuard& stoppedJobLock)
VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex) {
void V3ThreadPool::waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex)
VL_REQUIRES(m_stoppedJobsMutex) {
++m_stoppedJobs;
m_stoppedJobsCV.notify_all();
m_cv.notify_all();
@ -139,7 +140,7 @@ void V3ThreadPool::waitOtherThreads(VerilatedLockGuard& stoppedJobLock)
}
void V3ThreadPool::selfTest() {
VerilatedMutex commonMutex;
V3Mutex commonMutex;
int commonValue{0};
auto firstJob = [&](int sleep) -> void {
@ -151,7 +152,7 @@ void V3ThreadPool::selfTest() {
});
};
auto secondJob = [&](int sleep) -> void {
VerilatedLockGuard lock{commonMutex};
V3LockGuard lock{commonMutex};
lock.unlock();
s().waitIfStopRequested();
lock.lock();
@ -159,7 +160,7 @@ void V3ThreadPool::selfTest() {
commonValue = 1000;
};
auto thirdJob = [&](int sleep) -> void {
VerilatedLockGuard lock{commonMutex};
V3LockGuard lock{commonMutex};
std::this_thread::sleep_for(std::chrono::milliseconds{sleep});
lock.unlock();
s().requestExclusiveAccess([&]() { firstJob(sleep); });

View File

@ -17,7 +17,7 @@
#ifndef _V3THREADPOOL_H_
#define _V3THREADPOOL_H_ 1
#include "verilated_threads.h"
#include "V3Mutex.h"
#include <condition_variable>
#include <functional>
@ -34,7 +34,7 @@ class V3ThreadPool final {
static constexpr unsigned int FUTUREWAITFOR_MS = 100;
using job_t = std::function<void()>;
mutable VerilatedMutex m_mutex; // Mutex for use by m_queue
mutable V3Mutex m_mutex; // Mutex for use by m_queue
std::queue<job_t> m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs
// We don't need to guard this condition_variable as
// both `notify_one` and `notify_all` functions are atomic,
@ -42,7 +42,7 @@ class V3ThreadPool final {
// used by this condition_variable, so clang checks that we have mutex locked
std::condition_variable_any m_cv; // Conditions to wake up workers
std::list<std::thread> m_workers; // Worker threads
VerilatedMutex m_stoppedJobsMutex; // Used to signal stopped jobs
V3Mutex m_stoppedJobsMutex; // Used to signal stopped jobs
// Conditions to wake up stopped jobs
std::condition_variable_any m_stoppedJobsCV VL_GUARDED_BY(m_stoppedJobsMutex);
std::atomic_uint m_stoppedJobs{0}; // Currently stopped jobs waiting for wake up
@ -54,7 +54,7 @@ class V3ThreadPool final {
// CONSTRUCTORS
V3ThreadPool() = default;
~V3ThreadPool() {
VerilatedLockGuard lock{m_mutex};
V3LockGuard lock{m_mutex};
m_queue = {}; // make sure queue is empty
lock.unlock();
resize(0);
@ -106,15 +106,15 @@ private:
}
bool stopRequestedStandalone() VL_MT_SAFE_EXCLUDES(m_stoppedJobsMutex) {
const VerilatedLockGuard lock{m_stoppedJobsMutex};
const V3LockGuard lock{m_stoppedJobsMutex};
return stopRequested();
}
// Waits until exclusive access job completes its job
void waitStopRequested(VerilatedLockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex);
void waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex);
// Waits until all other jobs are stopped
void waitOtherThreads(VerilatedLockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex)
void waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex)
VL_REQUIRES(m_stoppedJobsMutex);
template <typename T>
@ -146,7 +146,7 @@ std::future<T> V3ThreadPool::enqueue(std::function<T()>&& f) VL_MT_SAFE {
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
std::future<T> result = prom->get_future();
pushJob(prom, std::move(f));
const VerilatedLockGuard guard{m_mutex};
const V3LockGuard guard{m_mutex};
m_cv.notify_one();
return result;
}
@ -157,7 +157,7 @@ void V3ThreadPool::pushJob(std::shared_ptr<std::promise<T>>& prom,
if (willExecuteSynchronously()) {
prom->set_value(f());
} else {
const VerilatedLockGuard guard{m_mutex};
const V3LockGuard guard{m_mutex};
m_queue.push([prom, f] { prom->set_value(f()); });
}
}

View File

@ -26,8 +26,8 @@
void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, const std::string& str)
VL_MT_SAFE_EXCLUDES(s_mutex) {
const VerilatedLockGuard lock{s_mutex};
if (filename == V3Options::getStdPackagePath()) return;
const V3LockGuard lock{s_mutex};
std::stringstream entry;
const size_t pos = str.find('\n');
entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" << filename << "\" -match \""
@ -51,12 +51,12 @@ void V3Waiver::write(const std::string& filename) VL_MT_SAFE_EXCLUDES(s_mutex) {
*ofp << "// 2. Keep the waiver permanently if you are sure this is okay\n";
*ofp << "// 3. Keep the waiver temporarily to suppress the output\n\n";
const VerilatedLockGuard lock{s_mutex};
const V3LockGuard lock{s_mutex};
if (s_waiverList.empty()) *ofp << "// No waivers needed - great!\n";
for (const auto& i : s_waiverList) *ofp << "// " << i << "\n\n";
}
VerilatedMutex V3Waiver::s_mutex;
V3Mutex V3Waiver::s_mutex;
V3Waiver::WaiverList V3Waiver::s_waiverList;

View File

@ -17,9 +17,8 @@
#ifndef VERILATOR_V3WAIVER_H_
#define VERILATOR_V3WAIVER_H_
#include "verilated_threads.h"
#include "V3Error.h"
#include "V3Mutex.h"
#include <string>
#include <vector>
@ -27,7 +26,7 @@
class V3Waiver final {
// TYPES
using WaiverList = std::vector<std::string>;
static VerilatedMutex s_mutex; // Protect members
static V3Mutex s_mutex; // Protect members
static WaiverList s_waiverList VL_GUARDED_BY(s_mutex);
public:

View File

@ -590,6 +590,8 @@ static void verilate(const string& argString) {
v3fatalSrc("VERILATOR_DEBUG_SKIP_IDENTICAL w/ --skip-identical: Changes found\n");
} // LCOV_EXCL_STOP
// Disable mutexes in single-thread verilation
V3MutexConfig::s().configure(v3Global.opt.verilateJobs() > 1 /*enable*/);
// Adjust thread pool size
V3ThreadPool::s().resize(v3Global.opt.verilateJobs());