Skip to content

Commit bba7438

Browse files
Fixes CppMicroServices#986, deadlock on concurrent operations within asyncworkservice (CppMicroServices#987) (CppMicroServices#1152)
Co-authored-by: tcormackMW <113473781+tcormackMW@users.noreply.github.com>
1 parent a79f36d commit bba7438

30 files changed

Lines changed: 488 additions & 136 deletions

compendium/DeclarativeServices/src/SCRBundleExtension.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,23 @@ namespace cppmicroservices
5151
, logger(logger)
5252
, configNotifier(configNotifier)
5353
{
54-
if (!bundle || !registry || !logger || !configNotifier)
54+
if (!bundle || !registry || !logger || !configNotifier)
5555
{
5656
throw std::invalid_argument("Invalid parameters passed to SCRBundleExtension constructor");
5757
}
5858
managers = std::make_shared<std::vector<std::shared_ptr<ComponentManager>>>();
5959
}
6060

61-
void SCRBundleExtension::Initialize(cppmicroservices::AnyMap const& scrMetadata,
61+
void
62+
SCRBundleExtension::Initialize(
63+
cppmicroservices::AnyMap const& scrMetadata,
6264
std::shared_ptr<cppmicroservices::async::AsyncWorkService> const& asyncWorkService)
63-
{
64-
if ( scrMetadata.empty() || !asyncWorkService )
65+
{
66+
if (scrMetadata.empty() || !asyncWorkService)
6567
{
6668
throw std::invalid_argument("Invalid parameters passed to SCRBundleExtension::Initialize");
6769
}
68-
70+
6971
auto version = ObjectValidator(scrMetadata, "version").GetValue<int>();
7072
auto metadataparser = metadata::MetadataParserFactory::Create(version, logger);
7173
std::vector<std::shared_ptr<ComponentMetadata>> componentsMetadata;
@@ -131,12 +133,14 @@ namespace cppmicroservices
131133
"Deleting instance of SCRBundleExtension for " + bundle_.GetSymbolicName());
132134
for (auto& compManager : *managers)
133135
{
134-
auto fut = compManager->Disable();
136+
std::shared_ptr<std::atomic<bool>> asyncStarted = std::make_shared<std::atomic<bool>>(false);
137+
auto fut = compManager->Disable(asyncStarted);
135138
registry->RemoveComponentManager(compManager);
136139
try
137140
{
138-
fut.get(); // since this happens when the bundle is stopped. Wait until the disable is finished on
139-
// the other thread.
141+
// since this happens when the bundle is stopped,
142+
// wait until the disable is finished on the other thread.
143+
compManager->WaitForFuture(fut, asyncStarted);
140144
}
141145
catch (...)
142146
{

compendium/DeclarativeServices/src/manager/BundleOrPrototypeComponentConfiguration.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace cppmicroservices
3333
{
3434

3535
BundleOrPrototypeComponentConfigurationImpl::BundleOrPrototypeComponentConfigurationImpl(
36-
std::shared_ptr<const metadata::ComponentMetadata> metadata,
36+
std::shared_ptr<metadata::ComponentMetadata const> metadata,
3737
cppmicroservices::Bundle const& bundle,
3838
std::shared_ptr<ComponentRegistry> registry,
3939
std::shared_ptr<cppmicroservices::logservice::LogService> logger,
@@ -176,7 +176,9 @@ namespace cppmicroservices
176176
{
177177
try
178178
{
179-
compMgr->Disable().get();
179+
std::shared_ptr<std::atomic<bool>> asyncStarted = std::make_shared<std::atomic<bool>>(false);
180+
auto f = compMgr->Disable(asyncStarted);
181+
compMgr->WaitForFuture(f, asyncStarted);
180182
}
181183
catch (...)
182184
{

compendium/DeclarativeServices/src/manager/ComponentManager.hpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ namespace cppmicroservices
4949
ComponentManager& operator=(ComponentManager&&) = delete;
5050
virtual ~ComponentManager() = default;
5151

52+
/**
53+
* Waits for the provided future from the asynchronous thread pool and executes
54+
* the task on current thread if the thread pool has stalled.
55+
*
56+
* We determine a likely stall by timeout (50ms) while waiting for the future. After this timeout, we
57+
* atomically compare and set the bool, asyncStarted, to 'take ownership' of execution of this task. If that
58+
* bool was already set, then the spwaned thread has already taken ownership of the task and we do not need
59+
* to execute it again. Otherwise, we execute the task. If we do not hit the timeout, then there was no
60+
* stall and we can get() the future.
61+
*
62+
* \param fut The future to wait on
63+
* \param asyncStarted The bool used to synchronize the waiting and posted thread
64+
* \return void, once the future has been satisfied
65+
*/
66+
virtual void WaitForFuture(std::shared_future<void>& fut, std::shared_ptr<std::atomic<bool>> asyncStarted)
67+
= 0;
68+
5269
/**
5370
* Returns the name of the component managed by this object. The name is the same
5471
* as specified in the component description.
@@ -69,15 +86,33 @@ namespace cppmicroservices
6986
* This method changes the state of the ComponentManager to ENABLED. The method returns
7087
* immediately after changing the state. Any configurations created as a result of the
7188
* state change will happen asynchronously on a separate thread.
89+
*
90+
* \param asyncStarted The bool used to synchronize the waiting and posted thread
91+
* \parblock
92+
* If returned future IS blocked on: create asyncStarted value and pass in on calling thread.
93+
* Once the future is returned, call WaitForFuture() with the returned future and your asyncStarted value
94+
*
95+
* If future IS NOT blocked on: call Enable(), or explicitly Enable(nullptr), no other action is required
96+
* \endparblock
97+
* \return std::shared_future<void> assosciated with the state change
7298
*/
73-
virtual std::shared_future<void> Enable() = 0;
99+
virtual std::shared_future<void> Enable(std::shared_ptr<std::atomic<bool>> asyncStarted = nullptr) = 0;
74100

75101
/**
76102
* This method changes the state of the ComponentManager to DISABLED. The method returns
77103
* immediately after changing the state. Any configurations deleted as a result of the
78-
* state change will happen asynchronously on a separate thread.
104+
* state change will happen asynchronously on a separate thread.
105+
*
106+
* \param asyncStarted The bool used to synchronize the waiting and posted thread
107+
* \parblock
108+
* If future IS blocked on: create asyncStarted value and pass in on calling thread.
109+
* Once the future is returned, call WaitForFuture() with the returned future and your asyncStarted value
110+
*
111+
* If future IS NOT blocked on: call Disable(), or explicitly Disable(nullptr), no other action is required
112+
* \endparblock
113+
* \return std::shared_future<void> assosciated with the state change
79114
*/
80-
virtual std::shared_future<void> Disable() = 0;
115+
virtual std::shared_future<void> Disable(std::shared_ptr<std::atomic<bool>> asyncStarted = nullptr) = 0;
81116

82117
/**
83118
* Returns a vector of ComponentConfiguration objects representing each of the configurations
@@ -89,7 +124,7 @@ namespace cppmicroservices
89124
* Returns the metadata object representing the component description for the
90125
* component managed by this object.
91126
*/
92-
virtual std::shared_ptr<const metadata::ComponentMetadata> GetMetadata() const = 0;
127+
virtual std::shared_ptr<metadata::ComponentMetadata const> GetMetadata() const = 0;
93128
};
94129
} // namespace scrimpl
95130
} // namespace cppmicroservices

0 commit comments

Comments
 (0)