Skip to content

Commit

Permalink
Merge pull request #38 from leapmotion/ref-functionaldecoration
Browse files Browse the repository at this point in the history
AutoPacket functional decoration
  • Loading branch information
gtremper committed Aug 8, 2014
2 parents 83d4d09 + 25009fd commit 6134d94
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 8 deletions.
21 changes: 20 additions & 1 deletion autowiring/AutoPacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
#include "is_shared_ptr.h"
#include "ObjectPool.h"
#include "is_any.h"
#include "MicroAutoFilter.h"
#include <sstream>
#include <typeinfo>
#include MEMORY_HEADER
#include TYPE_INDEX_HEADER
#include STL_UNORDERED_MAP
#include EXCEPTION_PTR_HEADER

//DEBUG
#include <iostream>

class AutoPacketFactory;
class AutoPacketProfiler;
struct AutoFilterDescriptor;
Expand Down Expand Up @@ -52,6 +56,7 @@ class AutoPacket:

// Saturation counters, constructed when the packet is created and reset each time thereafter
std::vector<SatCounter> m_satCounters;
size_t m_subscriberNum;

// The set of decorations currently attached to this object, and the associated lock:
mutable std::mutex m_lock;
Expand Down Expand Up @@ -102,6 +107,11 @@ class AutoPacket:
/// </remarks>
void Finalize(void);

/// <summary>
/// Adds a recipient for data associated only with this issuance of the packet.
/// </summary>
void InitializeRecipient(const AutoFilterDescriptor& descriptor);

/// <summary>
/// Marks the specified entry as being unsatisfiable
/// </summary>
Expand Down Expand Up @@ -312,7 +322,7 @@ class AutoPacket:
/// </summary>
/// <returns>A reference to the internally persisted object</returns>
/// <remarks>
/// Unlike Publish, the Decorate method is unconditional and will install the passed
/// The Decorate method is unconditional and will install the passed
/// value regardless of whether any subscribers exist.
/// </remarks>
template<class T>
Expand Down Expand Up @@ -396,6 +406,15 @@ class AutoPacket:
PulseSatisfaction(pTypeSubs, 1 + sizeof...(Ts));
}

/// <summary>
/// Adds a function to be called as an AutoFilter for this packet only.
/// </summary>
template<class Ret, class... Args>
void AddRecipient(std::function<Ret(Args...)> f) {
std::shared_ptr<MicroAutoFilter<Ret, Args...>> filter(new MicroAutoFilter<Ret, Args...>(f));
InitializeRecipient(MakeAutoFilterDescriptor(filter));
}

/// <returns>
/// True if the indicated type has been requested for use by some consumer
/// </returns>
Expand Down
50 changes: 50 additions & 0 deletions autowiring/MicroAutoFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once

#include "is_auto_filter.h"
#include "Deferred.h"

/// <summary>
/// Transmutes a function returning void to an instance that can be called by AutoPacket.
/// The specializations distinguish between void and Deferred return types.
/// </summary>
/// <remarks>
/// The default constructor yields an AutoFilter that does nothing.
/// </remarks>
template<class Ret, class... Args>
struct MicroAutoFilter {
// This case pertains only when the return value is not recognized
static_assert(is_auto_filter_return<Ret>{},
"The return is not an allowed type for AutoFilter methods");
};
template<class... Args>
struct MicroAutoFilter<void, Args...> {
MicroAutoFilter(const std::function<void(Args...)>& filter) : m_filter(filter) {
static_assert(all_auto_filter_args<Args...>::value,
"At least one argument is not an allowed type for AutoFilter methods");
}

void AutoFilter(Args... args) {
if (m_filter)
return m_filter(std::move(args)...);
}

protected:
std::function<void(Args...)> m_filter;
};
template<class... Args>
struct MicroAutoFilter<Deferred, Args...> {
MicroAutoFilter(const std::function<void(Args...)>& filter) : m_filter(filter) {
static_assert(all_auto_filter_args<Args...>::value,
"At least one argument is not an allowed type for AutoFilter methods");
}

Deferred AutoFilter(Args... args) {
if (m_filter)
return m_filter(args...);
return Deferred(this);
}

protected:
std::function<void(Args...)> m_filter;
};
84 changes: 84 additions & 0 deletions autowiring/is_auto_filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once

#include <functional>
#include TYPE_TRAITS_HEADER

class Deferred;
template<class T, bool auto_ready> class auto_out;

/// <summary>
/// Determines whether Arg is an instance of auto_out
/// </summary>
template<class Arg>
struct is_auto_out :
std::false_type
{};
template<class Arg, bool auto_ready>
struct is_auto_out<auto_out<Arg, auto_ready>> :
std::true_type
{};

/// <summary>
/// Determines whether Arg is an allowed AutoFilter argument:
/// - const input& output
/// - auto_out<ouput>&
/// </summary>
/// <remarks>
/// Only strict declaration types are supported.
/// The argument type "const AutoPacket&" is valid but "AutoPacket&" is not.
/// </remarks>
template<class Arg>
struct is_auto_filter_arg :
std::integral_constant<bool,
(std::is_reference<Arg>{} && std::is_const<typename std::remove_reference<Arg>::type>{}) ||
is_auto_out<typename std::remove_reference<Arg>::type>{}
>
{};

/// <summary>
/// Determines whether Arg is an allowed AutoFilter argument list.
/// </summary>
/// <remarks>
/// Only strict declaration types are supported.
/// At least one argument is required in order for the function to be valid.
/// If calling on instantiation is desired declare "const AutoPacket&"
/// as the only argument.
/// </remarks>
template<class... Args>
struct all_auto_filter_args :
std::false_type
{};
template<class Head, class... Tail>
struct all_auto_filter_args<Head, Tail...> :
std::integral_constant<bool, is_auto_filter_arg<Head>{} && all_auto_filter_args<Tail...>{}>
{};
template<class Head>
struct all_auto_filter_args<Head> :
is_auto_filter_arg<Head>
{};

/// <summary>
/// Determines whether the return value of a function is allowed for an AutoFilter:
/// - void
/// - Deferred
/// </summary>
template<class Ret>
struct is_auto_filter_return :
std::integral_constant<bool,
std::is_same<void, Ret>{} ||
std::is_same<Deferred, Ret>{}
>
{};

///<summary>
/// Determines whether T has the type of an autofilter method
///</summary>
template <class T>
struct is_auto_filter :
std::false_type
{};
template <class Ret, class... Args>
struct is_auto_filter<std::function<Ret(Args...)>> :
std::integral_constant<bool, is_auto_filter_return<Ret>{} && all_auto_filter_args<Args...>{}>
{};
99 changes: 99 additions & 0 deletions src/autowiring/AutoPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ AutoPacket::AutoPacket(AutoPacketFactory& factory, const std::shared_ptr<Object>
}
}

// Record divide between subscribers & recipients
m_subscriberNum = m_satCounters.size();

Reset();
}

Expand Down Expand Up @@ -213,9 +216,105 @@ void AutoPacket::Finalize(void) {
for (SatCounter* call : callQueue)
call->CallAutoFilter(*this);

// Remove all recipients & clean up the decorations list
// ASSERT: This reverse the order of accumulation,
// so searching for the subscriber is avoided.
while (m_satCounters.size() > m_subscriberNum) {
SatCounter& recipient = m_satCounters.back();

for(auto pCur = recipient.GetAutoFilterInput();
*pCur;
pCur++
) {
DecorationDisposition& entry = m_decorations[*pCur->ti];
switch(pCur->subscriberType) {
case inTypeInvalid:
// Should never happen--trivially ignore this entry
break;
case inTypeRequired:
assert(entry.m_subscribers.size() > 0);
assert(&recipient == entry.m_subscribers.back().first);
entry.m_subscribers.pop_back();
break;
case inTypeOptional:
assert(entry.m_subscribers.size() > 0);
assert(&recipient == entry.m_subscribers.back().first);
entry.m_subscribers.pop_back();
break;
case outTypeRef:
case outTypeRefAutoReady:
assert(&recipient == entry.m_publisher);
entry.m_publisher = nullptr;
break;
}
}

m_satCounters.pop_back();
}

// Remove decoration dispositions specific to subscribers
t_decorationMap::iterator dItr = m_decorations.begin();
t_decorationMap::iterator dEnd = m_decorations.end();
while (dItr != dEnd) {
if (dItr->second.m_subscribers.empty())
dItr = m_decorations.erase(dItr);
else
++dItr;
}

Reset();
}

void AutoPacket::InitializeRecipient(const AutoFilterDescriptor& descriptor) {
SatCounter* call = nullptr;
{
std::lock_guard<std::mutex> lk(m_lock);

// (1) Append & Initialize new satisfaction counter
m_satCounters.push_back(descriptor);
SatCounter& recipient = m_satCounters.back();
recipient.Reset();

// (2) Update satisfaction & Append types from subscriber
for(auto pCur = recipient.GetAutoFilterInput();
*pCur;
pCur++
) {
DecorationDisposition& entry = m_decorations[*pCur->ti];
switch(pCur->subscriberType) {
case inTypeInvalid:
// Should never happen--trivially ignore this entry
break;
case inTypeRequired:
entry.m_subscribers.push_back(std::make_pair(&recipient, true));
if (entry.satisfied)
recipient.Decrement(true);
break;
case inTypeOptional:
entry.m_subscribers.push_back(std::make_pair(&recipient, false));
if (entry.satisfied)
recipient.Decrement(false);
break;
case outTypeRef:
case outTypeRefAutoReady:
if(entry.m_publisher)
throw autowiring_error("Added two publishers of the same decoration to the same factory");
entry.m_publisher = &recipient;
break;
}
}

// (3) Check call status inside of lock
if (recipient) {
call = &recipient;
}
}

// (3) If all types are satisfied, call AutoFilter now.
if (call)
call->CallAutoFilter(*this);
}

bool AutoPacket::HasSubscribers(const std::type_info& ti) const {
std::lock_guard<std::mutex> lk(m_lock);
return m_decorations.count(ti) != 0;
Expand Down
2 changes: 2 additions & 0 deletions src/autowiring/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ set(Autowiring_SRCS
index_tuple.h
is_any.h
is_shared_ptr.h
is_auto_filter.h
InterlockedExchange.h
InvokeRelay.h
atomic_object.h
Expand All @@ -90,6 +91,7 @@ set(Autowiring_SRCS
ObjectPoolMonitor.cpp
optional_ptr.h
SatCounter.h
MicroAutoFilter.h
MicroBolt.h
SharedPointerSlot.h
SlotInformation.h
Expand Down
Loading

0 comments on commit 6134d94

Please sign in to comment.