From 04ab6c1d0d16d19d8e025750c5bc037e4b039075 Mon Sep 17 00:00:00 2001 From: Jason Lokerson Date: Thu, 6 Aug 2015 16:14:48 -0700 Subject: [PATCH] Add signals for CoreContext operations This should allow us to deprecate `AddTeardownListener` and avoid most uses of `CoreRunnable`. --- autowiring/CoreContext.h | 42 ++++++++++++++++++------ src/autowiring/CoreContext.cpp | 11 ++++++- src/autowiring/test/CoreContextTest.cpp | 43 +++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/autowiring/CoreContext.h b/autowiring/CoreContext.h index 136180dc7..48564b466 100644 --- a/autowiring/CoreContext.h +++ b/autowiring/CoreContext.h @@ -21,7 +21,6 @@ #include "MemoEntry.h" #include "CoreObjectDescriptor.h" #include "result_or_default.h" -#include "TeardownNotifier.h" #include "ThreadPool.h" #include "TypeRegistry.h" #include "TypeUnifier.h" @@ -144,7 +143,6 @@ class AutoSearchLambdaDefault: /// Events, threads, and filter graphs require that the context's Initiate() function is called. /// class CoreContext: - public TeardownNotifier, public std::enable_shared_from_this { protected: @@ -152,6 +150,19 @@ class CoreContext: CoreContext(const std::shared_ptr& pParent, t_childList::iterator backReference); public: + // Asserted when the context is initiated + autowiring::signal onInitiated; + + // Asserted when the context is actually running + autowiring::signal onRunning; + + // Asserted when the context is being shut down + autowiring::signal onShutdown; + + // Asserted when the context is tearing down but before members objects are destroyed or + // any contained AutoWired fields are unlinked + autowiring::signal onTeardown; + virtual ~CoreContext(void); /// @@ -486,18 +497,27 @@ class CoreContext: // Internal resolvers, used to determine which teardown style the user would like to use /// \internal template - void AddTeardownListener2(Fx&& fx, void (Fx::*)(void)) { TeardownNotifier::AddTeardownListener(fx); } + void AddTeardownListener2(Fx&& fx, void (Fx::*)(void)) { + onTeardown += [fx](const CoreContext&) { fx(); }; + } /// \internal template - void AddTeardownListener2(Fx&& fx, void (Fx::*)(const CoreContext&)) { TeardownNotifier::AddTeardownListener([fx, this] () mutable { fx(*this); }); } + void AddTeardownListener2(Fx&& fx, void (Fx::*)(const CoreContext&)) { + onTeardown += std::move(fx); + } + /// \internal template - void AddTeardownListener2(Fx&& fx, void (Fx::*)(void) const) { TeardownNotifier::AddTeardownListener(std::forward(fx)); } + void AddTeardownListener2(Fx&& fx, void (Fx::*)(void) const) { + onTeardown += [fx](const CoreContext&) { fx(); }; + } /// \internal template - void AddTeardownListener2(Fx&& fx, void (Fx::*)(const CoreContext&) const) { TeardownNotifier::AddTeardownListener([fx, this] () mutable { fx(*this); }); } + void AddTeardownListener2(Fx&& fx, void (Fx::*)(const CoreContext&) const) { + onTeardown += std::move(fx); + } public: // Accessor methods: @@ -1267,9 +1287,7 @@ class CoreContext: /// Adds a teardown notifier which receives a pointer to this context on destruction /// template - void AddTeardownListener(Fx&& fx) { - AddTeardownListener2(std::forward(fx), &Fx::operator()); - } + void DEPRECATED(AddTeardownListener(Fx&& fx), "Superceded by onTeardown"); /// /// Unregisters a slot as a recipient of potential autowiring @@ -1282,6 +1300,12 @@ class CoreContext: void Dump(std::ostream& os) const; }; + +template +void CoreContext::AddTeardownListener(Fx&& fx) { + AddTeardownListener2(std::forward(fx), &Fx::operator()); +} + namespace autowiring { /// /// Forward-declarable version of CoreContext::InjectCurrent diff --git a/src/autowiring/CoreContext.cpp b/src/autowiring/CoreContext.cpp index ce5baa297..2e033a3c8 100644 --- a/src/autowiring/CoreContext.cpp +++ b/src/autowiring/CoreContext.cpp @@ -81,7 +81,7 @@ CoreContext::~CoreContext(void) { ); // Notify all ContextMember instances that their parent is going away - NotifyTeardownListeners(); + onTeardown(*this); // Make sure events aren't happening anymore: UnregisterEventReceiversUnsafe(); @@ -435,6 +435,7 @@ void CoreContext::Initiate(void) { // Notify all child contexts that they can start if they want if (!IsRunning()) { lk.unlock(); + onInitiated(); // Need to inject a delayed context type so that this context will not be destroyed until // it has an opportunity to start. @@ -464,6 +465,7 @@ void CoreContext::Initiate(void) { // call to Start that follows the unlock. threadPool = m_threadPool; lk.unlock(); + onInitiated(); // Start the thread pool out of the lock, and then update our start token if our thread pool // reference has not changed. The next pool could potentially be nullptr if the parent is going @@ -490,6 +492,9 @@ void CoreContext::Initiate(void) { (*q)->Start(outstanding); } + // We assert this condition only after all threads have been at least notified that they can start + onRunning(); + // Update state of children now that we are initated TryTransitionChildrenState(); } @@ -532,6 +537,7 @@ void CoreContext::SignalShutdown(bool wait, ShutdownMode shutdownMode) { m_stateBlock->m_stateChanged.notify_all(); } + onShutdown(); // Teardown interleave assurance--all of these contexts will generally be destroyed // at the exit of this block, due to the behavior of SignalTerminate, unless exterior @@ -1208,7 +1214,10 @@ void CoreContext::TryTransitionChildrenState(void) { // Child had it's state changed child->m_stateBlock->m_stateChanged.notify_all(); + // Raise the run condition in the child childLk.unlock(); + child->onRunning(); + auto outstanding = child->m_stateBlock->IncrementOutstandingThreadCount(child); while (q != child->m_threads.end()) { diff --git a/src/autowiring/test/CoreContextTest.cpp b/src/autowiring/test/CoreContextTest.cpp index 212b318fe..ce021518b 100644 --- a/src/autowiring/test/CoreContextTest.cpp +++ b/src/autowiring/test/CoreContextTest.cpp @@ -522,3 +522,46 @@ TEST_F(CoreContextTest, UnlinkOnTeardown) { ASSERT_TRUE(strongB->v.IsAutowired()) << "An Autowired field pointing to a foreign context was incorrectly unlinked"; ASSERT_EQ(so.get(), strongB->so.get()) << "An Autowired field was unlinked on teardown even though it pointed outside of a context"; } + +TEST_F(CoreContextTest, InitiateAssertsSignals) { + AutoCurrentContext outer; + + auto teardown = std::make_shared(false); + { + AutoCreateContext ctxt; + auto initiated = std::make_shared(false); + auto running = std::make_shared(false); + auto shutdown = std::make_shared(false); + + ctxt->onInitiated += [initiated] { *initiated = true; }; + ctxt->onRunning += [running] { *running = true; }; + ctxt->onShutdown += [shutdown] { *shutdown = true; }; + ctxt->onTeardown += [teardown] (const CoreContext&) { *teardown = true; }; + + ctxt->Initiate(); + ASSERT_TRUE(*initiated) << "Initiation signal not asserted on context startup"; + ASSERT_FALSE(*running) << "Running signal asserted before the outer context was started"; + ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely"; + *initiated = false; + + outer->Initiate(); + ASSERT_FALSE(*initiated) << "Initiation signal was redundantly asserted"; + ASSERT_TRUE(*running) << "Running signal not asserted when the outer context was started"; + ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely"; + + *running = false; + + ctxt->Initiate(); + ASSERT_FALSE(*initiated) << "Initiation signal redundantly asserted"; + ASSERT_FALSE(*running) << "Running signal redundantly asserted"; + ASSERT_FALSE(*shutdown) << "Termination signal asserted unexpectedly"; + + ctxt->SignalShutdown(); + ASSERT_FALSE(*initiated) << "Initiation signal not asserted during teardown"; + ASSERT_FALSE(*running) << "Running signal asserted improperly on teardown"; + ASSERT_TRUE(*shutdown) << "Termination signal not asserted as expected"; + + ASSERT_FALSE(*teardown) << "Teardown handler notified prematurely"; + } + ASSERT_TRUE(*teardown) << "Teardown handler not correctly notified on context teardown"; +} \ No newline at end of file