-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Eliminate race condition in ContextMapTest.VerifyWithThreads
This race condition occurs because we weren't waiting for the context to actually finish terminating before attempting to assess the state of the context map. This meant that the secondary thread was sometimes still running even when we expected it to have already terminated.
- Loading branch information
1 parent
0e01483
commit 45428bf
Showing
5 changed files
with
92 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,44 @@ | ||
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. | ||
#pragma once | ||
#include <memory> | ||
#include MUTEX_HEADER | ||
|
||
struct CoreContextStateBlock | ||
class CoreObject; | ||
class CoreContext; | ||
|
||
struct CoreContextStateBlock: | ||
std::enable_shared_from_this<CoreContextStateBlock> | ||
{ | ||
public: | ||
CoreContextStateBlock(void); | ||
CoreContextStateBlock(std::shared_ptr<CoreContextStateBlock> parent); | ||
~CoreContextStateBlock(void); | ||
|
||
// Reference to the parent state block, where appropriate | ||
const std::shared_ptr<CoreContextStateBlock> parent; | ||
|
||
// General purpose lock for this class | ||
std::mutex m_lock; | ||
|
||
// Condition, signalled when context state has been changed | ||
std::condition_variable m_stateChanged; | ||
|
||
// Clever use of shared pointer to expose the number of outstanding CoreRunnable instances. | ||
// Destructor does nothing; this is by design. | ||
std::weak_ptr<CoreObject> m_outstanding; | ||
|
||
/// \internal | ||
/// <summary> | ||
/// Increments the total number of contexts still outstanding | ||
/// </summary> | ||
/// <param name="owner">The context that will be held for as long as the outstanding count is valid</param> | ||
/// <remarks> | ||
/// This is an indirect incrementation routine. The count will be incremented for as | ||
/// long as the returned shared_ptr is not destroyed. Once it's destroyed, the count | ||
/// is decremented. The caller is encouraged not to copy the return value, as doing | ||
/// so can give inflated values for the current number of outstanding threads. | ||
/// | ||
/// The caller is responsible for exterior synchronization | ||
/// </remarks> | ||
std::shared_ptr<CoreObject> IncrementOutstandingThreadCount(std::shared_ptr<CoreContext> owner); | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,53 @@ | ||
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. | ||
#include "stdafx.h" | ||
#include "CoreContextStateBlock.h" | ||
#include "CoreContext.h" | ||
|
||
CoreContextStateBlock::CoreContextStateBlock(void){} | ||
CoreContextStateBlock::CoreContextStateBlock(std::shared_ptr<CoreContextStateBlock> parent) : | ||
parent(parent) | ||
{} | ||
|
||
CoreContextStateBlock::~CoreContextStateBlock(void){} | ||
CoreContextStateBlock::~CoreContextStateBlock(void) {} | ||
|
||
std::shared_ptr<CoreObject> CoreContextStateBlock::IncrementOutstandingThreadCount(std::shared_ptr<CoreContext> owner) { | ||
// Optimistic check | ||
std::shared_ptr<CoreObject> retVal = m_outstanding.lock(); | ||
if (retVal) | ||
return retVal; | ||
|
||
// Double-check | ||
std::lock_guard<std::mutex> lk(m_lock); | ||
retVal = m_outstanding.lock(); | ||
if (retVal) | ||
return retVal; | ||
|
||
// Increment the parent's outstanding count as well. This will be held by the lambda, and will cause the enclosing | ||
// context's outstanding thread count to be incremented by one as long as we have any threads still running in our | ||
// context. This property is relied upon in order to get the Wait function to operate properly. | ||
std::shared_ptr<CoreObject> parentCount; | ||
if (parent) | ||
parentCount = parent->IncrementOutstandingThreadCount(owner->GetParentContext()); | ||
|
||
auto self = shared_from_this(); | ||
retVal.reset( | ||
(CoreObject*) 1, | ||
[this, self, parentCount, owner](CoreObject*) mutable { | ||
// Reset the owner before performing any other type of notification, we don't want to hold a reference | ||
// to the owner and prevent its destruction before signalling or resetting anything | ||
owner.reset(); | ||
|
||
// Object being destroyed, notify all recipients | ||
std::lock_guard<std::mutex> lk(m_lock); | ||
|
||
// Unfortunately, this destructor callback is made before weak pointers are | ||
// invalidated, which requires that we manually reset the outstanding count | ||
m_outstanding.reset(); | ||
|
||
// Wake everyone up | ||
m_stateChanged.notify_all(); | ||
} | ||
); | ||
|
||
m_outstanding = retVal; | ||
return retVal; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters