diff --git a/include/verilatedos.h b/include/verilatedos.h index f065ed6f6..a3a515518 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -94,20 +94,6 @@ // Function requires a capability inbound (-fthread-safety) #define VL_CAPABILITY(x) \ VL_CLANG_ATTR(capability(x)) -// Function requires not having a capability inbound (-fthread-safety) -#define VL_REQUIRES(x) \ - VL_CLANG_ATTR(annotate("REQUIRES")) \ - VL_CLANG_ATTR(requires_capability(x)) -// Name of capability/lock (-fthread-safety) -#define VL_GUARDED_BY(x) \ - VL_CLANG_ATTR(annotate("GUARDED_BY")) \ - VL_CLANG_ATTR(guarded_by(x)) -// The data that the annotated pointer points to is protected by the given capability. -// The pointer itself is not protected. -// Allowed on: pointer data member. (-fthread-safety) -#define VL_PT_GUARDED_BY(x) \ - VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \ - VL_CLANG_ATTR(pt_guarded_by(x)) // Name of mutex protecting this variable (-fthread-safety) #define VL_EXCLUDES(x) \ VL_CLANG_ATTR(annotate("EXCLUDES")) \ @@ -124,6 +110,32 @@ #define VL_ASSERT_CAPABILITY(x) \ VL_CLANG_ATTR(assert_capability(x)) +// Require mutex locks only in code units which work with enabled multi-threading. +#if !defined(VL_MT_DISABLED_CODE_UNIT) +// Function requires not having a capability inbound (-fthread-safety) +# define VL_REQUIRES(x) \ + VL_CLANG_ATTR(annotate("REQUIRES")) \ + VL_CLANG_ATTR(requires_capability(x)) +// Name of capability/lock (-fthread-safety) +# define VL_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("GUARDED_BY")) \ + VL_CLANG_ATTR(guarded_by(x)) +// The data that the annotated pointer points to is protected by the given capability. +// The pointer itself is not protected. +// Allowed on: pointer data member. (-fthread-safety) +# define VL_PT_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \ + VL_CLANG_ATTR(pt_guarded_by(x)) +#else +// Keep annotations for clang_check_attributes +# define VL_REQUIRES(x) \ + VL_CLANG_ATTR(annotate("REQUIRES")) +# define VL_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("GUARDED_BY")) +# define VL_PT_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) +#endif + // Defaults for unsupported compiler features #ifndef VL_ATTR_ALWINLINE # define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing @@ -430,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() Type(const Type& other) = delete; \ Type& operator=(const Type&) = delete +// Declare a class as unmovable; put after a private: +#define VL_UNMOVABLE(Type) \ + Type(Type&& other) = delete; \ + Type& operator=(Type&&) = delete + //========================================================================= // Verilated function size macros diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47471a1bd..396d79fd6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,6 +151,7 @@ set(HEADERS V3Table.h V3Task.h V3ThreadPool.h + V3ThreadSafety.h V3Timing.h V3Trace.h V3TraceDecl.h diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 4d364ef6c..471af2c79 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -289,6 +289,7 @@ NON_STANDALONE_HEADERS = \ V3AstNodeExpr.h \ V3AstNodeOther.h \ V3DfgVertices.h \ + V3ThreadPool.h \ V3WidthCommit.h \ AST_DEFS := \ diff --git a/src/V3ThreadPool.cpp b/src/V3ThreadPool.cpp index 36a6d6c50..d19a449bd 100644 --- a/src/V3ThreadPool.cpp +++ b/src/V3ThreadPool.cpp @@ -111,6 +111,10 @@ void V3ThreadPool::stopOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) --m_stoppedJobs; } +void V3ThreadPool::selfTestMtDisabled() { + // empty +} + void V3ThreadPool::selfTest() { V3Mutex commonMutex; int commonValue{0}; @@ -164,4 +168,10 @@ void V3ThreadPool::selfTest() { futuresInt.push_back(s().enqueue(forthJob)); auto result = V3ThreadPool::waitForFutures(futuresInt); UASSERT(result.back() == 1234, "unexpected future result = " << result.back()); + { + const V3MtDisabledLockGuard mtDisabler{v3MtDisabledLock()}; + selfTestMtDisabled(); + } } + +V3MtDisabledLock V3MtDisabledLock::s_mtDisabledLock; diff --git a/src/V3ThreadPool.h b/src/V3ThreadPool.h index ad47ec093..0ecb6fde8 100644 --- a/src/V3ThreadPool.h +++ b/src/V3ThreadPool.h @@ -17,7 +17,12 @@ #ifndef _V3THREADPOOL_H_ #define _V3THREADPOOL_H_ 1 +#if defined(VL_MT_DISABLED_CODE_UNIT) +#error "Source file has been declared as MT_DISABLED, threads use is prohibited." +#endif + #include "V3Mutex.h" +#include "V3ThreadSafety.h" #include #include @@ -163,6 +168,7 @@ public: } static void selfTest(); + static void selfTestMtDisabled() VL_MT_DISABLED; private: template diff --git a/src/V3ThreadSafety.h b/src/V3ThreadSafety.h new file mode 100644 index 000000000..12cc6e627 --- /dev/null +++ b/src/V3ThreadSafety.h @@ -0,0 +1,71 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Definitions for thread safety checing +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#ifndef VERILATOR_V3THREADSAFETY_H_ +#define VERILATOR_V3THREADSAFETY_H_ + +#include + +#include + +// A class that works as an indicator of MT_DISABLED context. +// It uses Clang's thread safety analysis (-fthread-safety) to do its work. +// Its use will most likely be optimized out (or at least reduced to a few insignificant symbols +// or instructions) during compilation. +class VL_CAPABILITY("lock") V3MtDisabledLock final { + friend class V3MtDisabledLockInstanceAccessor; + + static V3MtDisabledLock s_mtDisabledLock; + + constexpr V3MtDisabledLock() = default; + ~V3MtDisabledLock() = default; + VL_UNCOPYABLE(V3MtDisabledLock); + VL_UNMOVABLE(V3MtDisabledLock); + +public: + constexpr void lock() VL_ACQUIRE() VL_MT_SAFE {} + constexpr void unlock() VL_RELEASE() VL_MT_SAFE {} + + static constexpr V3MtDisabledLock& instance() + VL_RETURN_CAPABILITY(V3MtDisabledLock::s_mtDisabledLock) { + return s_mtDisabledLock; + } +}; + +// A class providing mutable access to V3MtDisabledLock::s_mtDisabledLock. +// This is a class because VL_RETURN_CAPABILITY works only on methods, not free functions. +// This is not a method in V3MtDisabledLock itself as a method declaration inside #ifdef block +// woudl break ODR. +class V3MtDisabledLockInstanceAccessor final { +public: + constexpr V3MtDisabledLock& operator()() const + VL_RETURN_CAPABILITY(V3MtDisabledLock::s_mtDisabledLock) { + return V3MtDisabledLock::s_mtDisabledLock; + } +}; +// Create a global object which can be called like a function. +static constexpr V3MtDisabledLockInstanceAccessor v3MtDisabledLock VL_ATTR_UNUSED; + +using V3MtDisabledLockGuard = V3LockGuardImp; + +// Annotated function can be called only in MT_DISABLED context, i.e. either in a code unit +// compiled with VL_MT_DISABLED_CODE_UNIT preprocessor definition, or after obtaining a lock on +// v3MtDisabledLock(). +#define VL_MT_DISABLED \ + VL_CLANG_ATTR(annotate("MT_DISABLED")) \ + VL_REQUIRES(V3MtDisabledLock::instance()) + +#endif // guard