Internals: Split Mutex class used in verilated code and verilator (#4048)
This commit is contained in:
parent
0307d59c1f
commit
b6dcec2710
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Mutex.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "V3Error.h"
|
||||
#include "V3LangCode.h"
|
||||
#include "V3Mutex.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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); });
|
||||
|
|
|
|||
|
|
@ -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()); });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue