diff --git a/docs/changelog.md b/docs/changelog.md index 604e56063..d7d93e333 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Block events mode for sc-memory context - Opportunity to set permissions for set of users - Guests identification - Create guest users during creating sc-memory context diff --git a/sc-memory/sc-core/sc-store/sc_event.c b/sc-memory/sc-core/sc-store/sc_event.c index a2f71813a..60c60c763 100644 --- a/sc-memory/sc-core/sc-store/sc_event.c +++ b/sc-memory/sc-core/sc-store/sc_event.c @@ -320,7 +320,10 @@ sc_result sc_event_emit( if (ctx == null_ptr) return SC_RESULT_NO; - if (_sc_memory_context_is_pending(ctx)) + if (_sc_memory_context_are_events_blocking(ctx)) + return SC_RESULT_OK; + + if (_sc_memory_context_are_events_pending(ctx)) { _sc_memory_context_pend_event(ctx, type, subscription_addr, connector_addr, connector_type, other_addr); return SC_RESULT_OK; diff --git a/sc-memory/sc-core/sc_memory.c b/sc-memory/sc-core/sc_memory.c index b299904c3..d0a7c1672 100644 --- a/sc-memory/sc-core/sc_memory.c +++ b/sc-memory/sc-core/sc_memory.c @@ -193,6 +193,16 @@ void sc_memory_context_pending_end(sc_memory_context * ctx) _sc_memory_context_pending_end(ctx); } +void sc_memory_context_blocking_begin(sc_memory_context * ctx) +{ + _sc_memory_context_blocking_begin(ctx); +} + +void sc_memory_context_blocking_end(sc_memory_context * ctx) +{ + _sc_memory_context_blocking_end(ctx); +} + sc_bool sc_memory_is_initialized() { return sc_storage_is_initialized(); diff --git a/sc-memory/sc-core/sc_memory.h b/sc-memory/sc-core/sc_memory.h index 8ee15a512..f59105c59 100644 --- a/sc-memory/sc-core/sc_memory.h +++ b/sc-memory/sc-core/sc_memory.h @@ -162,6 +162,30 @@ _SC_EXTERN void sc_memory_context_pending_begin(sc_memory_context * ctx); */ _SC_EXTERN void sc_memory_context_pending_end(sc_memory_context * ctx); +/*! + * @brief Starts events blocking mode for a context. + * + * In this mode, all new emitted events will be blocking until `sc_memory_context_blocking_end` is called. + * + * @param ctx Pointer to the sc-memory context. + * + * @note Use this function to start a blocking mode for events emitted by the specified context. + * @see sc_memory_context_blocking_end + */ +_SC_EXTERN void sc_memory_context_blocking_begin(sc_memory_context * ctx); + +/*! + * @brief Ends events blocking mode for a context. + * + * This function ends the blocking mode for events emitted by the specified context. + * + * @param ctx Pointer to the sc-memory context. + * + * @note Use this function to end the blocking mode for events emitted by the specified context. + * @see sc_memory_context_blocking_begin + */ +_SC_EXTERN void sc_memory_context_blocking_end(sc_memory_context * ctx); + /*! * @brief Checks if sc-memory is initialized. * diff --git a/sc-memory/sc-core/sc_memory_context_manager.c b/sc-memory/sc-core/sc_memory_context_manager.c index fe21d4183..856c17df6 100644 --- a/sc-memory/sc-core/sc_memory_context_manager.c +++ b/sc-memory/sc-core/sc_memory_context_manager.c @@ -31,6 +31,7 @@ struct _sc_event_emit_params }; #define SC_CONTEXT_FLAG_PENDING_EVENTS 0x1 +#define SC_CONTEXT_FLAG_BLOCKING_EVENTS 0x2 #define SC_CONTEXT_PERMISSIONS_FULL 0xff @@ -210,11 +211,11 @@ sc_addr _sc_memory_context_get_user_addr(sc_memory_context * ctx) return user_addr; } -sc_bool _sc_memory_context_is_pending(sc_memory_context const * ctx) +sc_bool _sc_memory_context_are_events_pending(sc_memory_context const * ctx) { - sc_monitor_acquire_write((sc_monitor *)&ctx->monitor); + sc_monitor_acquire_read((sc_monitor *)&ctx->monitor); sc_bool result = ((sc_memory_context *)ctx)->flags & SC_CONTEXT_FLAG_PENDING_EVENTS; - sc_monitor_release_write((sc_monitor *)&ctx->monitor); + sc_monitor_release_read((sc_monitor *)&ctx->monitor); return result; } @@ -276,3 +277,25 @@ void _sc_memory_context_pending_end(sc_memory_context * ctx) _sc_memory_context_emit_events(ctx); sc_monitor_release_write(&ctx->monitor); } + +sc_bool _sc_memory_context_are_events_blocking(sc_memory_context const * ctx) +{ + sc_monitor_acquire_read((sc_monitor *)&ctx->monitor); + sc_bool result = ((sc_memory_context *)ctx)->flags & SC_CONTEXT_FLAG_BLOCKING_EVENTS; + sc_monitor_release_read((sc_monitor *)&ctx->monitor); + return result; +} + +void _sc_memory_context_blocking_begin(sc_memory_context * ctx) +{ + sc_monitor_acquire_write(&ctx->monitor); + ctx->flags |= SC_CONTEXT_FLAG_BLOCKING_EVENTS; + sc_monitor_release_write(&ctx->monitor); +} + +void _sc_memory_context_blocking_end(sc_memory_context * ctx) +{ + sc_monitor_acquire_write(&ctx->monitor); + ctx->flags &= ~SC_CONTEXT_FLAG_BLOCKING_EVENTS; + sc_monitor_release_write(&ctx->monitor); +} diff --git a/sc-memory/sc-core/sc_memory_context_manager.h b/sc-memory/sc-core/sc_memory_context_manager.h index 762f8a7a5..8d885ae7e 100644 --- a/sc-memory/sc-core/sc_memory_context_manager.h +++ b/sc-memory/sc-core/sc_memory_context_manager.h @@ -89,7 +89,7 @@ void _sc_memory_context_free_impl(sc_memory_context_manager * manager, sc_memory sc_addr _sc_memory_context_get_user_addr(sc_memory_context * ctx); //! Checks if specified sc-memory context has pending events block. -sc_bool _sc_memory_context_is_pending(sc_memory_context const * ctx); +sc_bool _sc_memory_context_are_events_pending(sc_memory_context const * ctx); /*! Function that marks the beginning of a pending events block in a sc-memory context. * @param ctx Pointer to the sc-memory context for which the pending events block begins. @@ -126,4 +126,19 @@ void _sc_memory_context_pend_event( */ void _sc_memory_context_emit_events(sc_memory_context const * ctx); +//! Checks if specified sc-memory context has blocking events block. +sc_bool _sc_memory_context_are_events_blocking(sc_memory_context const * ctx); + +/*! Function that marks the beginning of a blocking events block in a sc-memory context. + * @param ctx Pointer to the sc-memory context for which the blocking events block begins. + * @note This function marks the beginning of a blocking events block in the sc-memory context. + */ +void _sc_memory_context_blocking_begin(sc_memory_context * ctx); + +/*! Function that marks the end of a blocking events block in a sc-memory context. + * @param ctx Pointer to the sc-memory context for which the blocking events block ends. + * @note This function marks the end of a blocking events block in the sc-memory context. + */ +void _sc_memory_context_blocking_end(sc_memory_context * ctx); + #endif diff --git a/sc-memory/sc-core/sc_memory_context_permissions.h b/sc-memory/sc-core/sc_memory_context_permissions.h index cd691fc42..25a4f7c07 100644 --- a/sc-memory/sc-core/sc_memory_context_permissions.h +++ b/sc-memory/sc-core/sc_memory_context_permissions.h @@ -12,7 +12,7 @@ #include "sc_memory_context_manager.h" -#define SC_CONTEXT_FLAG_SYSTEM 0x2 +#define SC_CONTEXT_FLAG_SYSTEM 0x10 /** * @brief Sets permissions for a specific sc-memory element. diff --git a/sc-memory/sc-memory/sc_memory.cpp b/sc-memory/sc-memory/sc_memory.cpp index 70f1ef98f..3bfcd28a8 100644 --- a/sc-memory/sc-memory/sc_memory.cpp +++ b/sc-memory/sc-memory/sc_memory.cpp @@ -189,6 +189,18 @@ void ScMemoryContext::EndEventsPending() sc_memory_context_pending_end(m_context); } +void ScMemoryContext::BeingEventsBlocking() +{ + CHECK_CONTEXT; + sc_memory_context_blocking_begin(m_context); +} + +void ScMemoryContext::EndEventsBlocking() +{ + CHECK_CONTEXT; + sc_memory_context_blocking_end(m_context); +} + bool ScMemoryContext::IsValid() const { return m_context != nullptr; diff --git a/sc-memory/sc-memory/sc_memory.hpp b/sc-memory/sc-memory/sc_memory.hpp index f1dcabd71..2d5eed5c9 100644 --- a/sc-memory/sc-memory/sc_memory.hpp +++ b/sc-memory/sc-memory/sc_memory.hpp @@ -140,6 +140,12 @@ class ScMemoryContext //! End events pending mode _SC_EXTERN void EndEventsPending(); + //! Begin events blocking mode + _SC_EXTERN void BeingEventsBlocking(); + + //! End events blocking mode + _SC_EXTERN void EndEventsBlocking(); + /*! * @brief Checks if the sc-memory context is valid. * @@ -1240,3 +1246,21 @@ class ScMemoryContextEventsPendingGuard private: ScMemoryContext & m_ctx; }; + +class ScMemoryContextEventsBlockingGuard +{ +public: + _SC_EXTERN explicit ScMemoryContextEventsBlockingGuard(ScMemoryContext & ctx) + : m_ctx(ctx) + { + m_ctx.BeingEventsBlocking(); + } + + _SC_EXTERN ~ScMemoryContextEventsBlockingGuard() + { + m_ctx.EndEventsBlocking(); + } + +private: + ScMemoryContext & m_ctx; +}; diff --git a/sc-memory/tests/sc-memory/events/test_sc_event.cpp b/sc-memory/tests/sc-memory/events/test_sc_event.cpp index 4dfad5b17..d983dd052 100644 --- a/sc-memory/tests/sc-memory/events/test_sc_event.cpp +++ b/sc-memory/tests/sc-memory/events/test_sc_event.cpp @@ -408,3 +408,108 @@ TEST_F(ScEventTest, pend_events) EXPECT_EQ(passedCount, el_num); } + +TEST_F(ScEventTest, BlockEventsAndNotEmitAfter) +{ + ScAddr const nodeAddr = m_ctx->CreateNode(ScType::NodeConst); + + std::atomic_bool isCalled = false; + ScEventAddOutputEdge event( + *m_ctx, + nodeAddr, + [&isCalled](ScAddr const & addr, ScAddr const &, ScAddr const & target) + { + return isCalled = true; + }); + + m_ctx->BeingEventsBlocking(); + + ScAddr const nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + m_ctx->EndEventsBlocking(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_FALSE(isCalled); +} + +TEST_F(ScEventTest, BlockEventsAndEmitAfter) +{ + ScAddr const nodeAddr = m_ctx->CreateNode(ScType::NodeConst); + + std::atomic_bool isCalled = false; + ScEventAddOutputEdge event( + *m_ctx, + nodeAddr, + [&isCalled](ScAddr const & addr, ScAddr const &, ScAddr const & target) + { + return isCalled = true; + }); + + m_ctx->BeingEventsBlocking(); + + ScAddr nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + m_ctx->EndEventsBlocking(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_FALSE(isCalled); + + nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_TRUE(isCalled); +} + +TEST_F(ScEventTest, BlockEventsGuardAndNotEmitAfter) +{ + ScAddr const nodeAddr = m_ctx->CreateNode(ScType::NodeConst); + + std::atomic_bool isCalled = false; + ScEventAddOutputEdge event( + *m_ctx, + nodeAddr, + [&isCalled](ScAddr const & addr, ScAddr const &, ScAddr const & target) + { + return isCalled = true; + }); + + ScMemoryContextEventsBlockingGuard guard(*m_ctx); + + ScAddr const nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_FALSE(isCalled); +} + +TEST_F(ScEventTest, BlockEventsGuardAndEmitAfter) +{ + ScAddr const nodeAddr = m_ctx->CreateNode(ScType::NodeConst); + + std::atomic_bool isCalled = false; + ScEventAddOutputEdge event( + *m_ctx, + nodeAddr, + [&isCalled](ScAddr const & addr, ScAddr const &, ScAddr const & target) + { + return isCalled = true; + }); + { + ScMemoryContextEventsBlockingGuard guard(*m_ctx); + + ScAddr const nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_FALSE(isCalled); + } + + ScAddr const nodeAddr2 = m_ctx->CreateNode(ScType::NodeConst); + m_ctx->CreateEdge(ScType::EdgeAccessConstPosPerm, nodeAddr, nodeAddr2); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_TRUE(isCalled); +}