Skip to content

Commit

Permalink
Merge pull request #287 from leapmotion/feat-autopacketgraph
Browse files Browse the repository at this point in the history
AutoPacketGraph
  • Loading branch information
codemercenary committed Dec 23, 2014
2 parents 3b4e434 + 5a94dc5 commit e84f8d9
Show file tree
Hide file tree
Showing 11 changed files with 438 additions and 38 deletions.
6 changes: 6 additions & 0 deletions autowiring/AutoPacket.h
Expand Up @@ -512,6 +512,12 @@ class AutoPacket:
return GetSubscribers(typeid(auto_id<T>));
}

/// <returns>All decoration dispositions</returns>
/// <remarks>
/// This method is useful for getting a picture of the entire disposition graph
/// </remarks>
std::list<DecorationDisposition> GetDispositions() const;

/// <returns>All decoration dispositions associated with the data type</returns>
/// <remarks>
/// This method is useful for determining whether flow conditions (broadcast, pipes
Expand Down
122 changes: 122 additions & 0 deletions autowiring/AutoPacketGraph.h
@@ -0,0 +1,122 @@
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once
#include "AutoFilterDescriptor.h"
#include "AutoPacket.h"
#include "AutoPacketFactory.h"
#include "Autowired.h"
#include "AutowiringEvents.h"
#include "CoreRunnable.h"
#include STL_UNORDERED_MAP


/// <summary>
/// Represents an edge in the graph from a type to an AutoFilter
/// </summary>
struct DeliveryEdge
{
// The type info
const std::type_info* type_info;

// The AutoFilterDescriptor
AutoFilterDescriptor descriptor;

// Specifies if the argument is an input (type -> descriptor) or output (descriptor -> type)
bool input;

// For the unordered map/hash comparison
bool operator==(const DeliveryEdge& rhs) const {
return
type_info == rhs.type_info &&
descriptor == rhs.descriptor &&
input == rhs.input;
}
};

/// <summary>
/// Using the same hash function as the AutoFilterDescriptor
/// </summary>
namespace std {
template<>
struct hash<DeliveryEdge>
{
size_t operator()(const DeliveryEdge& edge) const {
return (size_t) edge.descriptor.GetAutoFilter()->ptr();
}
};
}

/// <summary>
/// Graphical visualization of AutoPackets
/// </summary>
class AutoPacketGraph:
public AutowiringEvents,
public CoreRunnable
{
public:
AutoPacketGraph();

typedef std::unordered_map<DeliveryEdge, size_t, std::hash<DeliveryEdge>> t_deliveryEdges;

protected:
// A mapping of an edge to the number of times it was delivered
t_deliveryEdges m_deliveryGraph;

// A lock for this type
mutable std::mutex m_lock;

// Reference to the AutoPacketFactory
AutoRequired<AutoPacketFactory> m_factory;

/// <summary>
/// Demangle a type name as well as stripping "auto_in< >"
/// </summary>
/// <remarks>
/// The ">" that encloses the templates will have extra spaces between them, for instance
///
/// auto_in<Class>
/// auto_in<Class<T> >
/// auto_in<Class1<Class2, Class3<Class4> > >
///
/// All we care about is matching "^auto_in<(.*)>$"
/// </remarks>
std::string DemangleTypeName(const std::type_info* type_info) const;

/// <summary>
/// Scan all of the objects and add any AutoFilter's from all of the objects in a system.
/// </summary>
/// <remarks>
/// This function will scan all of the objects (and rescan) and only add new edges to our graph.
/// </remarks>
void LoadEdges();

/// <summary>
/// Record the delivery of a packet and increment the number of times the packet has been delivered
/// </summary>
void RecordDelivery(const std::type_info* ti, const AutoFilterDescriptor& descriptor, bool input);

/// AutowiringEvents overrides
virtual void NewContext(CoreContext&) override {}
virtual void ExpiredContext(CoreContext&) override {}
virtual void EventFired(CoreContext&, const std::type_info&) override {}
virtual void NewObject(CoreContext&, const ObjectTraits&) override;

/// CoreRunnable overrides
virtual bool OnStart(void) override;

public:
/// <summary>
/// Get a copy of the packet via AutoFilter
/// </summary>
void AutoFilter(AutoPacket& packet);

/// <summary>
/// Write the graph to a file in graphviz format
/// </summary>
/// <param name="filename">
/// The name of the file to write the graph to
/// </param>
/// <param name="numPackets">
/// Include the number of times the packet was delivered
/// </param>
bool WriteGV(const std::string& filename, bool numPackets = false) const;
};
68 changes: 45 additions & 23 deletions autowiring/CoreContext.h
Expand Up @@ -106,6 +106,25 @@ class CoreContext:
/// </summary>
static std::shared_ptr<CoreContext> GetGlobal(void);

/// <summary>
/// Represents a single entry, together with any deferred elements waiting on the satisfaction of this entry
/// </summary>
struct MemoEntry {
MemoEntry(void) :
pFirst(nullptr)
{}

// The first deferrable autowiring which requires this type, if one exists:
DeferrableAutowiring* pFirst;

// A back reference to the concrete type from which this memo was generated:
const ObjectTraits* pObjTraits;

// Once this memo entry is satisfied, this will contain the AnySharedPointer instance that performs
// the satisfaction
AnySharedPointer m_value;
};

protected:
// A pointer to the parent context
const std::shared_ptr<CoreContext> m_pParent;
Expand Down Expand Up @@ -133,22 +152,6 @@ class CoreContext:
typedef std::unordered_map<std::type_index, std::list<BoltBase*>> t_contextNameListeners;
t_contextNameListeners m_nameListeners;

/// <summary>
/// Represents a single entry, together with any deferred elements waiting on the satisfaction of this entry
/// </summary>
struct MemoEntry {
MemoEntry(void) :
pFirst(nullptr)
{}

// The first deferrable autowiring which requires this type, if one exists:
DeferrableAutowiring* pFirst;

// Once this memo entry is satisfied, this will contain the AnySharedPointer instance that performs
// the satisfaction
AnySharedPointer m_value;
};

/// <summary>
/// A proxy context member that knows how to create a factory for a particular type
/// </summary>
Expand Down Expand Up @@ -257,7 +260,7 @@ class CoreContext:
/// <summary>
/// Invokes all deferred autowiring fields, generally called after a new member has been added
/// </summary>
void UpdateDeferredElements(std::unique_lock<std::mutex>&& lk, const std::shared_ptr<Object>& entry);
void UpdateDeferredElements(std::unique_lock<std::mutex>&& lk, const ObjectTraits& entry);

/// <summary>
/// Adds the named event receiver to the collection of known receivers
Expand Down Expand Up @@ -427,6 +430,19 @@ class CoreContext:
/// </returns>
std::shared_ptr<CoreContext> NextSibling(void) const;

/// <returns>
/// The type identifier of the specified type identifier
/// </returns>
/// <remarks>
/// The returned type structure will be the actual type of the specified object as defined at the time of
/// injection. In the case of a static factory new or AutoFactory new, this type will be the type of the
/// interface. All other members are the concrete type actually injected, as opposed to the type unifier
/// for that type.
///
/// This method will throw an exception if the passed shared pointer is not strictly a member of this context
/// </remarks>
const std::type_info& GetAutoTypeId(const AnySharedPointer& ptr) const;

/// <summary>
/// Creation helper routine
/// </summary>
Expand Down Expand Up @@ -505,15 +521,21 @@ class CoreContext:
// Add this type to the TypeRegistry
(void) RegType<T>::r;

// First see if the object has already been injected:
std::shared_ptr<typename CreationRules::TActual> retVal;
FindByType(retVal);
if(retVal)
return std::static_pointer_cast<T>(retVal);
// First see if the base object type has already been injected. This is also necessary to
// ensure that a memo slot is created for the type by itself, in cases where the injected
// member does not inherit Object and this member is eventually satisfied by one that does.
{
std::shared_ptr<T> pure;
FindByType(pure);
if (pure)
return pure;
}

// We must make ourselves current for the remainder of this call:
CurrentContextPusher pshr(shared_from_this());
retVal.reset(CreationRules::New(*this, std::forward<Args>(args)...));
std::shared_ptr<typename CreationRules::TActual> retVal(
CreationRules::New(*this, std::forward<Args>(args)...)
);

// AutoInit if sensible to do so:
CallAutoInit(*retVal, has_autoinit<T>());
Expand Down
2 changes: 2 additions & 0 deletions autowiring/DecorationDisposition.h
Expand Up @@ -46,6 +46,7 @@ struct DecorationDisposition
m_type(source.m_type),
m_pImmediate(source.m_pImmediate),
m_publisher(source.m_publisher),
m_subscribers(source.m_subscribers),
isCheckedOut(source.isCheckedOut),
satisfied(source.satisfied)
{}
Expand All @@ -54,6 +55,7 @@ struct DecorationDisposition
m_type = source.m_type;
m_pImmediate = source.m_pImmediate;
m_publisher = source.m_publisher;
m_subscribers = source.m_subscribers;
isCheckedOut = source.isCheckedOut;
satisfied = source.satisfied;
return *this;
Expand Down
11 changes: 11 additions & 0 deletions src/autowiring/AutoPacket.cpp
Expand Up @@ -37,6 +37,9 @@ AutoPacket::~AutoPacket(void) {
current = current->m_successor.get();
prev_current->m_successor.reset();
}

// Needed for the AutoPacketGraph
NotifyTeardownListeners();
}

DecorationDisposition& AutoPacket::CheckoutImmediateUnsafe(const std::type_info& ti, const void* pvImmed)
Expand Down Expand Up @@ -370,6 +373,14 @@ void AutoPacket::RemoveRecipient(Recipient&& recipient) {
m_satCounters.erase(q);
}

std::list<DecorationDisposition> AutoPacket::GetDispositions() const {
std::lock_guard<std::mutex> lk(m_lock);
std::list<DecorationDisposition> dispositions;
for (auto& disposition : m_decorations)
dispositions.push_back(disposition.second);
return dispositions;
}

std::shared_ptr<AutoPacketInternal> AutoPacket::SuccessorInternal(void) {
std::lock_guard<std::mutex> lk(m_lock);

Expand Down

0 comments on commit e84f8d9

Please sign in to comment.