From 166564995eabe473b7863b3ebf6c28492303a4eb Mon Sep 17 00:00:00 2001 From: Jason Lokerson Date: Thu, 25 Feb 2016 13:01:40 -0800 Subject: [PATCH] Rename auto_signal.h to signal.h The type included here is called signal, the header should have the same name. Also preserve the auto_signal header file for legacy customers, just have it punt over to the newly named file. --- src/autowiring/CMakeLists.txt | 3 +- src/autowiring/auto_signal.h | 496 +----------------- .../{auto_signal.cpp => signal.cpp} | 0 src/autowiring/signal.h | 496 ++++++++++++++++++ 4 files changed, 499 insertions(+), 496 deletions(-) rename src/autowiring/{auto_signal.cpp => signal.cpp} (100%) create mode 100644 src/autowiring/signal.h diff --git a/src/autowiring/CMakeLists.txt b/src/autowiring/CMakeLists.txt index f651a9791..5d81809bc 100644 --- a/src/autowiring/CMakeLists.txt +++ b/src/autowiring/CMakeLists.txt @@ -25,7 +25,6 @@ set(Autowiring_SRCS auto_out.h auto_prev.h auto_signal.h - auto_signal.cpp auto_tuple.h AutoCurrentPacketPusher.h AutoFilterDescriptor.h @@ -134,6 +133,8 @@ set(Autowiring_SRCS Parallel.cpp registration.h SatCounter.h + signal.h + signal.cpp signal_base.h SlotInformation.cpp SlotInformation.h diff --git a/src/autowiring/auto_signal.h b/src/autowiring/auto_signal.h index 67301b14e..7180c92ae 100644 --- a/src/autowiring/auto_signal.h +++ b/src/autowiring/auto_signal.h @@ -1,496 +1,2 @@ // Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. -#pragma once -#include "atomic_list.h" -#include "auto_tuple.h" -#include "autowiring_error.h" -#include "callable.h" -#include "Decompose.h" -#include "index_tuple.h" -#include "noop.h" -#include "registration.h" -#include "signal_base.h" -#include "spin_lock.h" -#include -#include -#include -#include -#include -#include -#include TYPE_TRAITS_HEADER - -/// -/// Implements an asynchronous signal concept as an AutoFired alternative -/// -namespace autowiring { - template - struct signal; - - namespace detail { - // Holds true if type T can be copied safely - template - struct can_copy { - static const bool value = - !std::is_abstract::value && - std::is_copy_constructible::value; - }; - - // Utility type for handling dereferencing of an input value - template - struct dereferencer - { - static_assert(std::is_copy_constructible::value, "T must be copy constructable"); - static_assert(!std::is_abstract::value, "T cannot be abstract"); - - dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} - dereferencer(T&& val) : val(std::move(val)) {} - T val; - const T& operator*(void) const { return val; } - }; - - template - struct dereferencer - { - dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} - dereferencer(T&& val) : val(std::move(val)) {} - T val; - const T& operator*(void) const { return val; } - }; - - /// - /// Copyable reference argument - /// - template - struct dereferencer::value>::type> { - dereferencer(const dereferencer& rhs) : val(rhs.val) {} - dereferencer(const T& val) : val(val) {} - const T& val; - const T& operator*(void) const { return val; } - }; - - /// - /// Non-copyable reference argument - /// - template - struct dereferencer::value>::type> { - dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} - dereferencer(const T& val) : val(val) {} - T val; - const T& operator*(void) const { return val; } - }; - } - - // Current state of the signal, used as a type of lock. - enum class SignalState { - // Signal is totally idle, this is the default state - Free, - - // Someone is presently inserting or removing a listener - Updating, - - // Signal is being asserted in some thread - Asserting, - - // Signal is being asserted, and there is deferred work to be done - Deferred, - }; - - /// - /// A signal registration entry, for use as an embedded member variable of a context member. - /// - template - struct signal: - signal_base - { - public: - signal(void) {} - signal(const signal&) = delete; - - signal(signal&& rhs) : - m_pFirstListener(rhs.m_pFirstListener), - m_pLastListener(rhs.m_pLastListener) - { - if (!rhs.m_delayedCalls.empty()) - throw autowiring_error("Attempted to move a signal where pended lambdas still exist"); - rhs.m_pFirstListener = nullptr; - rhs.m_pLastListener = nullptr; - } - - ~signal(void) { - entry_base* prior = nullptr; - for (entry_base* cur = m_pFirstListener; cur; cur = cur->pFlink) { - delete prior; - prior = cur; - } - } - - signal& operator=(signal&& rhs) { - std::swap(m_pFirstListener, rhs.m_pFirstListener); - std::swap(m_pLastListener, rhs.m_pLastListener); - return *this; - } - - private: - mutable std::atomic m_state{ SignalState::Free }; - - struct entry_base; - - // Doubly linked list of all of our listeners - entry_base* volatile m_pFirstListener = nullptr; - entry_base* volatile m_pLastListener = nullptr; - - // Calls that had to be delayed due to asynchronous issues - mutable atomic_list m_delayedCalls; - - // Base type for listeners attached to this signal - struct entry_base { - virtual ~entry_base(void) {} - virtual void operator()(const Args&... args) = 0; - - entry_base* pFlink = nullptr; - entry_base* pBlink = nullptr; - }; - - template - struct entry: - entry_base - { - static_assert(!std::is_reference::value, "Cannot construct a reference binding"); - - template - entry(signal&, _Fn fn) : fn(std::forward<_Fn>(fn)) {} - Fn fn; - void operator()(const Args&... args) override { fn(args...); } - }; - - template - struct entry_reflexive : - entry_base - { - static_assert(!std::is_reference::value, "Cannot construct a reference binding"); - - template - entry_reflexive(signal& owner, _Fn fn) : - owner(owner), - fn(std::forward<_Fn>(fn)) - {} - signal& owner; - Fn fn; - void operator()(const Args&... args) override { fn(registration_t{ &owner, this }, args...); } - }; - - void LinkUnsafe(entry_base& e) { - // Standard, boring linked list insertion: - e.pBlink = m_pLastListener; - if (m_pLastListener) - m_pLastListener->pFlink = &e; - if (!m_pFirstListener) - m_pFirstListener = &e; - m_pLastListener = &e; - } - - struct callable_link : - callable_base - { - callable_link(signal& owner, entry_base* entry) : - owner(owner), - entry(std::move(entry)) - {} - - signal& owner; - entry_base* entry; - void operator()() override { - if(entry) - owner.LinkUnsafe(*entry); - } - }; - - void Link(entry_base* e) { - SignalState state = SignalState::Free; - - // We don't mind rare failures, here, because our algorithm is correct regardless - // of the return value of this comparison, it's just slightly more efficient if - // there is no failure. - if (!m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_relaxed, std::memory_order_relaxed)) { - // Control is contended, we need to hand off linkage responsibility to someone else. - auto link = new callable_link{ *this, e }; - uint32_t id = m_delayedCalls.push_entry(link); - - for (;;) { - // Try to transition from Asserting to the Deferred state first, if we succeed here - // then the call operator will take responsibility for our insertion request. - state = SignalState::Asserting; - if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) - return; - - // If we observed the state as being Deferred, then we can also short-circuit - if (state == SignalState::Deferred) - return; - - // Next try to transition from Free to Updating. - state = SignalState::Free; - if (m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_acquire, std::memory_order_relaxed)) { - if (m_delayedCalls.chain_id() != id) { - // Dispatcher already got to this one, we don't need to do anything - m_state = SignalState::Free; - return; - } - - // Success, cancel the operation and exit here. We can't delete this link because - // it has already been submitted to the queue, but calling it has no effect and it - // will be cleaned up later. - link->entry = nullptr; - break; - } - } - } - - // Link and go straight to the Free state. Updating is exclusive. - LinkUnsafe(*e); - m_state = SignalState::Free; - } - - void UnlinkUnsafe(const entry_base& entry) { - if (m_pFirstListener == &entry) { - m_pFirstListener = m_pFirstListener->pFlink; - if (m_pFirstListener) - m_pFirstListener->pBlink = nullptr; - } - if (m_pLastListener == &entry) { - m_pLastListener = m_pLastListener->pBlink; - if (m_pLastListener) - m_pLastListener->pFlink = nullptr; - } - - // Neighbor unlink: - if (entry.pFlink) - entry.pFlink->pBlink = entry.pBlink; - if (entry.pBlink) - entry.pBlink->pFlink = entry.pFlink; - } - - struct callable_unlink : - callable_base - { - callable_unlink(signal& owner, std::unique_ptr&& entry) : - owner(owner), - entry(std::move(entry)) - {} - - signal& owner; - std::unique_ptr entry; - void operator()() override { - if(entry) - owner.UnlinkUnsafe(*entry); - } - }; - - /// - /// Removes the specified entry from the set of listeners - /// - bool Unlink(std::unique_ptr e) { - // See discussion in Link - SignalState state = SignalState::Free; - if (!m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_relaxed, std::memory_order_relaxed)) { - auto link = new callable_unlink{ *this, std::move(e) }; - uint32_t chainID = m_delayedCalls.push_entry(link); - - for (;;) { - state = SignalState::Asserting; - if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) - return false; - if (state == SignalState::Deferred) - return false; - - state = SignalState::Free; - if (m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_acquire, std::memory_order_relaxed)) { - if (chainID != m_delayedCalls.chain_id()) { - // Dispatcher got here. We weren't the party responsible for removing - // this entry, but we can guarantee the postcondition, so we can return - // true. - m_state = SignalState::Free; - return true; - } - e = std::move(link->entry); - break; - } - } - } - UnlinkUnsafe(*e); - m_state = SignalState::Free; - return true; - } - - /// - /// Sequential signaling mechanism, invoked under the call lock - /// - void SignalUnsafe(Args... args) const { - for (auto cur = m_pFirstListener; cur; cur = cur->pFlink) - (*cur)(args...); - } - - template - struct callable_signal : - callable_base - { - callable_signal(const signal& owner, FnArgs&&... args) : - owner(owner), - args(std::forward(args)...) - {} - - const signal& owner; - autowiring::tuple...> args; - - template - void call(index_tuple) { - owner.SignalUnsafe(*autowiring::get(args)...); - } - - void operator()() override { - call(typename make_index_tuple::type{}); - } - }; - - public: - bool is_executing(void) const override { - switch (m_state.load()) { - case SignalState::Free: - case SignalState::Updating: - return false; - case SignalState::Asserting: - case SignalState::Deferred: - return true; - } - throw std::runtime_error("Invalid state defined on signal type"); - } - - /// - /// Attaches the specified handler to this signal - /// - /// - /// If the return value is not captured, the signal cannot be unregistered. Users are not required - /// to free this object. - /// - template - registration_t operator+=(Fn fn) { - typedef typename std::decay::type FnDecay; - typedef typename std::conditional< - Decompose::N == sizeof...(Args), - entry, - entry_reflexive - >::type EntryType; - - auto* e = new EntryType(*this, std::forward(fn)); - Link(e); - return{ this, e }; - } - - /// - /// Unregisters the specified registration object and clears its status - /// - /// - /// This method does not guarantee that the named registration object will not be called after - /// this method returns in multithreaded cases. The handler is only guaranteed not to be called - /// if `rhs.unique()` is true. - /// - bool operator-=(registration_t& rhs) override { - if (rhs.owner != this) - throw autowiring_error("Attempted to unlink a registration on an unrelated signal"); - if (!rhs.pobj) - return true; - - auto retVal = Unlink(std::unique_ptr{ static_cast(rhs.pobj) }); - rhs.pobj = nullptr; - return retVal; - } - - /// - /// Raises the signal and invokes all attached handlers - /// - /// - template - void operator()(FnArgs&&... args) const { - for (;;) { - SignalState state = SignalState::Free; - if (m_state.compare_exchange_weak(state, SignalState::Asserting, std::memory_order_acquire, std::memory_order_relaxed)) { - // Can safely make this call under state because there are never any blocking - // operations involving this lock. We cannot let exceptions propagate out of - // here because we might not actually be able to give up control. - try { - SignalUnsafe(std::forward(args)...); - } catch(...) {} - break; - } - - switch (state) { - case SignalState::Free: - case SignalState::Updating: - // Spurious failure, or insertion. - // We cannot delegate control to insertion, and spurious failure should be retried. - continue; - case SignalState::Asserting: - case SignalState::Deferred: - // Some other thread is already asserting this signal, doing work, we need to ensure - // that thread takes responsibility for this call. - break; - } - - // We failed to obtain control. Create a thunk here. - m_delayedCalls.push_entry( - new callable_signal{ - *this, - std::forward(args)... - } - ); - - // Now ensure that someone is going to take responsibility to make this invocation. We do - // this by trying to transition the Asserting state to Deferred before the currently - // asserting thread manages to transition from Asserting to Free. This is an implicit - // race condition, whoever wins the race gets to return control to the caller. - do { - state = SignalState::Asserting; - if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) - // Success, we have transferred responsibility for this call to the other entity. - return; - - if (state == SignalState::Deferred) - // State is already deferred, maybe by someone else. We can return control directly. - return; - - state = SignalState::Free; - } while (!m_state.compare_exchange_weak(state, SignalState::Asserting, std::memory_order_acquire, std::memory_order_relaxed)); - - // Great. If we got here it's because we were able to transition from Free to Asserting, which - // means that everyone else has already returned to their callers. We are the only ones still - // in operator(). We are going to have to call the callable_signal that we just got finished - // pending. - break; - } - - // Try to get out of the asserting state and into the free state - for( - SignalState state = SignalState::Asserting; - !m_state.compare_exchange_weak(state, SignalState::Free, std::memory_order_release, std::memory_order_relaxed); - state = SignalState::Asserting - ) { - // Failed to give up control. Our state was deferred or the delayed call list was non-empty, - // we need to transition to the Asserting state and empty our list before proceeding - state = SignalState::Deferred; - if (!m_state.compare_exchange_weak(state, SignalState::Asserting)) - // Circle around, maybe we had a spurious failure - continue; - - // Call all dispatchers in the right order - for (auto& cur : m_delayedCalls.release()) { - // Eat any exception that occurs - try { cur(); } - catch (...) {} - } - } - } - - // This overload is provided so that statement completion makes sense. Because of the - // template format of the earlier function call overload, overload resolution will never - // select this variant. - //void operator()(Args... args) const; - }; -} +#include "signal.h" diff --git a/src/autowiring/auto_signal.cpp b/src/autowiring/signal.cpp similarity index 100% rename from src/autowiring/auto_signal.cpp rename to src/autowiring/signal.cpp diff --git a/src/autowiring/signal.h b/src/autowiring/signal.h new file mode 100644 index 000000000..67301b14e --- /dev/null +++ b/src/autowiring/signal.h @@ -0,0 +1,496 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include "atomic_list.h" +#include "auto_tuple.h" +#include "autowiring_error.h" +#include "callable.h" +#include "Decompose.h" +#include "index_tuple.h" +#include "noop.h" +#include "registration.h" +#include "signal_base.h" +#include "spin_lock.h" +#include +#include +#include +#include +#include +#include +#include TYPE_TRAITS_HEADER + +/// +/// Implements an asynchronous signal concept as an AutoFired alternative +/// +namespace autowiring { + template + struct signal; + + namespace detail { + // Holds true if type T can be copied safely + template + struct can_copy { + static const bool value = + !std::is_abstract::value && + std::is_copy_constructible::value; + }; + + // Utility type for handling dereferencing of an input value + template + struct dereferencer + { + static_assert(std::is_copy_constructible::value, "T must be copy constructable"); + static_assert(!std::is_abstract::value, "T cannot be abstract"); + + dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} + dereferencer(T&& val) : val(std::move(val)) {} + T val; + const T& operator*(void) const { return val; } + }; + + template + struct dereferencer + { + dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} + dereferencer(T&& val) : val(std::move(val)) {} + T val; + const T& operator*(void) const { return val; } + }; + + /// + /// Copyable reference argument + /// + template + struct dereferencer::value>::type> { + dereferencer(const dereferencer& rhs) : val(rhs.val) {} + dereferencer(const T& val) : val(val) {} + const T& val; + const T& operator*(void) const { return val; } + }; + + /// + /// Non-copyable reference argument + /// + template + struct dereferencer::value>::type> { + dereferencer(dereferencer&& rhs) : val(std::move(rhs.val)) {} + dereferencer(const T& val) : val(val) {} + T val; + const T& operator*(void) const { return val; } + }; + } + + // Current state of the signal, used as a type of lock. + enum class SignalState { + // Signal is totally idle, this is the default state + Free, + + // Someone is presently inserting or removing a listener + Updating, + + // Signal is being asserted in some thread + Asserting, + + // Signal is being asserted, and there is deferred work to be done + Deferred, + }; + + /// + /// A signal registration entry, for use as an embedded member variable of a context member. + /// + template + struct signal: + signal_base + { + public: + signal(void) {} + signal(const signal&) = delete; + + signal(signal&& rhs) : + m_pFirstListener(rhs.m_pFirstListener), + m_pLastListener(rhs.m_pLastListener) + { + if (!rhs.m_delayedCalls.empty()) + throw autowiring_error("Attempted to move a signal where pended lambdas still exist"); + rhs.m_pFirstListener = nullptr; + rhs.m_pLastListener = nullptr; + } + + ~signal(void) { + entry_base* prior = nullptr; + for (entry_base* cur = m_pFirstListener; cur; cur = cur->pFlink) { + delete prior; + prior = cur; + } + } + + signal& operator=(signal&& rhs) { + std::swap(m_pFirstListener, rhs.m_pFirstListener); + std::swap(m_pLastListener, rhs.m_pLastListener); + return *this; + } + + private: + mutable std::atomic m_state{ SignalState::Free }; + + struct entry_base; + + // Doubly linked list of all of our listeners + entry_base* volatile m_pFirstListener = nullptr; + entry_base* volatile m_pLastListener = nullptr; + + // Calls that had to be delayed due to asynchronous issues + mutable atomic_list m_delayedCalls; + + // Base type for listeners attached to this signal + struct entry_base { + virtual ~entry_base(void) {} + virtual void operator()(const Args&... args) = 0; + + entry_base* pFlink = nullptr; + entry_base* pBlink = nullptr; + }; + + template + struct entry: + entry_base + { + static_assert(!std::is_reference::value, "Cannot construct a reference binding"); + + template + entry(signal&, _Fn fn) : fn(std::forward<_Fn>(fn)) {} + Fn fn; + void operator()(const Args&... args) override { fn(args...); } + }; + + template + struct entry_reflexive : + entry_base + { + static_assert(!std::is_reference::value, "Cannot construct a reference binding"); + + template + entry_reflexive(signal& owner, _Fn fn) : + owner(owner), + fn(std::forward<_Fn>(fn)) + {} + signal& owner; + Fn fn; + void operator()(const Args&... args) override { fn(registration_t{ &owner, this }, args...); } + }; + + void LinkUnsafe(entry_base& e) { + // Standard, boring linked list insertion: + e.pBlink = m_pLastListener; + if (m_pLastListener) + m_pLastListener->pFlink = &e; + if (!m_pFirstListener) + m_pFirstListener = &e; + m_pLastListener = &e; + } + + struct callable_link : + callable_base + { + callable_link(signal& owner, entry_base* entry) : + owner(owner), + entry(std::move(entry)) + {} + + signal& owner; + entry_base* entry; + void operator()() override { + if(entry) + owner.LinkUnsafe(*entry); + } + }; + + void Link(entry_base* e) { + SignalState state = SignalState::Free; + + // We don't mind rare failures, here, because our algorithm is correct regardless + // of the return value of this comparison, it's just slightly more efficient if + // there is no failure. + if (!m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_relaxed, std::memory_order_relaxed)) { + // Control is contended, we need to hand off linkage responsibility to someone else. + auto link = new callable_link{ *this, e }; + uint32_t id = m_delayedCalls.push_entry(link); + + for (;;) { + // Try to transition from Asserting to the Deferred state first, if we succeed here + // then the call operator will take responsibility for our insertion request. + state = SignalState::Asserting; + if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) + return; + + // If we observed the state as being Deferred, then we can also short-circuit + if (state == SignalState::Deferred) + return; + + // Next try to transition from Free to Updating. + state = SignalState::Free; + if (m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_acquire, std::memory_order_relaxed)) { + if (m_delayedCalls.chain_id() != id) { + // Dispatcher already got to this one, we don't need to do anything + m_state = SignalState::Free; + return; + } + + // Success, cancel the operation and exit here. We can't delete this link because + // it has already been submitted to the queue, but calling it has no effect and it + // will be cleaned up later. + link->entry = nullptr; + break; + } + } + } + + // Link and go straight to the Free state. Updating is exclusive. + LinkUnsafe(*e); + m_state = SignalState::Free; + } + + void UnlinkUnsafe(const entry_base& entry) { + if (m_pFirstListener == &entry) { + m_pFirstListener = m_pFirstListener->pFlink; + if (m_pFirstListener) + m_pFirstListener->pBlink = nullptr; + } + if (m_pLastListener == &entry) { + m_pLastListener = m_pLastListener->pBlink; + if (m_pLastListener) + m_pLastListener->pFlink = nullptr; + } + + // Neighbor unlink: + if (entry.pFlink) + entry.pFlink->pBlink = entry.pBlink; + if (entry.pBlink) + entry.pBlink->pFlink = entry.pFlink; + } + + struct callable_unlink : + callable_base + { + callable_unlink(signal& owner, std::unique_ptr&& entry) : + owner(owner), + entry(std::move(entry)) + {} + + signal& owner; + std::unique_ptr entry; + void operator()() override { + if(entry) + owner.UnlinkUnsafe(*entry); + } + }; + + /// + /// Removes the specified entry from the set of listeners + /// + bool Unlink(std::unique_ptr e) { + // See discussion in Link + SignalState state = SignalState::Free; + if (!m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_relaxed, std::memory_order_relaxed)) { + auto link = new callable_unlink{ *this, std::move(e) }; + uint32_t chainID = m_delayedCalls.push_entry(link); + + for (;;) { + state = SignalState::Asserting; + if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) + return false; + if (state == SignalState::Deferred) + return false; + + state = SignalState::Free; + if (m_state.compare_exchange_weak(state, SignalState::Updating, std::memory_order_acquire, std::memory_order_relaxed)) { + if (chainID != m_delayedCalls.chain_id()) { + // Dispatcher got here. We weren't the party responsible for removing + // this entry, but we can guarantee the postcondition, so we can return + // true. + m_state = SignalState::Free; + return true; + } + e = std::move(link->entry); + break; + } + } + } + UnlinkUnsafe(*e); + m_state = SignalState::Free; + return true; + } + + /// + /// Sequential signaling mechanism, invoked under the call lock + /// + void SignalUnsafe(Args... args) const { + for (auto cur = m_pFirstListener; cur; cur = cur->pFlink) + (*cur)(args...); + } + + template + struct callable_signal : + callable_base + { + callable_signal(const signal& owner, FnArgs&&... args) : + owner(owner), + args(std::forward(args)...) + {} + + const signal& owner; + autowiring::tuple...> args; + + template + void call(index_tuple) { + owner.SignalUnsafe(*autowiring::get(args)...); + } + + void operator()() override { + call(typename make_index_tuple::type{}); + } + }; + + public: + bool is_executing(void) const override { + switch (m_state.load()) { + case SignalState::Free: + case SignalState::Updating: + return false; + case SignalState::Asserting: + case SignalState::Deferred: + return true; + } + throw std::runtime_error("Invalid state defined on signal type"); + } + + /// + /// Attaches the specified handler to this signal + /// + /// + /// If the return value is not captured, the signal cannot be unregistered. Users are not required + /// to free this object. + /// + template + registration_t operator+=(Fn fn) { + typedef typename std::decay::type FnDecay; + typedef typename std::conditional< + Decompose::N == sizeof...(Args), + entry, + entry_reflexive + >::type EntryType; + + auto* e = new EntryType(*this, std::forward(fn)); + Link(e); + return{ this, e }; + } + + /// + /// Unregisters the specified registration object and clears its status + /// + /// + /// This method does not guarantee that the named registration object will not be called after + /// this method returns in multithreaded cases. The handler is only guaranteed not to be called + /// if `rhs.unique()` is true. + /// + bool operator-=(registration_t& rhs) override { + if (rhs.owner != this) + throw autowiring_error("Attempted to unlink a registration on an unrelated signal"); + if (!rhs.pobj) + return true; + + auto retVal = Unlink(std::unique_ptr{ static_cast(rhs.pobj) }); + rhs.pobj = nullptr; + return retVal; + } + + /// + /// Raises the signal and invokes all attached handlers + /// + /// + template + void operator()(FnArgs&&... args) const { + for (;;) { + SignalState state = SignalState::Free; + if (m_state.compare_exchange_weak(state, SignalState::Asserting, std::memory_order_acquire, std::memory_order_relaxed)) { + // Can safely make this call under state because there are never any blocking + // operations involving this lock. We cannot let exceptions propagate out of + // here because we might not actually be able to give up control. + try { + SignalUnsafe(std::forward(args)...); + } catch(...) {} + break; + } + + switch (state) { + case SignalState::Free: + case SignalState::Updating: + // Spurious failure, or insertion. + // We cannot delegate control to insertion, and spurious failure should be retried. + continue; + case SignalState::Asserting: + case SignalState::Deferred: + // Some other thread is already asserting this signal, doing work, we need to ensure + // that thread takes responsibility for this call. + break; + } + + // We failed to obtain control. Create a thunk here. + m_delayedCalls.push_entry( + new callable_signal{ + *this, + std::forward(args)... + } + ); + + // Now ensure that someone is going to take responsibility to make this invocation. We do + // this by trying to transition the Asserting state to Deferred before the currently + // asserting thread manages to transition from Asserting to Free. This is an implicit + // race condition, whoever wins the race gets to return control to the caller. + do { + state = SignalState::Asserting; + if (m_state.compare_exchange_weak(state, SignalState::Deferred, std::memory_order_relaxed, std::memory_order_relaxed)) + // Success, we have transferred responsibility for this call to the other entity. + return; + + if (state == SignalState::Deferred) + // State is already deferred, maybe by someone else. We can return control directly. + return; + + state = SignalState::Free; + } while (!m_state.compare_exchange_weak(state, SignalState::Asserting, std::memory_order_acquire, std::memory_order_relaxed)); + + // Great. If we got here it's because we were able to transition from Free to Asserting, which + // means that everyone else has already returned to their callers. We are the only ones still + // in operator(). We are going to have to call the callable_signal that we just got finished + // pending. + break; + } + + // Try to get out of the asserting state and into the free state + for( + SignalState state = SignalState::Asserting; + !m_state.compare_exchange_weak(state, SignalState::Free, std::memory_order_release, std::memory_order_relaxed); + state = SignalState::Asserting + ) { + // Failed to give up control. Our state was deferred or the delayed call list was non-empty, + // we need to transition to the Asserting state and empty our list before proceeding + state = SignalState::Deferred; + if (!m_state.compare_exchange_weak(state, SignalState::Asserting)) + // Circle around, maybe we had a spurious failure + continue; + + // Call all dispatchers in the right order + for (auto& cur : m_delayedCalls.release()) { + // Eat any exception that occurs + try { cur(); } + catch (...) {} + } + } + } + + // This overload is provided so that statement completion makes sense. Because of the + // template format of the earlier function call overload, overload resolution will never + // select this variant. + //void operator()(Args... args) const; + }; +}