diff --git a/src/dobj/private/LockableDObject.cpp b/src/dobj/private/LockableDObject.cpp new file mode 100644 index 0000000..65b8cf9 --- /dev/null +++ b/src/dobj/private/LockableDObject.cpp @@ -0,0 +1,22 @@ +#include "LockableDObject.h" + +/** + * @brief Access the underlying DObject. + */ +DObject& LockableDObject::get() { + return object_; +} + +/** + * @brief Locks the mutex to ensure thread-safe access. + */ +void LockableDObject::lock() { + mutex_.lock(); +} + +/** + * @brief Unlocks the mutex after thread-safe access. + */ +void LockableDObject::unlock() { + mutex_.unlock(); +} diff --git a/src/dobj/public/LockableDObject.h b/src/dobj/public/LockableDObject.h new file mode 100644 index 0000000..60790eb --- /dev/null +++ b/src/dobj/public/LockableDObject.h @@ -0,0 +1,47 @@ +#ifndef LOCKABLE_DOBJECT_H +#define LOCKABLE_DOBJECT_H + +#include "DObject.h" +#include + +/** + * @file LockableDObject.h + * @brief A lightweight wrapper for DObject that adds locking capabilities. + * + * This class allows safe concurrent access to a DObject by providing + * locking and unlocking methods. + */ + +/** + * @class LockableDObject + * @brief Wrapper for DObject with thread-safe access. + */ +class LockableDObject { +public: + /** + * @brief Default constructor. + */ + LockableDObject() = default; + + /** + * @brief Access the underlying DObject. + * @return A reference to the wrapped DObject. + */ + DObject& get(); + + /** + * @brief Locks the mutex to ensure thread-safe access. + */ + void lock(); + + /** + * @brief Unlocks the mutex after thread-safe access. + */ + void unlock(); + +private: + DObject object_; ///< The underlying DObject instance. + std::mutex mutex_; ///< Mutex for thread-safe access. +}; + +#endif // LOCKABLE_DOBJECT_H \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 08e8c91..e97649d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,10 +1,14 @@ # Define the library dobj_sources = files( - 'dobj/private/Metadata.cpp' + 'dobj/private/Metadata.cpp', + 'dobj/private/DObject.cpp', + 'dobj/private/LockableDObject.cpp' ) dobj_headers = files( - 'dobj/public/Metadata.h' + 'dobj/public/Metadata.h', + 'dobj/public/DObject.h', + 'dobj/public/LockableDObject.h' ) # Define the libdobj library so it can be linked against by other parts of the build system diff --git a/tests/dobj/LockableDObjectTest.cpp b/tests/dobj/LockableDObjectTest.cpp new file mode 100644 index 0000000..e2bbc36 --- /dev/null +++ b/tests/dobj/LockableDObjectTest.cpp @@ -0,0 +1,89 @@ +#include +#include "LockableDObject.h" +#include +#include + +/** + * @file LockableDObjectTest.cpp + * @brief Unit tests for the LockableDObject class. + */ + +/** + * @brief Test suite for the LockableDObject class. + */ +class LockableDObjectTest : public ::testing::Test { +protected: + LockableDObject lockableObject; + + void SetUp() override { + // Initialize the LockableDObject for tests + DObject& obj = lockableObject.get(); + obj.setData(42); // Set initial data + } +}; + +/** + * @test Verify the default construction of LockableDObject. + */ +TEST_F(LockableDObjectTest, DefaultConstruction) { + EXPECT_NO_THROW(LockableDObject obj); +} + +/** + * @test Verify access to the underlying DObject. + */ +TEST_F(LockableDObjectTest, AccessDObject) { + DObject& obj = lockableObject.get(); + EXPECT_EQ(std::get(obj.getData()), 42); // Ensure the data is accessible +} + +/** + * @test Verify locking and unlocking. + */ +TEST_F(LockableDObjectTest, LockAndUnlock) { + EXPECT_NO_THROW(lockableObject.lock()); + EXPECT_NO_THROW(lockableObject.unlock()); +} + +/** + * @test Verify thread safety with multiple threads. + */ +TEST_F(LockableDObjectTest, ThreadSafety) { + const int numThreads = 10; + std::vector threads; + std::atomic successCount{0}; + + // Each thread tries to update the data + for (int i = 0; i < numThreads; ++i) { + threads.emplace_back([this, i, &successCount]() { + lockableObject.lock(); + DObject& obj = lockableObject.get(); + obj.setData(i); + if (std::get(obj.getData()) == i) { + successCount++; + } + lockableObject.unlock(); + }); + } + + for (auto& t : threads) { + t.join(); + } + + EXPECT_EQ(successCount, numThreads); +} + +/** + * @test Verify that the DObject remains consistent when accessed via LockableDObject. + */ +TEST_F(LockableDObjectTest, ConsistentState) { + lockableObject.lock(); + DObject& obj = lockableObject.get(); + obj.setData(100); + EXPECT_EQ(std::get(obj.getData()), 100); + lockableObject.unlock(); + + lockableObject.lock(); + EXPECT_EQ(std::get(lockableObject.get().getData()), 100); + lockableObject.unlock(); +}