Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
codemercenary committed Mar 27, 2015
2 parents 109b87e + 18b0cb4 commit 51186d8
Show file tree
Hide file tree
Showing 27 changed files with 753 additions and 232 deletions.
2 changes: 1 addition & 1 deletion Doxyfile
Expand Up @@ -32,7 +32,7 @@ PROJECT_NAME = Autowiring
# This could be handy for archiving the generated documentation or
# if some version control system is used.

PROJECT_NUMBER = "0.5.0"
PROJECT_NUMBER = "0.5.1"

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer
Expand Down
20 changes: 18 additions & 2 deletions autowiring/AutoConfig.h
Expand Up @@ -40,9 +40,21 @@ class AutoConfig:
public AutoConfigBase
{
public:
static_assert(sizeof...(TKey)==1 || sizeof...(TKey)==2, "Must provide a key and optional namespace");
static_assert(sizeof...(TKey) >= 1, "Must provide a key and optionally at least one namespace");

AutoConfig(void) :
template<typename t_Arg, typename ...t_Args>
AutoConfig(t_Arg &&arg, t_Args&&... args) :
AutoConfigBase(typeid(ConfigTypeExtractor<TKey...>))
{
// Register with config registry
(void)RegConfig<T, TKey...>::r;

if (!IsConfigured()){
m_manager->Set(m_key, T(std::forward<t_Arg>(arg), std::forward<t_Args>(args)...));
}
}

AutoConfig() :
AutoConfigBase(typeid(ConfigTypeExtractor<TKey...>))
{
// Register with config registry
Expand All @@ -61,6 +73,10 @@ class AutoConfig:
return m_manager->Get(m_key)->template as<T>().get();
}

void operator=(const T& newValue) {
return m_manager->Set(m_key, newValue);
}

/// <returns>
/// True if this configurable field has been satisfied with a value
/// </returns>
Expand Down
19 changes: 17 additions & 2 deletions autowiring/AutoConfigManager.h
Expand Up @@ -21,6 +21,8 @@ class AutoConfigManager:

// Callback function type
typedef std::function<void(const AnySharedPointer&)> t_callback;

typedef std::function<void(const std::string&, const AnySharedPointer&)> t_add_callback;

// Validator function type
typedef std::function<bool(const AnySharedPointer&)> t_validator;
Expand All @@ -41,9 +43,15 @@ class AutoConfigManager:
// Set of keys for values set from this context
std::unordered_set<std::string> m_setHere;

// list of keys for values set from this context in order of creation.
std::vector<std::string> m_orderedKeys;

// map of callbacks registered for a key
std::unordered_map<std::string, std::vector<t_callback>> m_callbacks;

// list of callbacks registered for keys which exist in this context.
std::list<t_add_callback> m_addCallbacks;

// Exception throwers:
void ThrowKeyNotFoundException(const std::string& key) const;
void ThrowTypeMismatchException(const std::string& key, const std::type_info& ti) const;
Expand All @@ -67,7 +75,7 @@ class AutoConfigManager:
/// in the application, or if the specified value type does not match the type expected by this field
/// </remarks>
AnySharedPointer& Get(const std::string& key);

/// <summary>
/// Assigns the specified value to an AnySharedPointer slot
/// </summary>
Expand Down Expand Up @@ -106,7 +114,14 @@ class AutoConfigManager:

// Add a callback for when key is changed in this context
void AddCallback(const std::string& key, t_callback&& fx);


// Add a callback for when a key is set in this context. Is immediately called on all
// currently existing values in the order they were created
void AddCallback(t_add_callback&& fx);

// Returns a list of all keys which have been set from this context
const std::vector<std::string>& GetLocalKeys() const { return m_orderedKeys; }

private:
// Handles setting a value recursivly to all child contexts
void SetRecursive(const std::string& key, AnySharedPointer value);
Expand Down
9 changes: 4 additions & 5 deletions autowiring/AutowiringEnclosure.h
Expand Up @@ -102,11 +102,10 @@ class AutowiringEnclosure:
auto ctxt = ecef ? ecef->GetContext() : nullptr;
ctxt->SignalShutdown();

// Do not allow teardown to take more than 250 milliseconds or so
if(!ctxt->Wait(std::chrono::milliseconds(250))) {
// Critical error--took too long to tear down
assert(false);
}
// Do not allow teardown to take more than 5 seconds. This is considered a "timely teardown" limit.
// If it takes more than this amount of time to tear down, the test case itself should invoke SignalShutdown
// and Wait itself with the extended teardown period specified.
ASSERT_TRUE(ctxt->Wait(std::chrono::seconds(5))) << "Test case took too long to tear down, unit tests running after this point are untrustworthy";

static const char sc_autothrow [] = "AUTOTHROW_";
if(!strncmp(sc_autothrow, info.name(), sizeof(sc_autothrow) - 1))
Expand Down
35 changes: 32 additions & 3 deletions autowiring/ContextEnumerator.h
@@ -1,5 +1,6 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include "autowiring_error.h"
#include MEMORY_HEADER

class CoreContext;
Expand All @@ -26,6 +27,11 @@ class CoreContextT;
class ContextEnumerator
{
public:
/// <summary>
/// Constructs a context enumerator for the current context
/// </summary>
ContextEnumerator(void);

/// <summary>
/// Constructs an enumerator which may enumerate all of the contexts rooted at the specified root
/// </summary>
Expand All @@ -41,7 +47,7 @@ class ContextEnumerator
/// The iterator class which is actually used in enumerating contexts
class iterator {
public:
typedef std::input_iterator_tag iterator_category;
typedef std::forward_iterator_tag iterator_category;
typedef const std::shared_ptr<CoreContext>& value_type;
typedef size_t difference_type;
typedef const std::shared_ptr<CoreContext>* pointer;
Expand All @@ -67,8 +73,10 @@ class ContextEnumerator

// Operator overloads:
const iterator& operator++(void);
iterator operator++(int);

const std::shared_ptr<CoreContext>& operator*(void) const { return m_cur; }
const CoreContext& operator->(void) const { return ***this; }
const std::shared_ptr<CoreContext>& operator->(void) const { return m_cur; }
bool operator==(const iterator& rhs) const { return m_root == rhs.m_root && m_cur == rhs.m_cur; }
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
explicit operator bool(void) const { return !!m_cur.get(); }
Expand Down Expand Up @@ -96,7 +104,9 @@ class ContextEnumeratorT:
public ContextEnumerator
{
public:
ContextEnumeratorT(const std::shared_ptr<CoreContext>& ctxt):
ContextEnumeratorT()
{}
ContextEnumeratorT(const std::shared_ptr<CoreContext>& ctxt) :
ContextEnumerator(ctxt)
{}

Expand Down Expand Up @@ -126,10 +136,29 @@ class ContextEnumeratorT:
return *this;
}

iterator operator++(int) {
auto retVal = *this;
ContextEnumerator::iterator::operator++(0);
_advance();
return retVal;
}

std::shared_ptr<CoreContextT<Sigil>> operator*(void) const { return std::static_pointer_cast<CoreContextT<Sigil>>(m_cur); }
const CoreContext& operator->(void) const { return ***this; }
};

// Convenience routine for returning a single unique element
std::shared_ptr<CoreContext> unique(void) {
iterator q = begin();
iterator r = q;

// If advancing q gets us to the end, then we only have one element and we can return success
if (++q == end())
return *r;

throw autowiring_error("Attempted to get a unique context on a context enumerator that enumerates more than one child");
}

// Standard STL duck interface methods:
iterator begin(void) { return iterator(m_root, m_root); };
iterator end(void) { return iterator(m_root); }
Expand Down
91 changes: 75 additions & 16 deletions autowiring/ContextMap.h
Expand Up @@ -2,7 +2,7 @@
#pragma once
#include "autowiring_error.h"
#include "CoreContext.h"
#include STL_UNORDERED_MAP
#include <map>

extern std::shared_ptr<CoreContext> NewContextThunk(void);

Expand Down Expand Up @@ -30,40 +30,92 @@ class ContextMap
tracker(void):
destroyed(false)
{}

// True when the enclosing context map has been destroyed
bool destroyed;
};

ContextMap(void):
m_tracker(new tracker),
m_lk(*m_tracker)
m_tracker(std::make_shared<tracker>())
{
}

~ContextMap(void) {
// Teardown pathway assurance:
(std::lock_guard<std::mutex>)m_lk,
(m_tracker->destroyed = true);

// Done, we can release our copy of the shared pointer and end here
m_tracker.reset();
std::lock_guard<std::mutex> lk(*m_tracker);
m_tracker->destroyed = true;
}

private:
// Tracker lock, used to protect against accidental destructor-contending access while still allowing
// the parent ContextMap structure to be stack-allocated
std::shared_ptr<tracker> m_tracker;

typedef std::unordered_map<Key, std::weak_ptr<CoreContext>> t_mpType;
std::mutex& m_lk;
const std::shared_ptr<tracker> m_tracker;
typedef std::map<Key, std::weak_ptr<CoreContext>> t_mpType;
t_mpType m_contexts;

public:
// Accessor methods:
size_t size(void) const {return m_contexts.size();}

class iterator {
public:
iterator(ContextMap& parent) :
parent(parent)
{
std::lock_guard<std::mutex> lk(*parent.m_tracker);

// Advance to the first iterator we can actually lock down:
iter = parent.m_contexts.begin();
while (
iter != parent.m_contexts.end() &&
!(ctxt = iter->second.lock())
)
// Failure, next entry
iter++;
}

iterator(ContextMap& parent, typename t_mpType::iterator iter, std::shared_ptr<CoreContext> ctxt) :
parent(parent),
iter(iter),
ctxt(ctxt)
{}

private:
ContextMap& parent;
typename t_mpType::iterator iter;
std::shared_ptr<CoreContext> ctxt;

public:
const iterator& operator++(void) {
// We need to ensure our local shared pointer doesn't go away until after we have
// advanced to the next entry
std::shared_ptr<CoreContext> deferred = std::move(ctxt);

// Loop until we get another stable entry:
std::lock_guard<std::mutex> lk(*parent.m_tracker);
do iter++;
while (iter != parent.m_contexts.end() && !(ctxt = iter->second.lock()));

// We can safely unlock because the current entry won't be evicted automatically as long as
// the shared pointer reference is held down.
return *this;
}

const std::shared_ptr<CoreContext>& operator*(void) const { return ctxt; }
const CoreContext& operator->(void) const { return *ctxt; }
bool operator==(const iterator& rhs) const { return &parent == &rhs.parent && iter == rhs.iter; }
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
explicit operator bool(void) const {
return ctxt;
}
};

iterator begin(void) { return iterator(*this); }
iterator end(void) { return iterator(*this, m_contexts.end(), nullptr); }

template<class Fn>
void Enumerate(Fn&& fn) {
std::lock_guard<std::mutex> lk(m_lk);
std::lock_guard<std::mutex> lk(*m_tracker);
for(const auto& entry : m_contexts) {
auto ctxt = entry.second.lock();
if(ctxt && !fn(entry.first, ctxt))
Expand All @@ -81,7 +133,7 @@ class ContextMap
/// An exception will be thrown if the passed key is already associated with a context
/// </remarks>
void Add(const Key& key, std::shared_ptr<CoreContext>& context) {
std::lock_guard<std::mutex> lk(m_lk);
std::lock_guard<std::mutex> lk(*m_tracker);
auto& rhs = m_contexts[key];
if(!rhs.expired())
throw autowiring_error("Specified key is already associated with another context");
Expand Down Expand Up @@ -126,13 +178,20 @@ class ContextMap
/// <summary>
/// Attempts to find a context by the specified key
/// </summary>
std::shared_ptr<CoreContext> Find(const Key& key) {
std::lock_guard<std::mutex> lk(m_lk);
std::shared_ptr<CoreContext> Find(const Key& key) const {
std::lock_guard<std::mutex> lk(*m_tracker);
auto q = m_contexts.find(key);

return
q == m_contexts.end() ?
std::shared_ptr<CoreContext>() :
q->second.lock();
}

/// <summary>
/// Identical to Find
/// </summary>
std::shared_ptr<CoreContext> operator[](const Key& key) const {
return Find(key);
}
};

0 comments on commit 51186d8

Please sign in to comment.