Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance behavior of Unsatisfiable to have some predictable side-effects #409

Merged
merged 2 commits into from Feb 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 33 additions & 39 deletions autowiring/AutoPacket.h
Expand Up @@ -106,9 +106,9 @@ class AutoPacket:
/// <param name="info">The decoration which was just added to this packet</param>
/// <remarks>
/// This method results in a call to the AutoFilter method on any subscribers which are
/// satisfied by this decoration.
/// satisfied by this decoration. This method must be called with m_lock held.
/// </remarks>
void UpdateSatisfaction(const DecorationKey& info);
void UpdateSatisfactionUnsafe(std::unique_lock<std::mutex>&& lk, const DecorationDisposition& disposition);

/// <summary>
/// Performs a "satisfaction pulse", which will avoid notifying any deferred filters
Expand All @@ -126,7 +126,7 @@ class AutoPacket:
/// <summary>
/// Performs a decoration operation but does not attach priors to successors.
/// </summary>
void DecorateUnsafeNoPriors(const AnySharedPointer& ptr, const DecorationKey& key);
void DecorateNoPriors(const AnySharedPointer& ptr, DecorationKey key);

/// <summary>Runtime counterpart to Decorate</summary>
void Decorate(const AnySharedPointer& ptr, DecorationKey key);
Expand Down Expand Up @@ -185,7 +185,7 @@ class AutoPacket:
template<class T>
bool Has(int tshift=0) const {
std::lock_guard<std::mutex> lk(m_lock);
return HasUnsafe(DecorationKey(auto_id<T>::key(), tshift));
return HasUnsafe(DecorationKey(auto_id<T>::key(), true, tshift));
}

/// <summary>
Expand All @@ -197,7 +197,7 @@ class AutoPacket:

const T* retVal;
if (!Get(retVal, tshift))
ThrowNotDecoratedException(DecorationKey(auto_id<T>::key(), tshift));
ThrowNotDecoratedException(DecorationKey(auto_id<T>::key(), false, tshift));
return *retVal;
}

Expand All @@ -210,7 +210,7 @@ class AutoPacket:
/// </remarks>
template<class T>
bool Get(const T*& out, int tshift=0) const {
const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key(), tshift));
const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key(), false, tshift));
if (pDisposition) {
if (pDisposition->m_decorations.size() == 1) {
out = static_cast<const T*>(pDisposition->m_decorations[0]->ptr());
Expand Down Expand Up @@ -242,7 +242,7 @@ class AutoPacket:
template<class T>
bool Get(const std::shared_ptr<const T>*& out, int tshift=0) const {
// Decoration must be present and the shared pointer itself must also be present
const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key(), tshift));
const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key(), true, tshift));
if (!pDisposition || pDisposition->m_decorations.size() != 1) {
out = nullptr;
return false;
Expand All @@ -262,7 +262,7 @@ class AutoPacket:
template<class T>
bool Get(std::shared_ptr<const T>& out, int tshift = 0) const {
std::lock_guard<std::mutex> lk(m_lock);
auto deco = m_decorations.find(DecorationKey(auto_id<T>::key(), tshift));
auto deco = m_decorations.find(DecorationKey(auto_id<T>::key(), true, tshift));
if(deco != m_decorations.end() && deco->second.m_state == DispositionState::Satisfied) {
auto& disposition = deco->second;
if(disposition.m_decorations.size() == 1) {
Expand All @@ -274,11 +274,14 @@ class AutoPacket:
return false;
}

/// <returns>
/// The shared pointer decoration for the specified type and time shift, or nullptr if no such decoration exists
/// </returns>
template<class T>
const std::shared_ptr<const T>& GetShared(int tshift = 0) const {
const std::shared_ptr<const T>* GetShared(int tshift = 0) const {
const std::shared_ptr<const T>* retVal;
Get(retVal, tshift);
return *retVal;
return retVal;
}

/// <summary>
Expand All @@ -291,7 +294,7 @@ class AutoPacket:
template<class T>
const T** GetAll(int tshift = 0) const {
std::lock_guard<std::mutex> lk(m_lock);
auto q = m_decorations.find(DecorationKey(auto_id<T>::key(), tshift));
auto q = m_decorations.find(DecorationKey(auto_id<T>::key(), true, tshift));

// If decoration doesn't exist, return empty null-terminated buffer
if (q == m_decorations.end()) {
Expand Down Expand Up @@ -328,22 +331,8 @@ class AutoPacket:
/// </remarks>
template<class T>
void Unsatisfiable(void) {
DecorationKey key(auto_id<T>::key());
{
// Insert a null entry at this location:
std::lock_guard<std::mutex> lk(m_lock);
auto& entry = m_decorations[key];
entry.SetKey(key); // Ensure correct type if instantiated here
if(entry.m_state == DispositionState::PartlySatisfied ||
entry.m_state == DispositionState::Satisfied)
throw std::runtime_error("Cannot mark a decoration as unsatisfiable when that decoration is already present on this packet");

// Mark the entry as permanently checked-out
entry.m_state = DispositionState::Unsatisfiable;
}

// Now trigger a rescan:
MarkUnsatisfiable(key);
MarkUnsatisfiable(DecorationKey(auto_id<T>::key(), false, 0));
MarkUnsatisfiable(DecorationKey(auto_id<T>::key(), true, 0));
}

/// <summary>
Expand All @@ -356,11 +345,12 @@ class AutoPacket:
/// </remarks>
template<class T>
const T& Decorate(T t) {
DecorationKey key(auto_id<T>::key());

// Create a copy of the input, put the copy in a shared pointer
auto ptr = std::make_shared<T>(std::forward<T&&>(t));
Decorate(AnySharedPointer(ptr), key);
Decorate(
AnySharedPointer(ptr),
DecorationKey(auto_id<T>::key(), true, 0)
);
return *ptr;
}

Expand All @@ -375,7 +365,7 @@ class AutoPacket:
/// </remarks>
template<class T>
void Decorate(std::shared_ptr<T> ptr) {
DecorationKey key(auto_id<T>::key());
DecorationKey key(auto_id<T>::key(), true, 0);

// We don't want to see this overload used on a const T
static_assert(!std::is_const<T>::value, "Cannot decorate a shared pointer to const T with this overload");
Expand Down Expand Up @@ -425,8 +415,8 @@ class AutoPacket:
// Perform standard decoration with a short initialization:
std::unique_lock<std::mutex> lk(m_lock);
DecorationDisposition* pTypeSubs[1 + sizeof...(Ts)] = {
&DecorateImmediateUnsafe(DecorationKey(auto_id<T>::key()), &immed),
&DecorateImmediateUnsafe(DecorationKey(auto_id<Ts>::key()), &immeds)...
&DecorateImmediateUnsafe(DecorationKey(auto_id<T>::key(), false, 0), &immed),
&DecorateImmediateUnsafe(DecorationKey(auto_id<Ts>::key(), false, 0), &immeds)...
};
lk.unlock();

Expand All @@ -442,12 +432,14 @@ class AutoPacket:

// Now trigger a rescan to hit any deferred, unsatisfiable entries:
#if autowiring_USE_LIBCXX
for (const std::type_info* ti : {&auto_id<T>::key(), &auto_id<Ts>::key()...})
MarkUnsatisfiable(DecorationKey(*ti));
for (const std::type_info* ti : {&auto_id<T>::key(), &auto_id<Ts>::key()...}) {
MarkUnsatisfiable(DecorationKey(*ti, true, 0));
MarkUnsatisfiable(DecorationKey(*ti, false, 0));
}
#else
bool dummy[] = {
(MarkUnsatisfiable(DecorationKey(auto_id<T>::key())), false),
(MarkUnsatisfiable(DecorationKey(auto_id<Ts>::key())), false)...
(MarkUnsatisfiable(DecorationKey(auto_id<T>::key(), false, 0)), false),
(MarkUnsatisfiable(DecorationKey(auto_id<Ts>::key(), false, 0)), false)...
};
(void)dummy;
#endif
Expand Down Expand Up @@ -505,7 +497,9 @@ class AutoPacket:
/// <returns>True if the indicated type has been requested for use by some consumer</returns>
template<class T>
bool HasSubscribers(void) const {
return HasSubscribers(DecorationKey(auto_id<T>::key()));
return
HasSubscribers(DecorationKey(auto_id<T>::key(), false, 0)) ||
HasSubscribers(DecorationKey(auto_id<T>::key(), true, 0));
}

struct SignalStub {
Expand Down Expand Up @@ -597,7 +591,7 @@ template<class T>
bool AutoPacket::Get(const std::shared_ptr<T>*& out) const {
static_assert(!std::is_const<T>::value, "Overload resolution selected an incorrect version of Get");

const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key()));
const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id<T>::key(), true, 0));
if (!pDisposition || pDisposition->m_decorations.size() != 1) {
out = nullptr;
return false;
Expand Down
106 changes: 46 additions & 60 deletions autowiring/DecorationDisposition.h
Expand Up @@ -9,46 +9,71 @@
struct SatCounter;

struct DecorationKey {
DecorationKey(void) :
ti(nullptr),
is_shared(false),
tshift(-1)
{}

DecorationKey(const DecorationKey& rhs) :
ti(rhs.ti),
is_shared(rhs.is_shared),
tshift(rhs.tshift)
{}

explicit DecorationKey(const std::type_info& ti, int tshift = 0) :
ti(ti),
explicit DecorationKey(const std::type_info& ti, bool is_shared, int tshift) :
ti(&ti),
is_shared(is_shared),
tshift(tshift)
{}

// The type index
const std::type_info& ti;
const std::type_info* ti;

// True if this decoration can be used with AutoFilters that accept a shared_ptr input type
bool is_shared;

// Zero refers to a decoration created on this packet, a positive number [tshift] indicates
// a decoration attached [tshift] packets ago.
int tshift;

bool operator==(const DecorationKey& rhs) const {
return ti == rhs.ti && tshift == rhs.tshift;
return ti == rhs.ti && is_shared == rhs.is_shared && tshift == rhs.tshift;
}
};

namespace std {
template<>
struct hash<DecorationKey> {
size_t operator()(const DecorationKey& key) const {
return
key.tshift +
(key.is_shared ? 0x80000 : 0x70000) +
#if AUTOWIRING_USE_LIBCXX
return key.ti.hash_code() + key.tshift;
key.ti->hash_code();
#else
return std::type_index(key.ti).hash_code() + key.tshift;
std::type_index(*key.ti).hash_code();
#endif
}
};
}

// The possible states for a DecorationDisposition
enum class DispositionState {
// No decorations attached
Unsatisfied,

// Some decorations present, but not all of them. Cannot proceed.
PartlySatisfied,

// Everything attached, ready to go
Satisfied,

// Unsatisfiable, and the callers on this decoration cannot accept a non-null
// entry--IE, they accept const references as inputs.
UnsatisfiableNoCall,

// This decoration will never be satisfied. Calls are generated to the
Unsatisfiable
};

Expand All @@ -57,62 +82,26 @@ enum class DispositionState {
/// </remarks>
struct DecorationDisposition
{
#if AUTOWIRING_USE_LIBCXX
DecorationDisposition(DecorationDisposition&&) = delete;
void operator=(DecorationDisposition&&) = delete;
#else
// The methods below are needed for c++98 builds
DecorationDisposition(DecorationDisposition&& source) :
m_decorations(source.m_decorations),
m_pImmediate(source.m_pImmediate),
m_publishers(source.m_publishers),
m_state(source.m_state)
{}
DecorationDisposition& operator=(DecorationDisposition&& source) {
m_decorations = std::move(source.m_decorations);
m_pImmediate = source.m_pImmediate;
source.m_pImmediate = nullptr;
m_publishers = std::move(source.m_publishers);
m_state = source.m_state;
return *this;
}
#endif //AUTOWIRING_USE_LIBCXX

DecorationDisposition(void) :
m_type(nullptr),
m_pImmediate(nullptr),
m_state(DispositionState::Unsatisfied)
{}

DecorationDisposition(const DecorationDisposition& source) :
m_type(source.m_type),
m_decorations(source.m_decorations),
m_pImmediate(source.m_pImmediate),
m_publishers(source.m_publishers),
m_subscribers(source.m_subscribers),
m_state(source.m_state)
{}

DecorationDisposition& operator=(const DecorationDisposition& source) {
m_type = source.m_type;
m_pImmediate = source.m_pImmediate;
m_publishers = source.m_publishers;
m_subscribers = source.m_subscribers;
m_state = source.m_state;
return *this;
}

private:
// Destructured key for this decoration. Use accessor functions to access
// This is needed because DecorationKey doesn't have a default constructor
const std::type_info* m_type;
int m_tshift;

public:
// The decoration proper--potentially, this decoration might be from a prior execution of this
// packet. In the case of immediate decorations, this value will be invalid.
// Valid if and only if is_shared is false.
std::vector<AnySharedPointer> m_decorations;

// A pointer to the immediate decorations, if one is specified, or else nullptr
// A pointer to the immediate decorations, if one is specified, or else nullptr.
// Valid if and only if is_shared is true.
const void* m_pImmediate;

// Providers for this decoration, where it can be statically inferred. Note that a provider for
Expand All @@ -126,20 +115,17 @@ struct DecorationDisposition
// The current state of this disposition
DispositionState m_state;

// Set the key that identifies this decoration
void SetKey(const DecorationKey& key) {
m_type = &key.ti;
m_tshift = key.tshift;
}

// Get the key that identifies this decoration
DecorationKey GetKey(void) const {
assert(m_type);
return DecorationKey(*m_type, m_tshift);
}

operator bool() {
return !!m_type;
/// <returns>
/// True if all publishers have run on this disposition
/// </summary>
/// <remarks>
/// Publication is complete automatically on this type if there are no statically known publishers,
/// and at least one decoration has been attached to this disposition.
/// </remarks>
bool IsPublicationComplete(void) const {
return
!m_decorations.empty() &&
m_decorations.size() >= m_publishers.size();
}

void Reset(void) {
Expand Down
10 changes: 7 additions & 3 deletions autowiring/SharedPointerSlot.h
Expand Up @@ -135,16 +135,20 @@ struct SharedPointerSlot {
/// </remarks>
virtual void reset(void) {}

template<class T>
static const std::shared_ptr<T>& null(void) {
static const std::shared_ptr<T> s_empty;
return s_empty;
}

/// <summary>
/// Attempts to coerce this type to the specified type
/// </summary>
template<class T>
const std::shared_ptr<T>& as(void) const {
static const std::shared_ptr<T> s_empty;

if (type() == typeid(void))
// This is allowed, we always permit null to be cast to the requested type.
return s_empty;
return null<T>();

if (type() != typeid(auto_id<T>))
throw std::runtime_error("Attempted to obtain a shared pointer for an unrelated type");
Expand Down