Internals: Add additional mutex exclusion checks. No functional change.

This commit is contained in:
Wilson Snyder 2021-03-06 18:29:11 -05:00
parent 47dcbd4b8a
commit 8c3ad591ae
6 changed files with 24 additions and 21 deletions

View File

@ -241,12 +241,12 @@ private:
public: public:
// PUBLIC METHODS // PUBLIC METHODS
void clear() VL_EXCLUDES(m_mutex) { void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce(); Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
clearGuts(); clearGuts();
} }
void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) { void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce(); Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
if (matchp && matchp[0]) { if (matchp && matchp[0]) {
@ -261,25 +261,25 @@ public:
m_items = newlist; m_items = newlist;
} }
} }
void zero() VL_EXCLUDES(m_mutex) { void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce(); Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
for (const auto& itemp : m_items) itemp->zero(); for (const auto& itemp : m_items) itemp->zero();
} }
// We assume there's always call to i/f/p in that order // We assume there's always call to i/f/p in that order
void inserti(VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) { void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
assert(!m_insertp); assert(!m_insertp);
m_insertp = itemp; m_insertp = itemp;
} }
void insertf(const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) { void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
m_insertFilenamep = filenamep; m_insertFilenamep = filenamep;
m_insertLineno = lineno; m_insertLineno = lineno;
} }
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS], void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_EXCLUDES(m_mutex) { const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
assert(m_insertp); assert(m_insertp);
// First two key/vals are filename // First two key/vals are filename
@ -336,7 +336,7 @@ public:
m_insertp = nullptr; m_insertp = nullptr;
} }
void write(const char* filename) VL_EXCLUDES(m_mutex) { void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce(); Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
#ifndef VM_COVERAGE #ifndef VM_COVERAGE

View File

@ -99,13 +99,13 @@ private:
public: public:
// METHODS // METHODS
//// Add message to queue (called by producer) //// Add message to queue (called by producer)
void post(const VerilatedMsg& msg) VL_EXCLUDES(m_mutex) { void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex); const VerilatedLockGuard lock(m_mutex);
m_queue.insert(msg); // Pass by value to copy the message into queue m_queue.insert(msg); // Pass by value to copy the message into queue
++m_depth; ++m_depth;
} }
/// Service queue until completion (called by consumer) /// Service queue until completion (called by consumer)
void process() VL_EXCLUDES(m_mutex) { void process() VL_MT_SAFE_EXCLUDES(m_mutex) {
// Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size, // Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
// but on the reader side it's 4x faster to test an atomic then getting a mutex // but on the reader side it's 4x faster to test an atomic then getting a mutex
while (m_depth) { while (m_depth) {

View File

@ -114,7 +114,7 @@ void VlThreadPool::tearDownProfilingClientThread() {
t_profilep = nullptr; t_profilep = nullptr;
} }
void VlThreadPool::setupProfilingClientThread() { void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
assert(!t_profilep); assert(!t_profilep);
t_profilep = new ProfileTrace; t_profilep = new ProfileTrace;
// Reserve some space in the thread-local profiling buffer; // Reserve some space in the thread-local profiling buffer;
@ -126,7 +126,7 @@ void VlThreadPool::setupProfilingClientThread() {
} }
} }
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) { void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex); const VerilatedLockGuard lk(m_mutex);
for (const auto& profilep : m_allProfiles) { for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec. // Every thread's profile trace gets a copy of rec.
@ -134,7 +134,8 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) {
} }
} }
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) { void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex); const VerilatedLockGuard lk(m_mutex);
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep);); VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););

View File

@ -212,7 +212,7 @@ public:
~VlWorkerThread(); ~VlWorkerThread();
// METHODS // METHODS
inline void dequeWork(ExecRec* workp) { inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Spin for a while, waiting for new data // Spin for a while, waiting for new data
for (int i = 0; i < VL_LOCK_SPINS; ++i) { for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { // if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
@ -233,7 +233,8 @@ public:
m_ready_size.fetch_sub(1, std::memory_order_relaxed); m_ready_size.fetch_sub(1, std::memory_order_relaxed);
} }
inline void wakeUp() { addTask(nullptr, false, nullptr); } inline void wakeUp() { addTask(nullptr, false, nullptr); }
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) { inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify; bool notify;
{ {
const VerilatedLockGuard lk(m_mutex); const VerilatedLockGuard lk(m_mutex);
@ -286,11 +287,11 @@ public:
t_profilep->emplace_back(); t_profilep->emplace_back();
return &(t_profilep->back()); return &(t_profilep->back());
} }
void profileAppendAll(const VlProfileRec& rec); void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t ticksElapsed); void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex);
// In profiling mode, each executing thread must call // In profiling mode, each executing thread must call
// this once to setup profiling state: // this once to setup profiling state:
void setupProfilingClientThread(); void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
void tearDownProfilingClientThread(); void tearDownProfilingClientThread();
private: private:

View File

@ -48,21 +48,21 @@ private:
public: public:
// Put an element at the back of the queue // Put an element at the back of the queue
void put(T value) { void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex); VerilatedLockGuard lock(m_mutex);
m_queue.push_back(value); m_queue.push_back(value);
m_cv.notify_one(); m_cv.notify_one();
} }
// Put an element at the front of the queue // Put an element at the front of the queue
void put_front(T value) { void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex); VerilatedLockGuard lock(m_mutex);
m_queue.push_front(value); m_queue.push_front(value);
m_cv.notify_one(); m_cv.notify_one();
} }
// Get an element from the front of the queue. Blocks if none available // Get an element from the front of the queue. Blocks if none available
T get() { T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex); VerilatedLockGuard lock(m_mutex);
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty()); assert(!m_queue.empty());
@ -72,7 +72,7 @@ public:
} }
// Non blocking get // Non blocking get
bool tryGet(T& result) { bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lockGuard(m_mutex); const VerilatedLockGuard lockGuard(m_mutex);
if (m_queue.empty()) return false; if (m_queue.empty()) return false;
result = m_queue.front(); result = m_queue.front();

View File

@ -148,6 +148,7 @@
#define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED #define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED
#define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only #define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only
///< during normal operation (post-init) ///< during normal operation (post-init)
#define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex) ///< Threadsafe and uses given mutex
#define VL_MT_UNSAFE ///< Comment tag that function is not threadsafe when VL_THREADED #define VL_MT_UNSAFE ///< Comment tag that function is not threadsafe when VL_THREADED
#define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, #define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED,
///< protected to make sure single-caller ///< protected to make sure single-caller