diff --git a/compendium/DeclarativeServices/src/SCRActivator.cpp b/compendium/DeclarativeServices/src/SCRActivator.cpp index d10d9b61c..a5b261392 100644 --- a/compendium/DeclarativeServices/src/SCRActivator.cpp +++ b/compendium/DeclarativeServices/src/SCRActivator.cpp @@ -38,7 +38,7 @@ #include "cppmicroservices/servicecomponent/runtime/dto/ComponentDescriptionDTO.hpp" #include "cppmicroservices/servicecomponent/runtime/dto/ReferenceDTO.hpp" -#include "cppmicroservices/util/ScopeGuard.h" +#include "cppmicroservices/detail/ScopeGuard.h" using cppmicroservices::logservice::SeverityLevel; using cppmicroservices::service::component::ComponentConstants::SERVICE_COMPONENT; @@ -78,7 +78,7 @@ void SCRActivator::Stop(cppmicroservices::BundleContext context) { try { - cppmicroservices::util::ScopeGuard joinThreadPool{ [this]() { + cppmicroservices::detail::ScopeGuard joinThreadPool{ [this]() { if (threadpool) { try { threadpool->join(); diff --git a/compendium/DeclarativeServices/src/manager/ConcurrencyUtil.hpp b/compendium/DeclarativeServices/src/manager/ConcurrencyUtil.hpp index 39097290b..7a941d524 100644 --- a/compendium/DeclarativeServices/src/manager/ConcurrencyUtil.hpp +++ b/compendium/DeclarativeServices/src/manager/ConcurrencyUtil.hpp @@ -93,105 +93,6 @@ class Guarded } }; -/** - * A utility class similar to std::latch except it allows for incrementing the count as well. - * It is useful when the number of threads entering a block may vary dynamically. This class - * can be used to synchronize two blocks of code where the first block can be executed by - * 'n' number of threads simultaneously and the second block needs to wait for all the threads - * to exit the first block. - * - * Example code: - * function1() - * { - * if(latch.CountUp()) - * { - * // do something critical here - * latch.CountDown(); - * } - * } - * function2() - * { - * latch.Wait(); // this blocks the thread until all threads that have exited function1 - * // do something here. - * } - */ -class CounterLatch -{ -public: - CounterLatch() : count(0) {} - CounterLatch(const CounterLatch&) = delete; - CounterLatch(CounterLatch&&) = delete; - CounterLatch& operator=(const CounterLatch&) = delete; - CounterLatch& operator=(CounterLatch&&) = delete; - ~CounterLatch() = default; - - /** - * Increments the count of the latch, if current count is not negative - * \return \c true if the count was incremented, \c false otherwise - */ - bool CountUp() - { - std::lock_guard lock{mtx}; - if(count >= 0) - { - ++count; - return true; - } - return false; - } - - /** - * Decrements the count of the latch, releasing all waiting threads if the - * count reaches zero. - * If the current count is greater than zero then it is decremented. If - * the new count is zero then all waiting threads are notified. - * If the current count equals zero then nothing happens. - */ - void CountDown() - { - std::lock_guard lock{mtx}; - if(count > 0) - { - if(--count == 0) - { - // notify waiting threads - cond.notify_all(); - } - } - } - - /** - * Waits until the counter reaches 0. The value of the counter after this - * method returns is invalid (negative). This method is designed for a - * one-time use only. - * - * \throws std::runtime_error if the current count is negative. - */ - void Wait() - { - std::unique_lock lock{mtx}; - if(count < 0) - { - throw std::runtime_error("CounterLatch is in invalid state."); - } - cond.wait(lock, [&]() { return count == 0; }); - count = std::numeric_limits::min(); // makes the latch unusable for other threads - } - - /** - * Returns the current count of the latch - */ - long GetCount() - { - std::unique_lock lock{mtx}; - return count; - } - -private: - std::mutex mtx; ///< mutex to protect access to the counter - std::condition_variable cond; ///< used to notify the waiting thread - long count{0}; ///< latch counter -}; } } diff --git a/compendium/DeclarativeServices/src/manager/states/CCActiveState.cpp b/compendium/DeclarativeServices/src/manager/states/CCActiveState.cpp index 94a5d7cbc..85d26660b 100644 --- a/compendium/DeclarativeServices/src/manager/states/CCActiveState.cpp +++ b/compendium/DeclarativeServices/src/manager/states/CCActiveState.cpp @@ -25,21 +25,11 @@ #include "CCUnsatisfiedReferenceState.hpp" #include "cppmicroservices/SharedLibraryException.h" +#include "cppmicroservices/detail/ScopeGuard.h" + namespace cppmicroservices { namespace scrimpl { -class LatchScopeGuard -{ -public: - LatchScopeGuard(std::function cleanupFcn) - : _cleanupFcn(std::move(cleanupFcn)) - {} - ~LatchScopeGuard() { _cleanupFcn(); } - -private: - std::function _cleanupFcn; -}; - CCActiveState::CCActiveState() = default; std::shared_ptr CCActiveState::Activate( @@ -51,7 +41,7 @@ std::shared_ptr CCActiveState::Activate( auto logger = mgr.GetLogger(); if (latch.CountUp()) { { - LatchScopeGuard sg([this, logger]() { + detail::ScopeGuard sg([this, logger]() { // By using try/catch here, we ensure that this lambda function doesn't // throw inside LatchScopeGuard's dtor. try { diff --git a/compendium/DeclarativeServices/src/manager/states/CCActiveState.hpp b/compendium/DeclarativeServices/src/manager/states/CCActiveState.hpp index c3aebc09f..b98484327 100644 --- a/compendium/DeclarativeServices/src/manager/states/CCActiveState.hpp +++ b/compendium/DeclarativeServices/src/manager/states/CCActiveState.hpp @@ -26,6 +26,8 @@ #include "CCSatisfiedState.hpp" #include "../ConcurrencyUtil.hpp" +#include "cppmicroservices/detail/CounterLatch.h" + using cppmicroservices::service::component::runtime::dto::ComponentState; namespace cppmicroservices { @@ -77,7 +79,7 @@ class CCActiveState final latch.Wait(); } private: - CounterLatch latch; + detail::CounterLatch latch; }; } } diff --git a/compendium/DeclarativeServices/test/CMakeLists.txt b/compendium/DeclarativeServices/test/CMakeLists.txt index 69a5e3e4f..3e1fc2a03 100755 --- a/compendium/DeclarativeServices/test/CMakeLists.txt +++ b/compendium/DeclarativeServices/test/CMakeLists.txt @@ -63,7 +63,6 @@ set(_declarativeservices_tests TestComponentManagerEnabledState.cpp TestComponentManagerImpl.cpp TestComponentRegistry.cpp - TestCounterLatch.cpp TestMetadataParserFactory.cpp TestMetadataParserImplV1.cpp TestReferenceManagerImpl.cpp diff --git a/framework/include/CMakeLists.txt b/framework/include/CMakeLists.txt index d5c7ebd6c..4e1e18f84 100644 --- a/framework/include/CMakeLists.txt +++ b/framework/include/CMakeLists.txt @@ -59,4 +59,6 @@ set(_public_headers cppmicroservices/detail/BundleAbstractTracked.h cppmicroservices/detail/BundleAbstractTracked.tpp cppmicroservices/detail/BundleResourceBuffer.h + cppmicroservices/detail/ScopeGuard.h + cppmicroservices/detail/CounterLatch.h ) diff --git a/framework/include/cppmicroservices/detail/BundleAbstractTracked.h b/framework/include/cppmicroservices/detail/BundleAbstractTracked.h index 4fbe278df..7a874c713 100644 --- a/framework/include/cppmicroservices/detail/BundleAbstractTracked.h +++ b/framework/include/cppmicroservices/detail/BundleAbstractTracked.h @@ -64,7 +64,7 @@ class BundleAbstractTracked /** * BundleAbstractTracked constructor. */ - BundleAbstractTracked(BundleContext* bc); + BundleAbstractTracked(BundleContext bc); virtual ~BundleAbstractTracked(); @@ -284,7 +284,7 @@ class BundleAbstractTracked */ std::atomic trackingCount; - BundleContext* const bc; + BundleContext bc; bool CustomizerAddingFinal(S item, const std::shared_ptr& custom); diff --git a/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp b/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp index ee3d66f48..d3208c41b 100644 --- a/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp +++ b/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp @@ -30,8 +30,8 @@ namespace cppmicroservices { namespace detail { template -BundleAbstractTracked::BundleAbstractTracked(BundleContext* bc) - : closed(false), bc(bc) +BundleAbstractTracked::BundleAbstractTracked(BundleContext bc) + : closed(false), trackingCount(0), bc(bc) { } @@ -44,12 +44,12 @@ void BundleAbstractTracked::SetInitial(const std::vector& initiallis { std::copy(initiallist.begin(), initiallist.end(), std::back_inserter(initial)); - if (bc->GetLogSink()->Enabled()) + if (bc.GetLogSink()->Enabled()) { for(typename std::list::const_iterator item = initial.begin(); item != initial.end(); ++item) { - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::setInitial: " << (*item); + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::setInitial: " << (*item); } } } @@ -78,7 +78,7 @@ void BundleAbstractTracked::TrackInitial() if (tracked[item]) { /* if we are already tracking this item */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::trackInitial[already tracked]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial[already tracked]: " << item; continue; /* skip this item */ } if (std::find(adding.begin(), adding.end(), item) != adding.end()) @@ -86,12 +86,12 @@ void BundleAbstractTracked::TrackInitial() /* * if this item is already in the process of being added. */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::trackInitial[already adding]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial[already adding]: " << item; continue; /* skip this item */ } adding.push_back(item); } - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::trackInitial: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial: " << item; TrackAdding(item, R()); /* * Begin tracking it. We call trackAdding @@ -123,14 +123,14 @@ void BundleAbstractTracked::Track(S item, R related) if (std::find(adding.begin(), adding.end(),item) != adding.end()) { /* if this item is already in the process of being added. */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::track[already adding]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::track[already adding]: " << item; return; } adding.push_back(item); /* mark this item is being added */ } else { /* we are currently tracking this item */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::track[modified]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::track[modified]: " << item; Modified(); /* increment modification count */ } } @@ -162,7 +162,7 @@ void BundleAbstractTracked::Untrack(S item, R related) { /* if this item is already in the list * of initial references to process */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::untrack[removed from initial]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[removed from initial]: " << item; return; /* we have removed it from the list and it will not be * processed */ @@ -174,7 +174,7 @@ void BundleAbstractTracked::Untrack(S item, R related) { /* if the item is in the process of * being added */ - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::untrack[being added]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[being added]: " << item; return; /* * in case the item is untracked while in the process of * adding @@ -192,7 +192,7 @@ void BundleAbstractTracked::Untrack(S item, R related) } Modified(); /* increment modification count */ } - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::untrack[removed]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[removed]: " << item; /* Call customizer outside of synchronized region */ CustomizerRemoved(item, related, object); /* @@ -280,7 +280,7 @@ bool BundleAbstractTracked::CustomizerAddingFinal(S item, const std::sh template void BundleAbstractTracked::TrackAdding(S item, R related) { - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::trackAdding:" << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackAdding:" << item; std::shared_ptr object; bool becameUntracked = false; /* Call customizer outside of synchronized region */ @@ -304,7 +304,7 @@ void BundleAbstractTracked::TrackAdding(S item, R related) */ if (becameUntracked && object) { - DIAG_LOG(*bc->GetLogSink()) << "BundleAbstractTracked::trackAdding[removed]: " << item; + DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackAdding[removed]: " << item; /* Call customizer outside of synchronized region */ CustomizerRemoved(item, related, object); /* diff --git a/framework/include/cppmicroservices/detail/CounterLatch.h b/framework/include/cppmicroservices/detail/CounterLatch.h new file mode 100644 index 000000000..66e83d3b1 --- /dev/null +++ b/framework/include/cppmicroservices/detail/CounterLatch.h @@ -0,0 +1,133 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + Licensed 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. + + =============================================================================*/ + +#ifndef CPPMICROSERVICES_DETAIL_COUNTERLATCH_H +# define CPPMICROSERVICES_DETAIL_COUNTERLATCH_H + +#include + +namespace cppmicroservices { + +namespace detail { + +/** + * A utility class similar to std::latch except it allows for incrementing the count as well. + * It is useful when the number of threads entering a block may vary dynamically. This class + * can be used to synchronize two blocks of code where the first block can be executed by + * 'n' number of threads simultaneously and the second block needs to wait for all the threads + * to exit the first block. + * + * Example code: + * function1() + * { + * if(latch.CountUp()) + * { + * // do something critical here + * latch.CountDown(); + * } + * } + * function2() + * { + * latch.Wait(); // this blocks the thread until all threads that have exited function1 + * // do something here. + * } + */ +class CounterLatch final +{ +public: + CounterLatch() + : count(0) + {} + CounterLatch(const CounterLatch&) = delete; + CounterLatch(CounterLatch&&) = delete; + CounterLatch& operator=(const CounterLatch&) = delete; + CounterLatch& operator=(CounterLatch&&) = delete; + ~CounterLatch() = default; + + /** + * Increments the count of the latch, if current count is not negative + * \return \c true if the count was incremented, \c false otherwise + */ + bool CountUp() + { + std::lock_guard lock{ mtx }; + if (count >= 0) { + ++count; + return true; + } + return false; + } + + /** + * Decrements the count of the latch, releasing all waiting threads if the + * count reaches zero. + * If the current count is greater than zero then it is decremented. If + * the new count is zero then all waiting threads are notified. + * If the current count equals zero then nothing happens. + */ + void CountDown() + { + std::lock_guard lock{ mtx }; + if (count > 0) { + if (--count == 0) { + // notify waiting threads + cond.notify_all(); + } + } + } + + /** + * Waits until the counter reaches 0. The value of the counter after this + * method returns is invalid (negative). This method is designed for a + * one-time use only. + * + * \throws std::runtime_error if the current count is negative. + */ + void Wait() + { + std::unique_lock lock{ mtx }; + if (count < 0) { + throw std::runtime_error("CounterLatch is in invalid state."); + } + cond.wait(lock, [&]() { return count == 0; }); + count = std::numeric_limits< + long>::min(); // makes the latch unusable for other threads + } + + /** + * Returns the current count of the latch + */ + long GetCount() + { + std::unique_lock lock{ mtx }; + return count; + } + +private: + std::mutex mtx; ///< mutex to protect access to the counter + std::condition_variable cond; ///< used to notify the waiting thread + long count; ///< latch counter +}; + +} +} +#endif diff --git a/util/include/cppmicroservices/util/ScopeGuard.h b/framework/include/cppmicroservices/detail/ScopeGuard.h similarity index 92% rename from util/include/cppmicroservices/util/ScopeGuard.h rename to framework/include/cppmicroservices/detail/ScopeGuard.h index bc65b4295..40d9d90ee 100644 --- a/util/include/cppmicroservices/util/ScopeGuard.h +++ b/framework/include/cppmicroservices/detail/ScopeGuard.h @@ -20,11 +20,14 @@ =============================================================================*/ +#ifndef CPPMICROSERVICES_DETAIL_SCOPEGUARD_H +# define CPPMICROSERVICES_DETAIL_SCOPEGUARD_H + #include namespace cppmicroservices { -namespace util { +namespace detail { /** * A simple, single use scope guard using RAII @@ -53,3 +56,4 @@ class ScopeGuard { } } +#endif diff --git a/framework/include/cppmicroservices/detail/ServiceTracker.tpp b/framework/include/cppmicroservices/detail/ServiceTracker.tpp index 0ef2d3819..be2e91810 100755 --- a/framework/include/cppmicroservices/detail/ServiceTracker.tpp +++ b/framework/include/cppmicroservices/detail/ServiceTracker.tpp @@ -142,23 +142,36 @@ void ServiceTracker::Open() template void ServiceTracker::Close() { - std::shared_ptr<_TrackedService> outgoing = d->trackedService.Load(); - if (outgoing == nullptr) { - return; - } - - DIAG_LOG(*d->context.GetLogSink()) << "ServiceTracker::close:" << d->filter; - outgoing->Close(); - auto references = GetServiceReferences(); - try { d->context.RemoveListener(std::move(d->listenerToken)); } catch (const std::runtime_error& /*e*/) { /* In case the context was stopped or invalid. */ } - d->Modified(); /* clear the cache */ - outgoing->NotifyAll(); /* wake up any waiters */ + std::shared_ptr<_TrackedService> outgoing = d->trackedService.Load(); + { + auto l = d->Lock(); + US_UNUSED(l); + + if (outgoing == nullptr) { + return; + } + + if (d->Tracked()->closed) { + return; + } + + DIAG_LOG(*d->context.GetLogSink()) + << "ServiceTracker::close:" << d->filter; + outgoing->Close(); + + d->Modified(); /* clear the cache */ + outgoing->NotifyAll(); /* wake up any waiters */ + } + + outgoing->WaitOnCustomizersToFinish(); + + auto references = GetServiceReferences(); for(auto& ref : references) { outgoing->Untrack(ref, ServiceEvent()); } diff --git a/framework/include/cppmicroservices/detail/TrackedService.h b/framework/include/cppmicroservices/detail/TrackedService.h index fc8c7f1df..58dd528f9 100644 --- a/framework/include/cppmicroservices/detail/TrackedService.h +++ b/framework/include/cppmicroservices/detail/TrackedService.h @@ -27,6 +27,9 @@ #include "cppmicroservices/detail/BundleAbstractTracked.h" #include "cppmicroservices/detail/TrackedServiceListener.h" +#include "cppmicroservices/detail/CounterLatch.h" +#include "cppmicroservices/detail/ScopeGuard.h" + namespace cppmicroservices { namespace detail { @@ -57,12 +60,16 @@ class TrackedService */ void ServiceChanged(const ServiceEvent& event) override; + void WaitOnCustomizersToFinish(); + private: using Superclass = BundleAbstractTracked, TTT, ServiceEvent>; ServiceTracker* serviceTracker; ServiceTrackerCustomizer* customizer; + CounterLatch latch; + /** * Increment the tracking count and tell the tracker there was a * modification. diff --git a/framework/include/cppmicroservices/detail/TrackedService.tpp b/framework/include/cppmicroservices/detail/TrackedService.tpp index 03951300a..367f1c685 100644 --- a/framework/include/cppmicroservices/detail/TrackedService.tpp +++ b/framework/include/cppmicroservices/detail/TrackedService.tpp @@ -27,30 +27,52 @@ namespace detail { template TrackedService::TrackedService(ServiceTracker* serviceTracker, ServiceTrackerCustomizer* customizer) - : Superclass(&serviceTracker->d->context), serviceTracker(serviceTracker), customizer(customizer) + : Superclass(serviceTracker->d->context) + , serviceTracker(serviceTracker) + , customizer(customizer) + , latch{} { } +template +void TrackedService::WaitOnCustomizersToFinish() { + latch.Wait(); +} + template void TrackedService::ServiceChanged(const ServiceEvent& event) { - /* + (void)latch.CountUp(); + ScopeGuard sg([this]() { + // By using try/catch here, we ensure that this lambda function doesn't + // throw inside ScopeGuard's dtor. + try { + latch.CountDown(); + } catch (...) { + } + }); + + ServiceReference reference; + { + auto l = this->Lock(); + US_UNUSED(l); + /* * Check if we had a delayed call (which could happen when we * close). */ - if (this->closed) - { - return; - } + if (this->closed) { + return; + } - ServiceReference reference = event.GetServiceReference(); + reference = event.GetServiceReference(); - DIAG_LOG(*serviceTracker->d->context.GetLogSink()) << "TrackedService::ServiceChanged[" - << event.GetType() << "]: " << reference; - if (!reference) - { - return; + DIAG_LOG(*serviceTracker->d->context.GetLogSink()) + << "TrackedService::ServiceChanged[" << event.GetType() + << "]: " << reference; + if (!reference) { + return; + } } switch (event.GetType()) diff --git a/framework/test/gtest/CMakeLists.txt b/framework/test/gtest/CMakeLists.txt index 1f0e161de..be24f6d1d 100644 --- a/framework/test/gtest/CMakeLists.txt +++ b/framework/test/gtest/CMakeLists.txt @@ -46,6 +46,7 @@ set(_gtest_tests LogTest.cpp StaticBundleResourceTest.cpp FrameworkEventTest.cpp + TestCounterLatch.cpp ) if(BUILD_SHARED_LIBS) diff --git a/framework/test/gtest/ServiceTrackerTest.cpp b/framework/test/gtest/ServiceTrackerTest.cpp index c83017881..3601281c9 100644 --- a/framework/test/gtest/ServiceTrackerTest.cpp +++ b/framework/test/gtest/ServiceTrackerTest.cpp @@ -20,7 +20,6 @@ =============================================================================*/ -#include "cppmicroservices/ServiceTracker.h" #include "cppmicroservices/Bundle.h" #include "cppmicroservices/BundleContext.h" #include "cppmicroservices/Framework.h" @@ -28,6 +27,8 @@ #include "cppmicroservices/FrameworkFactory.h" #include "cppmicroservices/GetBundleContext.h" #include "cppmicroservices/ServiceInterface.h" +#include "cppmicroservices/ServiceReference.h" +#include "cppmicroservices/ServiceTracker.h" #include "ServiceControlInterface.h" #include "TestUtils.h" @@ -36,6 +37,7 @@ #include #include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -339,7 +341,7 @@ TEST_F(ServiceTrackerTestFixture, TestServiceTracker) auto o9 = st1->WaitForService(std::chrono::milliseconds(50)); ASSERT_TRUE(o9 && !o9->empty()) << "Checking WaitForService method"; - // Test that there is no RemovedService callback triggered when closing a service tracker + // Test that the RemovedService callback is triggered when closing a service tracker MockCustomizedServiceTracker customizer; // expect that closing the tracker results in RemovedService being called. @@ -356,6 +358,77 @@ TEST_F(ServiceTrackerTestFixture, TestServiceTracker) tracker->Close(); } +TEST_F(ServiceTrackerTestFixture, GetTrackingCount) +{ + BundleContext context = framework.GetBundleContext(); + + cppmicroservices::ServiceTracker tracker(context); + ASSERT_EQ(tracker.GetTrackingCount(), -1); + tracker.Open(); + ASSERT_EQ(tracker.GetTrackingCount(), 0); + + struct MyServiceOne : public MyInterfaceOne + {}; + auto svcReg = + context.RegisterService(std::make_shared()); + ASSERT_EQ(tracker.GetTrackingCount(), 1); + + svcReg.SetProperties({ { "foo", Any{ 1 } } }); + ASSERT_EQ(tracker.GetTrackingCount(), 2); + + (void)context.RegisterService( + std::make_shared()); + ASSERT_EQ(tracker.GetTrackingCount(), 3); + + svcReg.Unregister(); + ASSERT_EQ(tracker.GetTrackingCount(), 4); + + tracker.Close(); + ASSERT_EQ(tracker.GetTrackingCount(), 5); +} + +TEST_F(ServiceTrackerTestFixture, GetTracked) +{ + BundleContext context = framework.GetBundleContext(); + cppmicroservices::ServiceTracker tracker(context); + std::unordered_map, std::shared_ptr> tracked; + tracker.GetTracked(tracked); + ASSERT_TRUE(tracked.empty()); + tracker.Open(); + ASSERT_TRUE(tracked.empty()); + + struct MyServiceOne : public MyInterfaceOne + {}; + auto svcReg = + context.RegisterService(std::make_shared()); + tracker.GetTracked(tracked); + ASSERT_EQ(tracked.size(), 1ul); + + tracked.clear(); + tracker.Close(); + tracker.GetTracked(tracked); + ASSERT_TRUE(tracked.empty()); +} + +TEST_F(ServiceTrackerTestFixture, IsEmpty) +{ + BundleContext context = framework.GetBundleContext(); + cppmicroservices::ServiceTracker tracker(context); + ASSERT_TRUE(tracker.IsEmpty()); + tracker.Open(); + ASSERT_TRUE(tracker.IsEmpty()); + + struct MyServiceOne : public MyInterfaceOne + {}; + auto svcReg = + context.RegisterService(std::make_shared()); + ASSERT_FALSE(tracker.IsEmpty()); + + tracker.Close(); + ASSERT_TRUE(tracker.IsEmpty()); +} + + #ifdef US_ENABLE_THREADING_SUPPORT namespace { class FooService @@ -428,6 +501,38 @@ TEST_F(ServiceTrackerTestFixture, ServiceTrackerCloseRace) fut.get(); } +TEST_F(ServiceTrackerTestFixture, DefaultCustomizerServiceTrackerCloseRace) +{ + BundleContext context = framework.GetBundleContext(); + // test for a race in SerivceTracker::Close when no user provided + // customizer is specified and a service event is being processed by + // the service tracker while it is being destroyed. + std::promise gate; + auto gateFuture = gate.get_future(); + + std::atomic_bool keepRegisteringServices{ true }; + + auto fut = std::async(std::launch::async, + [&context, &gateFuture, &keepRegisteringServices]() { + gateFuture.get(); + while (keepRegisteringServices) { + (void)context.RegisterService( + std::make_shared()); + } + }); + + gate.set_value(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + ServiceTracker scopedTracker(context); + scopedTracker.Open(); + } // destroy scopedTracker + + keepRegisteringServices.store(false); + fut.get(); +} + TEST_F(ServiceTrackerTestFixture, ServiceTrackerConcurrentOpenClose) { BundleContext context = framework.GetBundleContext(); diff --git a/compendium/DeclarativeServices/test/TestCounterLatch.cpp b/framework/test/gtest/TestCounterLatch.cpp similarity index 96% rename from compendium/DeclarativeServices/test/TestCounterLatch.cpp rename to framework/test/gtest/TestCounterLatch.cpp index 83aaaef60..665ed0fcf 100644 --- a/compendium/DeclarativeServices/test/TestCounterLatch.cpp +++ b/framework/test/gtest/TestCounterLatch.cpp @@ -21,11 +21,13 @@ =============================================================================*/ #include "gtest/gtest.h" -#include "../src/manager/ConcurrencyUtil.hpp" -#include "ConcurrencyTestUtil.hpp" +#include "../util/ConcurrencyTestUtil.hpp" +#include "cppmicroservices/detail/CounterLatch.h" namespace cppmicroservices{ -namespace scrimpl { +namespace detail { + +using namespace test; TEST(CounterLatchTest, TestInitialState) { diff --git a/framework/test/util/ConcurrencyTestUtil.hpp b/framework/test/util/ConcurrencyTestUtil.hpp new file mode 100644 index 000000000..cbb31a47e --- /dev/null +++ b/framework/test/util/ConcurrencyTestUtil.hpp @@ -0,0 +1,64 @@ + + +#ifndef ConcurrencyTestUtil_hpp +#define ConcurrencyTestUtil_hpp + +#include +#include +#include +#include + +namespace cppmicroservices { +namespace detail { +namespace test { +/** + * Util method to determine if a future object is ready + */ +template +bool is_ready(T&& futObj) +{ + return (futObj.wait_for(std::chrono::seconds::zero()) == + std::future_status::ready); +} + +/** + * Utility method for concurrently running a function + */ +template +std::vector ConcurrentInvoke(std::function func) +{ + // test concurrent calls to enable and disable from multiple threads + std::vector returnVals; + std::promise go; + std::shared_future ready(go.get_future()); + size_t numCalls = std::max(64u, std::thread::hardware_concurrency()); + std::vector> readies(numCalls); + + // TODO: use std::barrier when available + std::vector> enable_async_futs(numCalls); + for (size_t i = 0; i < numCalls; ++i) { + enable_async_futs[i] = + std::async(std::launch::async, [&readies, i, &ready, &func]() { + readies[i].set_value(); + ready.wait(); + return func(); + }); + } + + for (size_t i = 0; i < numCalls; ++i) { + readies[i].get_future().wait(); + } + + go.set_value(); + + for (size_t i = 0; i < numCalls; ++i) { + returnVals.push_back(enable_async_futs[i].get()); + } + return returnVals; +} + +} +} +} + +#endif /* ConcurrencyTestUtil_hpp */ diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index f834300e2..6844a188e 100755 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -8,7 +8,6 @@ add_library(util OBJECT include/cppmicroservices/util/Error.h include/cppmicroservices/util/FileSystem.h include/cppmicroservices/util/MappedFile.h - include/cppmicroservices/util/ScopeGuard.h include/cppmicroservices/util/String.h src/BundleObjFactory.cpp