diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cd2b50b..6abededc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ set (SystemCCCI_SOVERSION "${SystemCCCI_VERSION_MAJOR}.${SystemCCCI_VERSION_MINO # lib-. (default: OFF) option(CCI_ENABLE_CFG "Build SystemCCI configuration library" ON) +option(CCI_ENABLE_THREAD_SAFETY "Enable thread-safe broker and parameter access" OFF) option(SYSTEMCCCI_BUILD_TESTS "Build tests & examples" ON) option(CCI_ENABLE_INSPECTION "Build SystemCCI inspection library" ON) option(BUILD_SOURCE_DOCUMENTATION "Build source documentation with Doxygen." OFF) diff --git a/configuration/CMakeLists.txt b/configuration/CMakeLists.txt index 5a7419f6..8c3cd3e5 100644 --- a/configuration/CMakeLists.txt +++ b/configuration/CMakeLists.txt @@ -256,11 +256,14 @@ if (NOT SystemC_TARGET_ARCH) endif (NOT SystemC_TARGET_ARCH) -list (APPEND CMAKE_PREFIX_PATH /opt/systemc) +list (APPEND CMAKE_PREFIX_PATH /opt/systemc $ENV{SYSTEMC_HOME}) IF (NOT SystemCLanguage_FOUND AND NOT TARGET SystemC::systemc) - list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) - find_package(SystemCLanguage REQUIRED) + find_package(SystemCLanguage CONFIG QUIET NO_CMAKE_PACKAGE_REGISTRY) + if (NOT TARGET SystemC::systemc) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) + find_package(SystemCLanguage REQUIRED) + endif() ENDIF () message (STATUS "Using SystemC ${SystemCLanguage_VERSION} (${SystemCLanguage_DIR})") IF ( "${SystemC_CXX_STANDARD}" AND NOT "${SystemC_CXX_STANDARD}" EQUAL "${CMAKE_CXX_STANDARD}") diff --git a/configuration/examples/CMakeLists.txt b/configuration/examples/CMakeLists.txt index 634c4fbb..9b10ccd1 100644 --- a/configuration/examples/CMakeLists.txt +++ b/configuration/examples/CMakeLists.txt @@ -175,9 +175,12 @@ endfunction() if (SYSTEMCCCI_BUILD_TESTS) include_directories(cci/common/inc/) - file(GLOB EXAMPLES cci/ex*) foreach(example ${EXAMPLES}) + # ex20_Thread_Safety requires CCI_ENABLE_THREAD_SAFETY + if ("${example}" MATCHES "ex20_Thread_Safety" AND NOT CCI_ENABLE_THREAD_SAFETY) + continue() + endif() add_test_exe(${example}) endforeach() endif() diff --git a/configuration/examples/cci/ex20_Thread_Safety/ex20_Thread_Safety.cpp b/configuration/examples/cci/ex20_Thread_Safety/ex20_Thread_Safety.cpp new file mode 100644 index 00000000..6e4b4437 --- /dev/null +++ b/configuration/examples/cci/ex20_Thread_Safety/ex20_Thread_Safety.cpp @@ -0,0 +1,235 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + ****************************************************************************/ + +/** + * @file ex20_Thread_Safety.cpp + * @brief Demonstrates CCI thread-safety issues when accessing the broker + * from multiple threads concurrently. + * + * This test creates a broker with preset values, then spawns worker threads + * that read presets and param handles concurrently. Without thread-safety + * mechanisms in the broker, this produces data races (detectable with TSan). + * + * It also demonstrates using a custom originator to access the broker + * from non-SystemC threads, avoiding the sc_get_current_object() issue. + */ + +#include +#include + +#include +#include +#include +#include + +/* Simple module that creates a CCI param */ +SC_MODULE(my_module) { + cci::cci_param p_value; + + SC_CTOR(my_module) + : p_value("value", 42, "a test parameter") + { + SC_REPORT_INFO("my_module", "Constructed"); + } +}; + +/* Worker thread function: reads broker state concurrently. + * Uses a pre-created broker handle with a custom originator + * (not dependent on sc_get_current_object). */ +static void worker_read(cci::cci_broker_handle broker, int id, + std::atomic& errors) { + try { + for (int i = 0; i < 100; i++) { + /* Read preset values — concurrent with other readers and writers */ + auto val = broker.get_preset_cci_value("mod.value"); + + /* Get param handles — concurrent access to param registry */ + auto handles = broker.get_param_handles(); + + /* Get unconsumed presets — iterates internal maps */ + auto presets = broker.get_unconsumed_preset_values(); + } + } catch (const std::exception& e) { + std::cerr << "Thread " << id << " exception: " << e.what() << "\n"; + errors++; + } +} + +/* Worker thread function: writes preset values concurrently */ +static void worker_write(cci::cci_broker_handle broker, int id, + std::atomic& errors) { + try { + for (int i = 0; i < 100; i++) { + /* Write preset values — concurrent with readers and other writers */ + broker.set_preset_cci_value( + "dynamic_param_" + std::to_string(id) + "_" + std::to_string(i), + cci::cci_value(i)); + + /* Also read while writing */ + auto val = broker.get_preset_cci_value("mod.value"); + } + } catch (const std::exception& e) { + std::cerr << "Thread " << id << " exception: " << e.what() << "\n"; + errors++; + } +} + +int sc_main(int argc, char *argv[]) { + /* Create broker and register it */ + cci_utils::consuming_broker global_broker("Global Broker"); + cci::cci_register_broker(global_broker); + + /* Create a custom originator for non-SystemC threads */ + cci::cci_originator worker_orig("worker_thread"); + auto worker_broker = global_broker.create_broker_handle(worker_orig); + + /* Set some initial preset values */ + cci::cci_originator main_orig("sc_main"); + auto main_broker = global_broker.create_broker_handle(main_orig); + main_broker.set_preset_cci_value("mod.value", cci::cci_value(100)); + main_broker.set_preset_cci_value("mod.other", cci::cci_value(200)); + + /* Create a module (consumes the preset) */ + my_module mod("mod"); + + SC_REPORT_INFO("sc_main", "Starting concurrent access test..."); + + std::atomic errors{0}; + const int NUM_READERS = 4; + const int NUM_WRITERS = 2; + + /* Spawn reader and writer threads — all using the worker_broker handle + * with the custom originator (no sc_get_current_object dependency) */ + std::vector threads; + + for (int i = 0; i < NUM_READERS; i++) { + threads.emplace_back(worker_read, worker_broker, i, std::ref(errors)); + } + for (int i = 0; i < NUM_WRITERS; i++) { + threads.emplace_back(worker_write, worker_broker, + NUM_READERS + i, std::ref(errors)); + } + + /* Join all threads */ + for (auto& t : threads) { + t.join(); + } + + if (errors == 0) { + SC_REPORT_INFO("sc_main", + "All threads completed without exceptions. " + "NOTE: absence of crashes does NOT prove thread safety. " + "Run with ThreadSanitizer (TSan) to detect data races."); + } else { + SC_REPORT_ERROR("sc_main", + ("Threads reported " + std::to_string(errors.load()) + + " error(s)").c_str()); + } + + /* Also demonstrate: reading params from a non-SystemC thread works + * with the custom originator — no sc_get_current_object needed */ + std::thread param_reader([&worker_broker]() { + auto h = worker_broker.get_param_handle("mod.value"); + if (h.is_valid()) { + int val = h.get_cci_value().get_int(); + std::cout << "Worker thread read mod.value = " << val << "\n"; + } + }); + param_reader.join(); + + /* Performance measurement: read-heavy workload */ + SC_REPORT_INFO("sc_main", "Starting performance measurement..."); + + const int ITERATIONS = 100000; + const int NUM_THREADS = 4; + + /* Single-threaded baseline */ + auto t0 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < ITERATIONS; i++) { + auto h = main_broker.get_param_handle("mod.value"); + if (h.is_valid()) h.get_cci_value(); + } + auto t1 = std::chrono::high_resolution_clock::now(); + auto single_us = std::chrono::duration_cast(t1 - t0).count(); + + /* Multi-threaded: same total work split across threads */ + auto t2 = std::chrono::high_resolution_clock::now(); + { + std::vector perf_threads; + for (int t = 0; t < NUM_THREADS; t++) { + perf_threads.emplace_back([&worker_broker, ITERATIONS, NUM_THREADS]() { + for (int i = 0; i < ITERATIONS / NUM_THREADS; i++) { + auto h = worker_broker.get_param_handle("mod.value"); + if (h.is_valid()) h.get_cci_value(); + } + }); + } + for (auto& t : perf_threads) t.join(); + } + auto t3 = std::chrono::high_resolution_clock::now(); + auto multi_us = std::chrono::duration_cast(t3 - t2).count(); + + std::cout << "Performance: " << ITERATIONS << " get_param_handle + get_cci_value\n"; + std::cout << " Single-threaded: " << single_us << " us (" + << (single_us * 1000 / ITERATIONS) << " ns/op)\n"; + std::cout << " Multi-threaded (" << NUM_THREADS << " threads): " + << multi_us << " us (" + << (multi_us * 1000 / ITERATIONS) << " ns/op)\n"; + std::cout << " Overhead: " << (multi_us * 100 / std::max(single_us, (decltype(single_us))1) - 100) + << "%\n"; + + /* Cached handle benchmark — more realistic (handle created once, reused) */ + auto t4 = std::chrono::high_resolution_clock::now(); + { + auto h = main_broker.get_param_handle("mod.value"); + for (int i = 0; i < ITERATIONS; i++) { + if (h.is_valid()) h.get_cci_value(); + } + } + auto t5 = std::chrono::high_resolution_clock::now(); + auto cached_single_us = std::chrono::duration_cast(t5 - t4).count(); + + auto t6 = std::chrono::high_resolution_clock::now(); + { + std::vector perf_threads; + for (int t = 0; t < NUM_THREADS; t++) { + perf_threads.emplace_back([&worker_broker, ITERATIONS, NUM_THREADS]() { + auto h = worker_broker.get_param_handle("mod.value"); + for (int i = 0; i < ITERATIONS / NUM_THREADS; i++) { + if (h.is_valid()) h.get_cci_value(); + } + }); + } + for (auto& t : perf_threads) t.join(); + } + auto t7 = std::chrono::high_resolution_clock::now(); + auto cached_multi_us = std::chrono::duration_cast(t7 - t6).count(); + + std::cout << "\nCached handle: " << ITERATIONS << " get_cci_value (handle reused)\n"; + std::cout << " Single-threaded: " << cached_single_us << " us (" + << (cached_single_us * 1000 / ITERATIONS) << " ns/op)\n"; + std::cout << " Multi-threaded (" << NUM_THREADS << " threads): " + << cached_multi_us << " us (" + << (cached_multi_us * 1000 / ITERATIONS) << " ns/op)\n"; + std::cout << " Overhead: " << (cached_multi_us * 100 / std::max(cached_single_us, (decltype(cached_single_us))1) - 100) + << "%\n"; + + SC_REPORT_INFO("sc_main", "Test complete."); + return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/configuration/src/CMakeLists.txt b/configuration/src/CMakeLists.txt index 5d288d49..2621c9ce 100644 --- a/configuration/src/CMakeLists.txt +++ b/configuration/src/CMakeLists.txt @@ -108,6 +108,7 @@ target_compile_definitions ( $<$:WIN32> $<$,$,$>>: SC_WIN_DLL> + $<$:CCI_THREAD_SAFE> PRIVATE SC_BUILD SC_INCLUDE_FX diff --git a/configuration/src/cci/cfg/cci_originator.cpp b/configuration/src/cci/cfg/cci_originator.cpp index 8f7720a5..48ab0e4d 100644 --- a/configuration/src/cci/cfg/cci_originator.cpp +++ b/configuration/src/cci/cfg/cci_originator.cpp @@ -20,12 +20,29 @@ #include "cci/cfg/cci_originator.h" #include +#include #include "cci/cfg/cci_config_macros.h" #include "cci/cfg/cci_report_handler.h" CCI_OPEN_NAMESPACE_ +/// Captured SystemC thread ID — set on first originator construction. +/// Used to detect non-SystemC threads where sc_get_current_object() +/// is unreliable. +static std::thread::id sysc_thread_id{}; +static bool sysc_thread_id_set = false; + +static bool is_on_sysc_thread() { + if (!sysc_thread_id_set) { + // First call — assume we're on the SystemC thread + sysc_thread_id = std::this_thread::get_id(); + sysc_thread_id_set = true; + return true; + } + return std::this_thread::get_id() == sysc_thread_id; +} + cci_originator::cci_originator(const std::string& originator_name) : m_originator_obj() , m_originator_str() @@ -143,6 +160,7 @@ bool cci_originator::operator<(const cci_originator& originator) const { } sc_core::sc_object *cci_originator::current_originator_object() { + if (!is_on_sysc_thread()) return NULL; return sc_core::sc_get_current_object(); } diff --git a/configuration/src/cci/cfg/cci_originator.h b/configuration/src/cci/cfg/cci_originator.h index 71d28e05..6750b320 100644 --- a/configuration/src/cci/cfg/cci_originator.h +++ b/configuration/src/cci/cfg/cci_originator.h @@ -21,6 +21,7 @@ #define CCI_CFG_CCI_ORIGINATOR_H_INCLUDED_ #include "cci/core/cci_cmnhdr.h" +#include "cci/cfg/cci_config_macros.h" CCI_OPEN_NAMESPACE_ @@ -48,11 +49,13 @@ class cci_originator : m_originator_obj(), m_originator_str() {} public: - /// Default Constructor assumes current module is the originator + /// Default Constructor assumes current module is the originator. + /// Outside the SystemC hierarchy (e.g. non-SystemC threads), + /// creates an originator with the unknown/default name. inline cci_originator() : m_originator_obj(current_originator_object()), - m_originator_str(NULL) { - check_is_valid(); + m_originator_str(m_originator_obj ? NULL + : new std::string(CCI_UNKNOWN_ORIGINATOR_STRING_)) { } /// Constructor with an originator name diff --git a/configuration/src/cci/cfg/cci_param_typed.h b/configuration/src/cci/cfg/cci_param_typed.h index 0fcf14f8..91cc5228 100644 --- a/configuration/src/cci/cfg/cci_param_typed.h +++ b/configuration/src/cci/cfg/cci_param_typed.h @@ -48,12 +48,21 @@ class cci_param_typed_handle; ///@cond CCI_HIDDEN_FROM_DOXYGEN namespace cci_impl { /// implementation defined helper to set/reset a boolean flag +#ifdef CCI_THREAD_SAFE +struct scoped_true { + explicit scoped_true(std::atomic& ref) : ref_(ref) { ref_ = true; } + ~scoped_true() { ref_ = false; } +private: + std::atomic& ref_; +}; +#else struct scoped_true { explicit scoped_true(bool& ref) : ref_(ref) { ref_ = true; } ~scoped_true() { ref_ = false; } private: bool& ref_; -}; // class scoped_true +}; +#endif } // namespace cci_impl ///@endcond diff --git a/configuration/src/cci/cfg/cci_param_untyped.cpp b/configuration/src/cci/cfg/cci_param_untyped.cpp index 30912121..ed190e10 100644 --- a/configuration/src/cci/cfg/cci_param_untyped.cpp +++ b/configuration/src/cci/cfg/cci_param_untyped.cpp @@ -39,7 +39,7 @@ cci_param_untyped::cci_param_untyped(const std::string& name, const cci_originator& originator) : m_description(desc), m_lock_pwd(NULL), m_broker_handle(broker_handle), m_value_origin(originator), - m_originator(originator), fast_read(false),fast_write(false) + m_originator(originator) { if(name_type == CCI_ABSOLUTE_NAME) { m_name = name; @@ -269,12 +269,18 @@ cci_originator cci_param_untyped::get_originator() const void cci_param_untyped::add_param_handle(cci_param_untyped_handle* param_handle) { +#ifdef CCI_THREAD_SAFE + std::lock_guard lk(m_param_handles_mutex); +#endif m_param_handles.push_back(param_handle); } void cci_param_untyped::remove_param_handle( cci_param_untyped_handle* param_handle) { +#ifdef CCI_THREAD_SAFE + std::lock_guard lk(m_param_handles_mutex); +#endif m_param_handles.erase(std::remove(m_param_handles.begin(), m_param_handles.end(), param_handle), @@ -284,6 +290,9 @@ void cci_param_untyped::remove_param_handle( void cci_param_untyped::invalidate_all_param_handles() { +#ifdef CCI_THREAD_SAFE + std::lock_guard lk(m_param_handles_mutex); +#endif while( !m_param_handles.empty() ) m_param_handles.front()->invalidate(); // removes itself from the list } diff --git a/configuration/src/cci/cfg/cci_param_untyped.h b/configuration/src/cci/cfg/cci_param_untyped.h index ae83c660..17dccb52 100644 --- a/configuration/src/cci/cfg/cci_param_untyped.h +++ b/configuration/src/cci/cfg/cci_param_untyped.h @@ -23,6 +23,10 @@ #ifndef CCI_CFG_CCI_PARAM_UNTYPED_H_INCLUDED_ #define CCI_CFG_CCI_PARAM_UNTYPED_H_INCLUDED_ +#ifdef CCI_THREAD_SAFE +#include +#include +#endif #include #include @@ -579,7 +583,11 @@ class cci_param_untyped : public cci_param_if struct callback_obj_vector { callback_obj_vector():oncall(false){}; std::vector vec; +#ifdef CCI_THREAD_SAFE + mutable std::atomic oncall; +#else mutable bool oncall; +#endif }; /// Pre write callbacks @@ -613,11 +621,19 @@ class cci_param_untyped : public cci_param_if /// @copydoc cci_param_if::invalidate_all_param_handles virtual void invalidate_all_param_handles(); - /// Parameter handles +#ifdef CCI_THREAD_SAFE + /// Parameter handles (recursive_mutex because invalidate_all calls + /// invalidate which calls remove_param_handle — re-entrant) + mutable std::recursive_mutex m_param_handles_mutex; +#endif std::vector m_param_handles; protected: - bool fast_read, fast_write; +#ifdef CCI_THREAD_SAFE + std::atomic fast_read{false}, fast_write{false}; +#else + bool fast_read{false}, fast_write{false}; +#endif }; CCI_CLOSE_NAMESPACE_ diff --git a/configuration/src/cci/core/cci_value.cpp b/configuration/src/cci/core/cci_value.cpp index c713ef64..3ab88f5a 100644 --- a/configuration/src/cci/core/cci_value.cpp +++ b/configuration/src/cci/core/cci_value.cpp @@ -34,6 +34,9 @@ #include "cci/cfg/cci_report_handler.h" #include // std::swap +#ifdef CCI_THREAD_SAFE +#include +#endif #include //std::stringstream namespace rapidjson = RAPIDJSON_NAMESPACE; @@ -68,6 +71,9 @@ struct impl_pool { static impl_type* allocate() { +#ifdef CCI_THREAD_SAFE + std::lock_guard lk(mutex_); +#endif impl_type* ret = free_list_; if (free_list_ != NULL) { free_list_ = *reinterpret_cast(free_list_); @@ -80,14 +86,23 @@ struct impl_pool static void deallocate(impl_type* elem) { if (elem == NULL) return; // delete NULL is no-op +#ifdef CCI_THREAD_SAFE + std::lock_guard lk(mutex_); +#endif elem->~impl_type(); // release internal memory (not pooled) *reinterpret_cast(elem) = free_list_; free_list_ = elem; } private: static impl_type* free_list_; +#ifdef CCI_THREAD_SAFE + static std::mutex mutex_; +#endif }; impl_type* impl_pool::free_list_; +#ifdef CCI_THREAD_SAFE +std::mutex impl_pool::mutex_; +#endif } // anonymous namespace diff --git a/configuration/src/cci/utils/consuming_broker.cpp b/configuration/src/cci/utils/consuming_broker.cpp index ffc08272..c5b67fa3 100644 --- a/configuration/src/cci/utils/consuming_broker.cpp +++ b/configuration/src/cci/utils/consuming_broker.cpp @@ -23,6 +23,13 @@ #include "cci/utils/consuming_broker.h" +#ifdef CCI_THREAD_SAFE +#define CCI_LOCK_READ std::shared_lock _lk(m_mutex) +#define CCI_LOCK_WRITE std::unique_lock _lk(m_mutex) +#else +#define CCI_LOCK_READ +#define CCI_LOCK_WRITE +#endif namespace cci_utils { using namespace cci; @@ -49,6 +56,7 @@ namespace cci_utils { const cci_value & value, const cci_originator& originator) { + CCI_LOCK_WRITE; if (locked.find(parname) != locked.end()) { cci_report_handler::set_param_failed("Setting preset value failed (parameter locked)."); return; @@ -79,6 +87,7 @@ namespace cci_utils { std::vector consuming_broker::get_unconsumed_preset_values() const { + CCI_LOCK_READ; std::vector unconsumed_preset_cci_values; std::map::const_iterator iter; std::vector::const_iterator pred; @@ -105,11 +114,13 @@ namespace cci_utils { void consuming_broker::ignore_unconsumed_preset_values(const cci_preset_value_predicate &pred) { + CCI_LOCK_WRITE; m_ignored_unconsumed_predicates.push_back(pred); } cci_originator consuming_broker::get_value_origin(const std::string &parname) const { + CCI_LOCK_READ; cci_param_if* p = get_orig_param(parname); if (p) { return p->get_value_origin(); @@ -125,6 +136,7 @@ namespace cci_utils { cci_originator consuming_broker::get_preset_value_origin(const std::string &parname) const { + CCI_LOCK_READ; std::map::const_iterator it; it = m_preset_value_originator_map.find(parname); if (it != m_preset_value_originator_map.end()) @@ -135,6 +147,7 @@ namespace cci_utils { cci_value consuming_broker::get_preset_cci_value(const std::string &parname) const { + CCI_LOCK_READ; { std::map::const_iterator iter = m_used_value_registry.find(parname); @@ -155,6 +168,7 @@ namespace cci_utils { void consuming_broker::lock_preset_value(const std::string &parname) { + CCI_LOCK_WRITE; // no error is possible. Even if the parameter does not yet exist. locked.insert(parname); } @@ -162,6 +176,7 @@ namespace cci_utils { cci_value consuming_broker::get_cci_value(const std::string &parname, const cci_originator &originator) const { + CCI_LOCK_READ; cci_param_if* p = get_orig_param(parname); if(p) { return p->get_cci_value(originator); @@ -203,6 +218,7 @@ namespace cci_utils { const std::string &parname, const cci_originator& originator) const { + CCI_LOCK_READ; cci_param_if* orig_param = get_orig_param(parname); if (orig_param) { return cci_param_untyped_handle(*orig_param, originator); @@ -212,6 +228,7 @@ namespace cci_utils { bool consuming_broker::has_preset_value(const std::string &parname) const { + CCI_LOCK_READ; { std::map::const_iterator iter = m_used_value_registry.find(parname); @@ -233,6 +250,7 @@ namespace cci_utils { consuming_broker::register_create_callback( const cci_param_create_callback &cb, const cci_originator &orig) { + CCI_LOCK_WRITE; m_create_callbacks.push_back(create_callback_obj_t(cb, orig)); return cb; } @@ -241,6 +259,7 @@ namespace cci_utils { consuming_broker::unregister_create_callback( const cci_param_create_callback_handle &cb, const cci_originator &orig) { + CCI_LOCK_WRITE; std::vector::iterator it; for(it=m_create_callbacks.begin() ; it < m_create_callbacks.end(); it++ ) { @@ -256,6 +275,7 @@ namespace cci_utils { consuming_broker::register_destroy_callback( const cci_param_destroy_callback &cb, const cci_originator& orig) { + CCI_LOCK_WRITE; m_destroy_callbacks.push_back(destroy_callback_obj_t(cb, orig)); return cb; } @@ -264,6 +284,7 @@ namespace cci_utils { consuming_broker::unregister_destroy_callback( const cci_param_destroy_callback_handle &cb, const cci_originator &orig) { + CCI_LOCK_WRITE; std::vector::iterator it; for(it=m_destroy_callbacks.begin() ; it < m_destroy_callbacks.end(); it++ ) { @@ -277,6 +298,7 @@ namespace cci_utils { bool consuming_broker::unregister_all_callbacks( const cci_originator &orig) { + CCI_LOCK_WRITE; bool result = false; std::vector::iterator it; for(it=m_create_callbacks.begin() ; it < m_create_callbacks.end(); it++ ) @@ -299,11 +321,13 @@ namespace cci_utils { } bool consuming_broker::has_callbacks() const { + CCI_LOCK_READ; return (!m_create_callbacks.empty() || !m_destroy_callbacks.empty()); } void consuming_broker::add_param(cci_param_if* par) { + CCI_LOCK_WRITE; sc_assert(par != NULL && "Unable to add a NULL parameter"); const std::string &par_name = par->name(); bool new_element = m_param_registry.insert( @@ -324,6 +348,7 @@ namespace cci_utils { } void consuming_broker::remove_param(cci_param_if* par) { + CCI_LOCK_WRITE; sc_assert(par != NULL && "Unable to remove a NULL parameter"); m_param_registry.erase(par->name()); @@ -344,6 +369,7 @@ namespace cci_utils { std::vector consuming_broker::get_param_handles(const cci_originator& originator) const { + CCI_LOCK_READ; std::vector param_handles; std::map::const_iterator it; for (it=m_param_registry.begin(); it != m_param_registry.end(); ++it) { diff --git a/configuration/src/cci/utils/consuming_broker.h b/configuration/src/cci/utils/consuming_broker.h index 87bda3dd..fab4e50e 100644 --- a/configuration/src/cci/utils/consuming_broker.h +++ b/configuration/src/cci/utils/consuming_broker.h @@ -22,6 +22,9 @@ #include #include +#ifdef CCI_THREAD_SAFE +#include +#endif #include "cci/core/cci_name_gen.h" #include "cci/cfg/cci_broker_if.h" @@ -137,6 +140,12 @@ namespace cci_utils { std::string m_name; +#ifdef CCI_THREAD_SAFE + /// Mutex for thread-safe access to broker data structures. + /// Uses shared_mutex: concurrent reads (shared_lock), exclusive writes (unique_lock). + mutable std::shared_mutex m_mutex; +#endif + // These are used as a database of _preset_ values. std::map m_param_registry; std::map m_unused_value_registry;