Skip to content

Commit

Permalink
Merge pull request #37 from leapmotion/test-autoselfupdate
Browse files Browse the repository at this point in the history
AutoSelfUpdate implementation
  • Loading branch information
Graham Tremper committed Aug 7, 2014
2 parents d8e84a3 + 415c7b9 commit 83d4d09
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 110 deletions.
39 changes: 31 additions & 8 deletions autowiring/AutoSelfUpdate.h
@@ -1,28 +1,51 @@
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once
#include "Autowiring/NewAutoFilter.h"
#include "Autowiring/atomic_object.h"
#include "NewAutoFilter.h"
#include "shared_object.h"

///<summary>
///Enables an automatic self-update when a packet is decorated with the object type.
///Provides the prior object (the last decorated instance) to all subsequent packets.
///</summary>
///<remarks>
///In order to ensure that this method will be consistent with any other AutoFilter calls,
///the object inherits from atomic_object, which implements basic locking functionality.
///the object inherits from shared_object, which implements basic locking functionality.
///</remarks>
template<class object, class lock = std::mutex>
class AutoSelfUpdate:
public atomic_object<object, lock> {
public shared_object<object, lock> {
protected:
using shared_object<object, lock>::get_lock;
using shared_object<object, lock>::get_object;

public:
AutoSelfUpdate() {}
AutoSelfUpdate(const shared_object<object, lock>& source) : shared_object<object, lock>(source) {}
AutoSelfUpdate(const object& source) : shared_object<object, lock>(source) {}
using shared_object<object, lock>::operator =;
using shared_object<object, lock>::operator object;

//The distinct type assigned to the prior value of the object
class prior_object: public object {
public:
prior_object(const object& source): object(source) {}
};

//Avoid intermediate copy by defining an explicit cast
operator prior_object() const {
std::lock_guard<lock> lock_this(get_lock());
return prior_object(get_object());
}

//Decorates all packets with instances of prior_object
void AutoFilter(AutoPacket& packet) {
//TODO: Decorate with prior<object>
packet.Decorate(this->operator prior_object());
}

void SelfUpdate(object& update) {
atomic_object<object, lock>::operator = (update);
//Updates this object
void AutoGather(const object& update) {
shared_object<object, lock>::operator = (update);
}

NewAutoFilter<decltype(&AutoSelfUpdate<object>::SelfUpdate), &AutoSelfUpdate<object>::SelfUpdate> AutoFilter_SelfUpdate;
NewAutoFilter<decltype(&AutoSelfUpdate<object>::AutoGather), &AutoSelfUpdate<object>::AutoGather> SelfUpdate;
};
7 changes: 0 additions & 7 deletions autowiring/NewAutoFilter.h
Expand Up @@ -8,13 +8,6 @@
class NewAutoFilterBase {
protected:
NewAutoFilterBase(const AutoFilterDescriptorStub& stub);

public:
// The actual descriptor stub
const AutoFilterDescriptorStub& m_stub;

// The next filter AutoFilter entry in the series
NewAutoFilterBase* pFlink;
};

template<class MemFn, MemFn memFn>
Expand Down
40 changes: 24 additions & 16 deletions autowiring/SlotInformation.h
Expand Up @@ -4,8 +4,18 @@
#include <typeinfo>
#include MEMORY_HEADER

struct AutoFilterDescriptorStub;
class DeferrableAutowiring;
class NewAutoFilterBase;

struct AutoFilterDescriptorStubLink {
AutoFilterDescriptorStubLink(const AutoFilterDescriptorStub& stub, const AutoFilterDescriptorStubLink* pFlink) :
stub(stub),
pFlink(pFlink)
{}

const AutoFilterDescriptorStub& stub;
const AutoFilterDescriptorStubLink* const pFlink;
};

/// <summary>
/// Represents information about a single slot detected as having been declared in a context member
Expand Down Expand Up @@ -50,7 +60,7 @@ struct SlotInformationStumpBase {
// If there are any custom AutoFilter fields defined, this is the first of them
// Note that these custom fields -only- include fields registered via the AutoFilter
// registration type
NewAutoFilterBase* pFirstAutoFilter;
const AutoFilterDescriptorStubLink* pFirstAutoFilter;
};

/// <summary>
Expand Down Expand Up @@ -123,7 +133,8 @@ class SlotInformationStackLocation {
// Current slot information:
SlotInformation* m_pCur;

// If this type has additional AutoFilter routines,
// Most recent AutoFilter descriptor link:
AutoFilterDescriptorStubLink* m_pLastLink;

// Information about the object being constructed while this stack location is valid:
const void* m_pObj;
Expand All @@ -150,18 +161,11 @@ class SlotInformationStackLocation {
/// <param name="pSpace">The pointer to the base of the space about to be constructed</param>
template<class T>
static SlotInformationStackLocation PushStackLocation(T* pSpace) {
auto* pStump = &SlotInformationStump<T>::s_stump;

// If we're already initialized, then we have nothing to do. This line is an optimization; if there
// is a race here, then the worst-case scenario is an unneeded sequence of memory allocations that
// will only ever be referenced by this stack location.
if(pStump->bInitialized)
return SlotInformationStackLocation(pStump);

// New stack location to enclose this stump. This stack location may be concurrent with respect
// to other threads, but only one thread will succeed in colonizing this stump with a chain of
// slot entries
return SlotInformationStackLocation(pStump, pSpace, sizeof(T));
return SlotInformationStackLocation(
&SlotInformationStump<T>::s_stump,
pSpace,
sizeof(T)
);
}

/// <summary>
Expand All @@ -177,9 +181,13 @@ class SlotInformationStackLocation {
/// <summary>
/// Registers the named slot with the current stack location
/// </summary>
/// <remarks>
static void RegisterSlot(DeferrableAutowiring* pAutowiring);

/// <summary>
/// Registers a NewAutoFilter with this SlotInformation
/// </summary>
static void RegisterSlot(const AutoFilterDescriptorStub& stub);

// Operator overloads:
void operator=(SlotInformationStackLocation&& rhs) = delete;
void operator=(const SlotInformationStackLocation& rhs) = delete;
Expand Down
83 changes: 74 additions & 9 deletions autowiring/atomic_object.h
Expand Up @@ -42,7 +42,7 @@ class atomic_object {
/// atomic_object<object> target(*unlock_object<object>(source));
///</remarks>
atomic_object(const atomic_object<object>& source) {
std::lock_guard<lock> lock_this(source.m_lock);
std::lock_guard<lock> lock_source(source.m_lock);
m_object = source.m_object;
m_initialized = source.m_initialized;
}
Expand All @@ -64,10 +64,15 @@ class atomic_object {
atomic_object<object, lock>& operator = (const atomic_object<object>& source) {
if (this == &source)
return *this;
std::lock_guard<lock> lock_this(m_lock);
std::lock_guard<lock> locksource(source.m_lock);
//IMPORTANT: Aquisition of both locks must be atomic.
//The following code:
// m_initialized = source.initialized(m_object);
//could deadlock with its counterpart in source.
std::lock(m_lock, source.m_lock);
m_object = source.m_object;
m_initialized = source.m_initialized;
m_lock.unlock();
source.m_lock.unlock();
return *this;
}

Expand All @@ -89,29 +94,89 @@ class atomic_object {
return m_object;
}

///<summary>
///Reset using default constructor yielding initialized() == false.
///</summary>
///<return>True if the object was not assigned default values</return>
bool reset() {
std::lock_guard<lock> lock_this(m_lock);
bool was_initialized = m_initialized;
m_initialized = false;
m_object = object();
return was_initialized;
}

///<summary>
///Atomic copy of target to this object, only if initialized() == false.
///</summary>
///<return>True if the object was not assigned default values</return>
bool reset(const object& target) {
std::lock_guard<lock> lock_this(m_lock);
bool was_initialized = m_initialized;
if (!m_initialized)
m_object = target;
m_initialized = true;
return was_initialized;
}

///<return>True if the object was not assigned default values</return>
bool initialized() const {
std::lock_guard<lock> lock_this(m_lock);
return m_initialized;
}

///<summary>
///Atomic copy of this location to argument location, only if this has location.
///Atomic copy of this object to target, only if initialized() == true.
///</summary>
///<return>True if the object was not assigned default values</return>
bool initialized(object& target) {
bool initialized(object& target) const {
std::lock_guard<lock> lock_this(m_lock);
if (m_initialized)
target = m_object;
return m_initialized;
}

///<summary>
///Reset using default constructor yielding initialized() == false.
///If uninitialized uses target for initialization.
///If initialized assigns current value to target.
///</summary>
void reset() {
///<returns> Returns +1 for transfer from target to this, -1 for transfer from this to target</returns>
int transfer(object& target) {
std::lock_guard<lock> lock_this(m_lock);
m_initialized = false;
m_object = object();
int val = 0;
if (m_initialized) {
target = m_object;
val = +1;
} else {
m_object = target;
m_initialized = true;
val = -1;
}
return val;
}

///<summary>
///If neither this nor target are uninitialized, no transfer occurs.
///If this is uninitialized and target is not, then this is initialized by target.
///If target is uninitialized and this is, then target is initialized by this.
///If both this and target are initialized, no transfer occurs.
///</summary>
///<returns> Returns +1 for transfer from target to this, -1 for transfer from this to target, else 0</returns>
int transfer(atomic_object<object, lock>& target) {
std::lock(m_lock, target.m_lock);
int val = 0;
if (m_initialized && !target.m_initialized) {
target.m_object = m_object;
target.m_initialized = true;
val = -1;
}
if (!m_initialized && target.m_initialized) {
m_object = target.m_object;
m_initialized = true;
val = +1;
}
m_lock.unlock();
target.m_lock.unlock();
return val;
}
};
62 changes: 53 additions & 9 deletions autowiring/shared_object.h
Expand Up @@ -29,9 +29,15 @@ class shared_object {
object& get_object() {
return m_share->m_object;
}
const object& get_object() const {
return m_share->m_object;
}
bool& get_initialized() {
return m_share->m_initialized;
}
const bool& get_initialized() const {
return m_share->m_initialized;
}

public:
typedef unlock_object<object, lock> unlock;
Expand All @@ -48,7 +54,8 @@ class shared_object {
///<remarks>
///To copy the atomic_object referenced by source use:
/// shared_object<object> target(*unlock_object<object>(source));
///</remakrs>
///This method does NOT copy the atomic_object referenced by source.
///</remarks>
shared_object(const shared_object<object, lock>& source):
m_share(source.m_share) {}

Expand All @@ -65,6 +72,7 @@ class shared_object {
///<remarks>
///This method is equivalent to:
/// target = *unlock_object<object>(source);
///This method does NOT redirect the atomic_object reference to the reference of source.
///</remarks>
shared_object<object, lock>& operator = (const shared_object<object, lock>& source) {
*m_share = *source.m_share;
Expand All @@ -86,27 +94,63 @@ class shared_object {
return *m_share;
}

///<summary>
///Reset using default constructor yielding initialized() == false.
///</summary>
///<return>True if the object was not assigned default values</return>
bool reset() {
return m_share->reset();
}

///<return>True if the object was not assigned default values</return>
bool initialized() const {
return m_share->initialized();
}

///<summary>
///Atomic copy of this location to argument location, only if this has location.
///Atomic copy of target to this object, only if initialized() == false.
///</summary>
///<return>True if the object was not assigned default values</return>
bool reset(const object& target) {
return m_share->reset(target);
}

///<summary>
///Atomic copy of this object to target, only if initialized() == true.
///</summary>
///<return>True if the object was not assigned default values</return>
bool initialized(object& target) {
return m_share->initialized(target);
}

///<summary>
///Reset using default constructor yielding initialized() == false.
///If uninitialized uses target for initialization.
///If initialized assigns current value to target.
///</summary>
///<remarks>
///This method is equivalent to:
/// unlock_object<object>(source)->reset();
///</remarks>
void reset() {
m_share->reset();
///<returns> Returns +1 for transfer from target to this, -1 for transfer from this to target</returns>
int transfer(object& target) {
return m_share->transfer(target);
}

///<summary>
///If neither this nor target are uninitialized, no transfer occurs.
///If this is uninitialized and target is not, then this is initialized by target.
///If target is uninitialized and this is, then target is initialized by this.
///If both this and target are initialized, no transforer occurs.
///</summary>
///<returns> Returns +1 for transfer from target to this, -1 for transfer from this to target, else 0</returns>
int transfer(atomic_object<object, lock>& target) {
return m_share->transfer(target);
}

///<summary>
///If neither this nor target are uninitialized, no transfer occurs.
///If this is uninitialized and target is not, then this is initialized by target.
///If target is uninitialized and this is, then target is initialized by this.
///If both this and target are initialized, no transforer occurs.
///</summary>
///<returns> Returns +1 for transfer from target to this, -1 for transfer from this to target, else 0</returns>
int transfer(shared_object<object, lock>& target) {
return m_share->transfer(target.m_share);
}
};

0 comments on commit 83d4d09

Please sign in to comment.