diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 377d5abbe..69b9bc009 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -241,12 +241,12 @@ private: public: // PUBLIC METHODS - void clear() VL_EXCLUDES(m_mutex) { + void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); clearGuts(); } - void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) { + void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); if (matchp && matchp[0]) { @@ -261,25 +261,25 @@ public: m_items = newlist; } } - void zero() VL_EXCLUDES(m_mutex) { + void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); for (const auto& itemp : m_items) itemp->zero(); } // 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); assert(!m_insertp); 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); m_insertFilenamep = filenamep; m_insertLineno = lineno; } 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); assert(m_insertp); // First two key/vals are filename @@ -336,7 +336,7 @@ public: 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(); const VerilatedLockGuard lock(m_mutex); #ifndef VM_COVERAGE diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 97901fa2a..75cb683b5 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -99,13 +99,13 @@ private: public: // METHODS //// 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); m_queue.insert(msg); // Pass by value to copy the message into queue ++m_depth; } /// 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, // but on the reader side it's 4x faster to test an atomic then getting a mutex while (m_depth) { diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 580179bac..132e4e29a 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -114,7 +114,7 @@ void VlThreadPool::tearDownProfilingClientThread() { t_profilep = nullptr; } -void VlThreadPool::setupProfilingClientThread() { +void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) { assert(!t_profilep); t_profilep = new ProfileTrace; // 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); for (const auto& profilep : m_allProfiles) { // 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); VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep);); diff --git a/include/verilated_threads.h b/include/verilated_threads.h index f0b88bbd5..3082898a8 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -212,7 +212,7 @@ public: ~VlWorkerThread(); // 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 for (int i = 0; i < VL_LOCK_SPINS; ++i) { 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); } 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; { const VerilatedLockGuard lk(m_mutex); @@ -286,11 +287,11 @@ public: t_profilep->emplace_back(); return &(t_profilep->back()); } - void profileAppendAll(const VlProfileRec& rec); - void profileDump(const char* filenamep, vluint64_t ticksElapsed); + void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex); + void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex); // In profiling mode, each executing thread must call // this once to setup profiling state: - void setupProfilingClientThread(); + void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex); void tearDownProfilingClientThread(); private: diff --git a/include/verilated_trace.h b/include/verilated_trace.h index fb7cbe90a..dd98ce2b1 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -48,21 +48,21 @@ private: public: // 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); m_queue.push_back(value); m_cv.notify_one(); } // 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); m_queue.push_front(value); m_cv.notify_one(); } // 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); m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); assert(!m_queue.empty()); @@ -72,7 +72,7 @@ public: } // Non blocking get - bool tryGet(T& result) { + bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lockGuard(m_mutex); if (m_queue.empty()) return false; result = m_queue.front(); diff --git a/include/verilatedos.h b/include/verilatedos.h index abd61195f..c8d6a9dc3 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -148,6 +148,7 @@ #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 ///< 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_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, ///< protected to make sure single-caller