File tree Expand file tree Collapse file tree 4 files changed +83
-306
lines changed
Expand file tree Collapse file tree 4 files changed +83
-306
lines changed Original file line number Diff line number Diff line change @@ -206,11 +206,15 @@ namespace Concurrency {
206206 ENTROPY_ASSERT (mainThreadSelectingCount == 0 , " WorkContractGroup destroyed with threads still in selectForMainThreadExecution" );
207207
208208 // Then notify the concurrency provider to remove us from active groups
209+ // CRITICAL: Read provider without holding lock to avoid ABBA deadlock
210+ IConcurrencyProvider* provider = nullptr ;
209211 {
210212 std::unique_lock<std::shared_mutex> lock (_concurrencyProviderMutex);
211- if (_concurrencyProvider) {
212- _concurrencyProvider->notifyGroupDestroyed (this );
213- }
213+ provider = _concurrencyProvider;
214+ }
215+
216+ if (provider) {
217+ provider->notifyGroupDestroyed (this );
214218 }
215219 }
216220
Original file line number Diff line number Diff line change 2929#include < thread>
3030#include < condition_variable>
3131#include < mutex>
32+ #include < shared_mutex>
3233#include < optional>
3334#include < limits>
3435
@@ -176,9 +177,22 @@ namespace Concurrency {
176177 /* *
177178 * @brief Destructor ensures all work is stopped and completed
178179 *
179- * Calls stop() to prevent new work selection, then wait() to ensure
180- * all executing work completes. Finally notifies the concurrency provider
181- * (if any) that the group is being destroyed.
180+ * Follows a strict destruction protocol to prevent deadlocks:
181+ * 1. Calls stop() to prevent new work selection
182+ * 2. Calls wait() to ensure all executing work completes
183+ * 3. Unschedules and releases all remaining contracts
184+ * 4. Reads concurrency provider pointer WITHOUT holding mutex lock
185+ * 5. Calls notifyGroupDestroyed() to inform provider of destruction
186+ *
187+ * CRITICAL: The provider notification is made without holding the group's
188+ * concurrency provider mutex to prevent ABBA deadlock with WorkService.
189+ * Any deviation from this protocol may result in deadlock during destruction.
190+ *
191+ * The provider will then:
192+ * - Remove this group from its internal lists
193+ * - Call setConcurrencyProvider(nullptr) to clear the back-reference
194+ *
195+ * This ensures proper bidirectional cleanup without lock ordering issues.
182196 */
183197 ~WorkContractGroup ();
184198
You can’t perform that action at this time.
0 commit comments