// bdlmt_signaler.h                                                   -*-C++-*-
#ifndef INCLUDED_BDLMT_SIGNALER
#define INCLUDED_BDLMT_SIGNALER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide an implementation of a managed signals and slots system.
//
//@CLASSES:
//  bdlmt::Signaler:                a signaler
//  bdlmt::SignalerConnection:      signaler/slot connection
//  bdlmt::SignalerConnectionGuard: RAII signaler/slot connection
//
//@DESCRIPTION: This component provides the template class
// `bdlmt::Signaler<t_PROT>`, an implementation of a managed signal and slots
// system for the void returning function prototype `t_PROT`.  Each signaler
// represents a callback with multiple targets (called "slots") which are
// invoked in a known order when the signaler is invoked (called being
// "emitted").
//
// A slot being connected to a signaler is represented by a
// `bdlmt::SignalerConnection` which can be used to disconnect that connection
// at any time, but can also be discarded if managing the lifetime of the
// individual connection is not needed.  A guard to disconnect a slot on its
// destruction is available in `bdlmt::SignalerConnectionGuard`.
//
// Signalers and the slots connected to them are all managed.  Any connections
// will be automatically disconnected when a `bdlmt::Signaler` is destroyed, or
// when explicitly disconnected, and all internally allocated resources will be
// destroyed when no more references to them remain.  This enables the user to
// make signaler/slot connections and emit signals without expanding effort on
// managing the lifetimes of any of the involved objects.
//
///Slot Object Requirements
///------------------------
// Slots connected to a signaler `bdlmt::Signaler<t_PROT>` must be callable and
// copyable objects that may be passed to the constructor of
// `bsl::function<t_PROT>`.  I.e. a slot must be callable with the same
// arguments as `t_PROT`, and if a slot returns a value it will be discarded.
//
///Call Groups
///-----------
// Slots are free to have side effects, which means that some slots may have to
// be called before others even if they are not connected in that order.
// `bdlmt::Signaler` allows slots to be placed into groups that are ordered in
// some way.  Group values are integers, and are ordered by the `integer <`
// relation.  By default, all connected slots have the group value set to 0.
//
///Concurrency and Order of Execution
///----------------------------------
// Within a single thread of execution slots are always executed in the order
// defined by their respective groups and, within groups, by the order they
// were connected to the signaler.  If the signaler's call operator is invoked
// concurrently from multiple threads, slots may also be executed concurrently.
//
///Slots Lifetime
///--------------
// Internally, `bdlmt::Signaler` stores copies of connected slot objects.  The
// copy of the slot object is destroyed after the slot is disconnected from the
// signaler, or after the signaler is destroyed, but the exact moment is
// unspecified.  It is only guaranteed that the lifetime of such object will
// not exceed the collective lifetime of the signaler and all connection
// objects associated with to that signaler.
//
///Comparison of `SignalerConnection`s
///-----------------------------------
// Ordering comparisons of `bdlmt::SignalerConnection`s are transitive and are
// provided to facilitate their being stored in an associative container.  The
// ordering of a `bdlmt::SignalerConnection` does not change when it is
// disconnected.
//
// In equality comparisons, two default constructed connections compare
// equivalent and a default constructed connection is never equivalent to a
// connection to a slot.  If a connection is not default constructed, it is
// equivalent only to another connection that refers to the same slot.
//
///Thread Safety
///-------------
// `bdlmt::Signaler` is **fully thread-safe**, meaning that multiple threads
// may use their own instances of the class or use a shared instance without
// further synchronization.
//
// With the exception of assignment operators, `swap()`, `reset()` and
// `release()` member functions, `bdlmt::SignalerConnection` and
// `bdlmt::SignalerConnectionGuard` are thread-safe, meaning that multiple
// threads may use their own instances of the class or use a shared instance
// without further synchronization.
//
// It is safe to access or modify two distinct connection objects
// simultaneously, each from a separate thread, even if they represent the same
// slot connection.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usege
/// - - - - - - - - - - -
// Suppose we want to implement a GUI button class that allows users to
// keep track of its `press` events.
//
// First, we declare the `class`:
// ```
// /// A pretend GUI button.
// class Button {
//
//     // DATA
//     int d_numPresses;
//
//   public:
//     // TYPES
//
//     /// Slot argument is the number of times the button has been pressed.
//     typedef bsl::function<void(int)> OnPressSlotType;
//
//   private:
//     // PRIVATE DATA
//
//     /// Signaler argument is the number of times the button has been
//     /// pressed.
//     bdlmt::Signaler<void(int)> d_onPress;
//
//   public:
//     // CREATORS
//
//     /// Construct a `Button` object.
//     Button();
//
//     // MANIPULATORS
//
//     /// Connect the specified `slot` to this button.
//     bdlmt::SignalerConnection onPressConnect(const OnPressSlotType& slot);
//
//     /// Simulate user pressing on GUI button.
//     void press();
// };
// ```
// Then, we define its methods:
// ```
// // CREATORS
// Button::Button()
// : d_numPresses(0)
// {
// }
//
// // MANIPULATORS
// bdlmt::SignalerConnection Button::onPressConnect(
//                                                const OnPressSlotType& slot)
// {
//     return d_onPress.connect(slot);
// }
//
// void Button::press()
// {
//     d_onPress(++d_numPresses);
// }
// ```
// Next, we provide an event handler callback printing its argument, which the
// class will pass the number of times the button has been pressed:
// ```
// void showPresses(int numPresses)
// {
//     bsl::cout << "Button pressed " << numPresses << " times.\n";
// }
// ```
// Then, in `main`, create a button and subscribe to its events.
// ```
// u::Button                 button;
// bdlmt::SignalerConnection connection = button.onPressConnect(
//                                                           &u::showPresses);
// ```
// Next the button is "pressed", we will receive a notification.
// ```
// button.press();
// ```
// Now, we see the following message:
// ```
// Button pressed 1 times.
// ```
// Finally, unsubscribe from button's events when we don't want to receive
// notifications anymore.  (If we didn't call `disconnect`, `button` would
// clean up all the allocated resources when it went out of scope):
// ```
// connection.disconnect();
// ```

#include <bdlscm_version.h>
#include <bdlcc_skiplist.h>

#include <bslma_default.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_allocatorargt.h>
#include <bslmf_forwardingtype.h>
#include <bslmf_functionpointertraits.h>
#include <bslmf_integralconstant.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_typelist.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bslmt_readerwritermutex.h>
#include <bslmt_readlockguard.h>
#include <bslmt_writelockguard.h>

#include <bsls_annotation.h>
#include <bsls_assert.h>
#include <bsls_atomic.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_types.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <bsl_cstddef.h>      // 'bsl::size_t'
#include <bsl_functional.h>
#include <bsl_memory.h>
#include <bsl_utility.h>      // 'bsl::pair'

#include <bslma_allocator.h>

namespace BloombergLP {

// FORWARD DECLARATION

namespace bdlmt {

template <class>
class Signaler_Node;
class SignalerConnection;

                             // ==================
                             // struct Signaler_NA
                             // ==================

/// Provides a "Not an Argument" tag type.
struct Signaler_NotArg {
};

                        // ===========================
                        // class Signaler_ArgumentType
                        // ===========================

/// For a function prototype `t_PROT` of up to 9 arguments, provide types
/// `ForwardingTypeN` which is the most convenient way to forward the `Nth`
/// argument.
/// * as the type of argument N itself (in the case of some fundamental types)
/// * as a const reference (if `TypeN` is large and either by value or by
///   const reference), or
/// * as a reference to a modifiable object, if that's how the argument was
///   passed in the first place.
/// Note that nothing is passed as an rvalue reference, since if there are
/// multiple slots (usually the case), the argument will be moved from by the
/// first one and then unsuitable to be used by the ones following.
///
/// Also provide `ForwardingNotArg` the type that forwards `Signaler_NotArg`.
template <class t_PROT>
struct Signaler_ArgumentType {

  private:
    // PRIVATE TYPES
    typedef typename bslmf::FunctionPointerTraits<t_PROT *>::ArgumentList Args;

    template <int t_NUM>
    struct Forward {
        // PUBLIC TYPES
        typedef typename bslmf::TypeListTypeOf<t_NUM, Args, Signaler_NotArg>::
                                                         TypeOrDefault ArgType;

        typedef typename bslmf::ForwardingType<ArgType>::Type Type;
    };

  public:
    // PUBLIC TYPES
    typedef typename bslmf::ForwardingType<Signaler_NotArg>::Type
                                         ForwardingNotArg;

    typedef typename Forward<1>::Type    ForwardingType1;
    typedef typename Forward<2>::Type    ForwardingType2;
    typedef typename Forward<3>::Type    ForwardingType3;
    typedef typename Forward<4>::Type    ForwardingType4;
    typedef typename Forward<5>::Type    ForwardingType5;
    typedef typename Forward<6>::Type    ForwardingType6;
    typedef typename Forward<7>::Type    ForwardingType7;
    typedef typename Forward<8>::Type    ForwardingType8;
    typedef typename Forward<9>::Type    ForwardingType9;
};

                          // =========================
                          // struct Signaler_Invocable
                          // =========================

/// Provides a call operator for the derived class `bdlmt::Signaler`, such that
/// its call signature is identical to that of `t_PROT`.
template <class t_SIGNALER, class t_PROT>
struct Signaler_Invocable {
};

template <class t_SIGNALER>
struct Signaler_Invocable<t_SIGNALER, void()> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()() const;
};

template <class t_SIGNALER, class t_ARG1>
struct Signaler_Invocable<t_SIGNALER, void(t_ARG1)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1) const;
};

template <class t_SIGNALER, class t_ARG1, class t_ARG2>
struct Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1, t_ARG2) const;
};

template <class t_SIGNALER, class t_ARG1, class t_ARG2, class t_ARG3>
struct Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2, t_ARG3)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1, t_ARG2, t_ARG3) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4>
struct Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2, t_ARG3, t_ARG4)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4,
          class t_ARG5>
struct Signaler_Invocable<t_SIGNALER,
                          void(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4,
          class t_ARG5,
          class t_ARG6>
struct Signaler_Invocable<
    t_SIGNALER,
    void(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4,
          class t_ARG5,
          class t_ARG6,
          class t_ARG7>
struct Signaler_Invocable<
    t_SIGNALER,
    void(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6, t_ARG7)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1,
                    t_ARG2,
                    t_ARG3,
                    t_ARG4,
                    t_ARG5,
                    t_ARG6,
                    t_ARG7) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4,
          class t_ARG5,
          class t_ARG6,
          class t_ARG7,
          class t_ARG8>
struct Signaler_Invocable<
    t_SIGNALER,
    void(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6, t_ARG7, t_ARG8)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1,
                    t_ARG2,
                    t_ARG3,
                    t_ARG4,
                    t_ARG5,
                    t_ARG6,
                    t_ARG7,
                    t_ARG8) const;
};

template <class t_SIGNALER,
          class t_ARG1,
          class t_ARG2,
          class t_ARG3,
          class t_ARG4,
          class t_ARG5,
          class t_ARG6,
          class t_ARG7,
          class t_ARG8,
          class t_ARG9>
struct Signaler_Invocable<t_SIGNALER,
                          void(t_ARG1,
                               t_ARG2,
                               t_ARG3,
                               t_ARG4,
                               t_ARG5,
                               t_ARG6,
                               t_ARG7,
                               t_ARG8,
                               t_ARG9)> {
    // ACCESSOR

    /// Call the functions held in all slot holders, in the order of group
    /// numbers and with the ordering within one group being the order in which
    /// connections were made, passing the number and type of arguments passed
    /// to this function.
    void operator()(t_ARG1,
                    t_ARG2,
                    t_ARG3,
                    t_ARG4,
                    t_ARG5,
                    t_ARG6,
                    t_ARG7,
                    t_ARG8,
                    t_ARG9) const;
};

                       // ============================
                       // class Signaler_SlotNode_Base
                       // ============================

/// Provide a non-template protocol base class for `Signaler_SlotNode` so
/// `SignalerConnection` objects, which are not templated, can refer to and
/// manipulate `Signaler_SlotNode` objects.
class Signaler_SlotNode_Base {

  protected:
    // PROTECTED CREATORS

    /// Virtual d'tor.
    virtual ~Signaler_SlotNode_Base();

  public:
    // MANIPULATORS

    /// Disconnect this slot and return without waiting.  If the slot was
    /// already disconnected, this function has no effect.  Throws nothing.
    /// Note that it is guaranteed that this slot will not be called by a
    /// signal on the same signaler that begins after this function completes.
    virtual void disconnect() BSLS_KEYWORD_NOEXCEPT = 0;

    /// Disconnect this slot and block the calling thread pending the
    /// completion of signals being emitted on the signaler by any other
    /// threads.  If the slot was already disconnected, this function has no
    /// effect on the slot.  Throws nothing.  The behavior is undefined if this
    /// function is called from a slot on the same signaler.  Note that it is
    /// guaranteed that this slot will not be called by a signal on the same
    /// signaler that begins after this function completes, whether `wait` is
    /// `true` or not.
    virtual void disconnectAndWait() BSLS_KEYWORD_NOEXCEPT = 0;

    // ACCESSOR

    /// Return `true` if this slot is connected to its associated signaler, and
    /// `false` otherwise.
    virtual bool isConnected() const = 0;
};

                            // =======================
                            // class Signaler_SlotNode
                            // =======================

/// Dynamically-allocated container for one slot, containing a function object
/// that can be called by a signaler.  Owned by a shared pointer in a skip list
/// container in the `Signaler_Node`.  Also referred to by weak pointers from
/// `SignalerConnection` objects.
template <class t_PROT>
class Signaler_SlotNode : public Signaler_SlotNode_Base {

  private:
    // PRIVATE TYPES
    typedef Signaler_ArgumentType<t_PROT>               ArgumentType;
    typedef typename ArgumentType::ForwardingNotArg     ForwardingNotArg;
    typedef Signaler_Node<t_PROT>                       SignalerNode;

  public:
    // PUBLIC TYPE

    /// Defines a "key" used to index slots in an associative collection.  The
    /// first element of the pair is the slot call group; the second is the
    /// slot ID.
    typedef bsl::pair<int, unsigned> SlotMapKey;

  private:
    // PRIVATE DATA

    // Slot key containing the call group and the slot ID.  Used when notifying
    // the signaler about disconnection.
    SlotMapKey                  d_slotMapKey;

    // Set to `true` on construction, and to `false` on disconnection.  Used
    // for preventing calling a slot after it has been disconnected.
    bsls::AtomicBool            d_isConnected;

    // Weak reference to the associated signaler node.
    bsl::weak_ptr<SignalerNode> d_signalerNodePtr;

    // The target callback.
    bsl::function<t_PROT>         d_func;

  private:
    // NOT IMPLEMENTED
    Signaler_SlotNode(           const Signaler_SlotNode&)
                                                          BSLS_KEYWORD_DELETED;
    Signaler_SlotNode& operator=(const Signaler_SlotNode&)
                                                          BSLS_KEYWORD_DELETED;

  private:
    // PRIVATE ACCESSORS

    /// Dispatch function to be called by the `invoke` function, the first
    /// argument is an `integral_constant` containing the number of specified
    /// arguments `argN` that follow it.  Each function takes 9 arguments in
    /// addition to the integral constant, but the last ones of type
    /// `ForwardingNotArg` are not used.
    void doInvoke(bsl::integral_constant<int, 0>, // arguments count tag
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 1>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 2>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 3>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 4>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 5>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  typename ArgumentType::ForwardingType5 arg5,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 6>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  typename ArgumentType::ForwardingType5 arg5,
                  typename ArgumentType::ForwardingType6 arg6,
                  ForwardingNotArg,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 7>, // arguments count tag
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  typename ArgumentType::ForwardingType5 arg5,
                  typename ArgumentType::ForwardingType6 arg6,
                  typename ArgumentType::ForwardingType7 arg7,
                  ForwardingNotArg,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 8>,
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  typename ArgumentType::ForwardingType5 arg5,
                  typename ArgumentType::ForwardingType6 arg6,
                  typename ArgumentType::ForwardingType7 arg7,
                  typename ArgumentType::ForwardingType8 arg8,
                  ForwardingNotArg) const;
    void doInvoke(bsl::integral_constant<int, 9>,
                  typename ArgumentType::ForwardingType1 arg1,
                  typename ArgumentType::ForwardingType2 arg2,
                  typename ArgumentType::ForwardingType3 arg3,
                  typename ArgumentType::ForwardingType4 arg4,
                  typename ArgumentType::ForwardingType5 arg5,
                  typename ArgumentType::ForwardingType6 arg6,
                  typename ArgumentType::ForwardingType7 arg7,
                  typename ArgumentType::ForwardingType8 arg8,
                  typename ArgumentType::ForwardingType9 arg9) const;

  public:
    // CREATORS

    /// Create a `Signaler_SlotNode` object associated with signaler node at
    /// the specified `signalerNodePtr` using the specified `slotMapKey` and
    /// with the specified `slot` callable object.  Specify an `allocator` used
    /// to supply memory.
    template <class t_FUNC>
    Signaler_SlotNode(
                    const bsl::weak_ptr<SignalerNode>&         signalerNodePtr,
                    BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC)  slot,
                    SlotMapKey                                 slotMapKey,
                    bslma::Allocator                          *allocator);

    /// Destroy this object.
    //! ~Signaler_SlotNode() = default;

  public:
    // MANIPULATOR

    /// Disconnect this slot and return without waiting.  If the slot was
    /// already disconnected, this function has no effect.  Throws nothing.
    /// Note that it is guaranteed that this slot will not be called by a
    /// signal on the same signaler that begins after this function completes.
    void disconnect() BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_OVERRIDE;

    /// Disconnect this slot and block the calling thread pending the
    /// completion of signals being emitted on the signaler by any other
    /// threads.  If the slot was already disconnected, this function has no
    /// effect on the slot.  Throws nothing.  The behavior is undefined if this
    /// function is called from a slot on the same signaler.  Note that it is
    /// guaranteed that this slot will not be called by a signal on the same
    /// signaler that begins after this function completes, whether `wait` is
    /// `true` or not.
    void disconnectAndWait() BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_OVERRIDE;

    /// Notify this slot that is was disconnected from its associated signaler.
    /// Throws nothing.  After this function completes, `isConnected()` returns
    /// `false`.
    void notifyDisconnected() BSLS_KEYWORD_NOEXCEPT;

    // ACCESSORS

    /// Invoke the stored callback `c`, as if by `c(args...)`, where
    /// `args...` are the specified arguments `arg1`, `arg2`, `arg3`, etc.,
    /// except that the actual number of arguments passed to `c` is equal to
    /// the number of arguments for `t_PROT`.  If this slot is disconnected,
    /// this function has no effect.
    void invoke(typename ArgumentType::ForwardingType1 arg1,
                typename ArgumentType::ForwardingType2 arg2,
                typename ArgumentType::ForwardingType3 arg3,
                typename ArgumentType::ForwardingType4 arg4,
                typename ArgumentType::ForwardingType5 arg5,
                typename ArgumentType::ForwardingType6 arg6,
                typename ArgumentType::ForwardingType7 arg7,
                typename ArgumentType::ForwardingType8 arg8,
                typename ArgumentType::ForwardingType9 arg9) const;

    /// Return `true` if this slot is connected to its associated signaler, and
    /// `false` otherwise.
    bool isConnected() const BSLS_KEYWORD_OVERRIDE;
};

                            // ===================
                            // class Signaler_Node
                            // ===================

/// Provides the implementation of a signaler.  This object has a 1-1
/// relationship with the `Signaler`, which has a shared pointer to it.  This
/// allows other objects to refer to it via shared and weak pointers.  This
/// allows `SignalerConnection` objects to outlive the `Signaler -
/// Signaler_Node` pair, since they can test or lock weak pointers to see if
/// the `Signaler_Node` still exists when they are trying to disconnect
/// themselves.
template <class t_PROT>
class Signaler_Node
: public bsl::enable_shared_from_this<Signaler_Node<t_PROT> > {

  private:
    // PRIVATE TYPES
    typedef Signaler_SlotNode<t_PROT>                     SlotNode;
    typedef typename SlotNode::SlotMapKey                 SlotMapKey;
    typedef Signaler_ArgumentType<t_PROT>                 ArgumentType;

    typedef bdlcc::SkipList<SlotMapKey,                // [GROUP, ID] pair
                            bsl::shared_ptr<SlotNode> > KeyToSlotMap;
    typedef typename KeyToSlotMap::PairHandle           SlotPairHandle;

  private:
    // PRIVATE DATA

    // The purpose of this mutex is to implement the waiting behavior of
    // diconnects in `wait` mode.
    mutable bslmt::ReaderWriterMutex  d_signalerMutex;

    // Thread-safe collection containing slots indexed (and ordered) by their
    // respective keys.
    KeyToSlotMap                      d_slotMap;

    // For supplying `second` members of the `SlotMapKey` values that are
    // unique to a signaler.
    bsls::AtomicUint                  d_keyId;

  private:
    // NOT IMPLEMENTED
    Signaler_Node(           const Signaler_Node&) BSLS_KEYWORD_DELETED;
    Signaler_Node& operator=(const Signaler_Node&) BSLS_KEYWORD_DELETED;

  public:
    // CREATORS

    /// Create a `Signaler_Node` object having no connected slots.  Specify an
    /// `allocator` used to supply memory.  Note that the supplied allocator
    /// must remain valid until all connection objects associated with this
    /// signaler are destroyed.
    explicit
    Signaler_Node(bslma::Allocator *allocator);

  public:
    // MANIPULATORS

    /// Implements `Signaler::connect`.  Connect the specified `slot`, a
    /// callable object which must meet the `Slot Object Requirements`
    /// described in the component documentation, to this signaler.  Specify
    /// a `group` used to order slots upon invocation.  Return an instance
    /// of `SignalerConnection` representing the created connection.  This
    /// function meets the strong exception guarantee.  Note that the
    /// connected slot may be called by a signal emitted from another thread
    /// before this function completes.  Also note that it is unspecified
    /// whether connecting a slot while the signaler is emitting will result
    /// in the slot being called immediately.  Note that `t_FUNC` may have a
    /// return type other than `void`, but in that case, when the slot is
    /// called, the return value will be discarded.
    template <class t_FUNC>
    SignalerConnection connect(
                              BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) slot,
                              int                                       group);

    /// Implements `Signaler::disconnectAllSlots()`.  Disconnect all slots,
    /// if any, connected to this signaler.  Any signals emitted on the
    /// corresponding signaler that happen after this call to disconnect
    /// completes will not call any slots that were connected prior to this
    /// call.  Throws nothing.  Note that this function does not block the
    /// calling thread pending completion of ongoing signals emitted on the
    /// signaler.  Also note that it is unspecified how many slots, if any,
    /// will be called by any invocation on the signaler that begins before
    /// this function completes.  Also note that if a slot is connected to
    /// this signaler during a call to this function, it is unspecified
    /// whether that slot will be disconnected.
    void disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT;

    /// Implements `Signaler::disconnectAllSlotsAndWait`.  Disconnect all
    /// slots, if any, connected to this signaler.  Any signals emitted on
    /// the corresponding signaler that happens after this call to
    /// disconnect completes will not call any slots that were connected
    /// prior to this call.  This function blocks the calling thread pending
    /// completion of all ongoing signals being emitted on the signaler.
    /// Throws nothing.  The behavior is undefined if this method is called
    /// from a slot connected to the signaler.  Note that it is unspecified
    /// how many slots, if any, will be called by any invocation on the
    /// signaler that begins before this function completes.  Also note that
    /// if a slot is connected to this signaler during a call to this
    /// function, it is unspecified whether that slot will be disconnected.
    void disconnectAllSlotsAndWait() BSLS_KEYWORD_NOEXCEPT;

    /// Implements `Signaler::disconnectGroup()`.  Disconnect all slots, if
    /// any, connected to this signaler in the specified `group`.  Any
    /// signal emitted on the corresponding signaler that happens after this
    /// call to disconnect completes will not call any slots in `group` that
    /// were connected prior to this call.  Throws nothing.  Note that this
    /// function does not block the calling thread pending completion of
    /// ongoing signals emitted on the signaler.  Also note that it is
    /// unspecified how many affected slots, if any, will be signalled to by
    /// any invocation on the signaler that begins before this function
    /// completes.  Also note that if a slot in `group` is connected to this
    /// signaler during a call to this function, it is unspecified whether
    /// that slot will be disconnected.
    void disconnectGroup(int group) BSLS_KEYWORD_NOEXCEPT;

    /// Implements `Signaler::disconnectGroupAndWait()`.  Disconnect all
    /// slots, if any, connected to this signaler in the specified `group`.
    /// Any signal emitted on the corresponding signaler that happens after
    /// this call to disconnect completes will not call any slots in `group`
    /// that were connected prior to this call.  This function blocks the
    /// calling thread pending completion of ongoing signals being emitted
    /// on the signaler.  Throws nothing.  The behavior is undefined if this
    /// method is called from a slot connected to the signaler.  Note that
    /// it is unspecified how many affected slots, if any, will be signaled
    /// to by any emission on the signaler that begins before this function
    /// completes.  Also note that if a slot in `group` is connected to this
    /// signaler during a call to this function, it is unspecified whether
    /// that slot will be disconnected.
    void disconnectGroupAndWait(int group) BSLS_KEYWORD_NOEXCEPT;

    /// Notify this signaler that a slot with the specified `slotMapKey` was
    /// disconnected.  Throws nothing.
    void notifyDisconnected(SlotMapKey slotMapKey) BSLS_KEYWORD_NOEXCEPT;

    /// Block until all signals currently being emitted on the signaler have
    /// completed.
    void synchronizeWait() BSLS_KEYWORD_NOEXCEPT;

  public:
    // ACCESSORS

    /// Called by `Signaler_Invocable`s call operators, passing the
    /// specified `arg1`, `arg2`, `arg3`, `arg4`, `arg5`, `arg6`, `arg7`,
    /// `arg8` and `arg9` on to the slots.
    void invoke(typename ArgumentType::ForwardingType1 arg1,
                typename ArgumentType::ForwardingType2 arg2,
                typename ArgumentType::ForwardingType3 arg3,
                typename ArgumentType::ForwardingType4 arg4,
                typename ArgumentType::ForwardingType5 arg5,
                typename ArgumentType::ForwardingType6 arg6,
                typename ArgumentType::ForwardingType7 arg7,
                typename ArgumentType::ForwardingType8 arg8,
                typename ArgumentType::ForwardingType9 arg9) const;

    /// Implements `Signaler::slotCount()`.  Return the number of slots
    /// connected to this signaler.  Note that in multithreaded environment,
    /// the value returned by `slotCount()` is approximate.
    bsl::size_t slotCount() const;
};

                               // ==============
                               // class Signaler
                               // ==============

/// This class template provides a thread-safe signaler that executes
/// connected slots when invoked via its call operator.  `t_PROT` is a
/// function signature and must have a return type of `void`.  The callable
/// objects assigned to the slots may have return types other than `void`,
/// in which case their return values will be discarded.
template <class t_PROT>
class Signaler : public Signaler_Invocable<Signaler<t_PROT>, t_PROT> {

  public:
    // TYPES
    typedef void ResultType;  // Defines the result type of 'operator()'.  If
                              // 't_PROT' has a result type that is not 'void',
                              // the return values of the calls to the slots
                              // are discarded.

  private:
    // PRIVATE DATA
    bsl::shared_ptr<Signaler_Node<t_PROT> > d_signalerNodePtr;

    // FRIENDS
    friend struct Signaler_Invocable<Signaler<t_PROT>, t_PROT>;

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Signaler, bslma::UsesBslmaAllocator)

  private:
    // NOT IMPLEMENTED
    Signaler(           const Signaler&) BSLS_KEYWORD_DELETED;
    Signaler& operator=(const Signaler&) BSLS_KEYWORD_DELETED;

  public:
    // CREATORS

    /// Create a `Signaler` object having no connected slots.  Optionally
    /// specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.  Note that the supplied allocator must remain valid until all
    /// connection objects associated with this signaler are destroyed.
    explicit
    Signaler(bslma::Allocator *basicAllocator = 0);

    /// Destroy this object.  Call `disconnectAllSlots()`.  The behavior is
    /// undefined if this function is invoked from a slot connected to this
    /// signaler.
    ~Signaler();

  public:
    // MANIPULATORS

    /// Connect the specified `slot`, a callable object which must meet the
    /// `Slot Object Requirements` described in the component documentation,
    /// to this signaler.  Optionally specify a `group` used to order slots
    /// upon invocation.  Return an instance of `SignalerConnection`
    /// representing the created connection.  This function meets the strong
    /// exception guarantee.  Note that the connected slot may be called by
    /// a signal emitted from another thread before this function completes.
    /// Also note that it is unspecified whether connecting a slot while the
    /// signaler is emitting will result in the slot being called
    /// immediately.  Note that `t_FUNC` may have a return type other than
    /// `void`, but in that case, when the slot is called, the return value
    /// will be discarded.
    template <class t_FUNC>
    SignalerConnection connect(
                          BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) slot,
                          int                                       group = 0);

    /// Disconnect all slots, if any, connected to this signaler.  Any
    /// signals emitted on the corresponding signaler that happen after this
    /// call to disconnect completes will not call any slots that were
    /// connected prior to this call.  Throws nothing.  Note that this
    /// function does not block the calling thread pending completion of
    /// ongoing signals emitted on the signaler.  Also note that it is
    /// unspecified how many slots, if any, will be called by any invocation
    /// on the signaler that begins before this function completes.  Also
    /// note that if a slot is connected to this signaler during a call to
    /// this function, it is unspecified whether that slot will be
    /// disconnected.
    void disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT;

    /// Disconnect all slots, if any, connected to this signaler.  Any
    /// signals emitted on the corresponding signaler that happens after
    /// this call to disconnect completes will not call any slots that were
    /// connected prior to this call.  This function blocks the calling
    /// thread pending completion of all ongoing signals being emitted on
    /// the signaler.  Throws nothing.  The behavior is undefined if this
    /// method is called from a slot connected to the signaler.  Note that
    /// it is unspecified how many slots, if any, will be called by any
    /// invocation on the signaler that begins before this function
    /// completes.  Also note that if a slot is connected to this signaler
    /// during a call to this function, it is unspecified whether that slot
    /// will be disconnected.
    void disconnectAllSlotsAndWait() BSLS_KEYWORD_NOEXCEPT;

    /// Disconnect all slots, if any, connected to this signaler in the
    /// specified `group`.  Any signal emitted on the corresponding signaler
    /// that happens after this call to disconnect completes will not call
    /// any slots in `group` that were connected prior to this call.  Throws
    /// nothing.  Note that this function does not block the calling thread
    /// pending completion of ongoing signals emitted on the signaler.  Also
    /// note that it is unspecified how many affected slots, if any, will be
    /// signalled to by any invocation on the signaler that begins before
    /// this function completes.  Also note that if a slot in `group` is
    /// connected to this signaler during a call to this function, it is
    /// unspecified whether that slot will be disconnected.
    void disconnectGroup(int group) BSLS_KEYWORD_NOEXCEPT;

    /// Disconnect all slots, if any, connected to this signaler in the
    /// specified `group`.  Any signal emitted on the corresponding signaler
    /// that happens after this call to disconnect completes will not call
    /// any slots in `group` that were connected prior to this call.  This
    /// function blocks the calling thread pending completion of ongoing
    /// signals being emitted on the signaler.  Throws nothing.  The
    /// behavior is undefined if this method is called from a slot connected
    /// to the signaler.  Note that it is unspecified how many affected
    /// slots, if any, will be signaled to by any emission on the signaler
    /// that begins before this function completes.  Also note that if a
    /// slot in `group` is connected to this signaler during a call to this
    /// function, it is unspecified whether that slot will be disconnected.
    void disconnectGroupAndWait(int group) BSLS_KEYWORD_NOEXCEPT;

  public:
    // ACCESSORS

    // 'bdlmt::Signaler_Invocable', from which this 'class' inherits, provides
    // a call operator that, in C++11, would be defined and behave exactly this
    // way, except that the number of arguments is limited to
    // 9, where 'ARGS...' are the arguments of 't_PROT'.  Sequentially emit
    // the signal, sequentially calling each slot connected to this signaler as
    // if by 'f_i(args...)', where 'f_i' is the i-th connected slot.  The
    // behavior is undefined if this function is invoked from a slot connected
    // to this signaler.  Note that signals emitted to slots are ordered by
    // their respective groups, and within groups, by the order in which they
    // were connected.  Also note that the call operator does not forward
    // rvalue references.  That is done explicitly to prevent invocation
    // arguments from being moved to the first slot, leaving them "empty" for
    // all subsequent slots.  Also note that if a slot is connected by a called
    // slot and the group of the new slots is less than the group of the called
    // slot, the new slot will not be called, otherwise it will.  If a slot
    // that has not been visited in a traversal is disconnected by a call to
    // any of the 'disconnect*' methods, the disconnected slot will not be
    // called in the traversal.  Also note that if execution of a slot throws
    // an exception, the emission sequence is interrupted and the exception is
    // propagated to the caller of the signaler immediately.
        // void operator()(ARGS... args) const;
        //

    /// Return the number of slots connected to this signaler.  Note that
    /// the value returned by `slotCount()` is approximate if the signaler
    /// is being simultaneously manipulated by other threads.
    bsl::size_t slotCount() const;
};

                          // ========================
                          // class SignalerConnection
                          // ========================

/// This `class` represents a connection between a signaler and a slot.  It
/// is a lightweight object that has the ability to query whether the
/// signaler and slot are currently connected, and to disconnect the slot
/// from the signaler.  Note that, unless otherwise specified, it is safe to
/// invoke any method of `SignalerConnection` from the context of its
/// associated slot, or any other slot.
class SignalerConnection {

  private:
    // PRIVATE TYPES
    typedef Signaler_SlotNode_Base SlotNode_Base;

  private:
    // PRIVATE DATA

    // Weak pointer to the associated slot.
    bsl::weak_ptr<SlotNode_Base> d_slotNodeBasePtr;

    // FRIENDS
    template <class>
    friend class Signaler_Node;
    friend bool operator<( const SignalerConnection&,
                           const SignalerConnection&);
    friend bool operator==(const SignalerConnection&,
                           const SignalerConnection&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(SignalerConnection,
                                   bslmf::IsBitwiseMoveable)

  private:
    // PRIVATE CREATORS

    /// Create `SignalerConnection` object weakly linked to the specified
    /// `slotNodeBasePtr`.
    explicit
    SignalerConnection(const bsl::shared_ptr<SlotNode_Base>& slotNodeBasePtr)
                                                         BSLS_KEYWORD_NOEXCEPT;

  public:
    // CREATORS

    /// Create a `SignalerConnection` object having no associated slot.
    SignalerConnection();

    /// Create a `SignalerConnection` object that refers to and assumes
    /// management of the same slot (if any) as the specified `original`
    /// object.  Throws nothing.
    SignalerConnection(const SignalerConnection& original)
                                                         BSLS_KEYWORD_NOEXCEPT;

    /// Create a `SignalerConnection` object that refers to and assumes
    /// management of the same slot (if any) as the specified `original`
    /// object, and reset `original` to a default-constructed state.  Throws
    /// nothing.
    SignalerConnection(bslmf::MovableRef<SignalerConnection> original)
                                                         BSLS_KEYWORD_NOEXCEPT;

    // MANIPULATORS

    /// Make this connection refer to and assume management of the same slot
    /// (if any) as the specified `rhs` connection.  Return `*this`.
    SignalerConnection&
    operator=(const SignalerConnection& rhs);

    /// Make this connection refer to and assume management of the same slot
    /// (if any) as the specified `rhs` connection, and reset `rhs` to a
    /// default-constructed state.  Return `*this`.  Throws nothing.
    SignalerConnection&
    operator=(bslmf::MovableRef<SignalerConnection> rhs) BSLS_KEYWORD_NOEXCEPT;

    /// Disassociate this connection object from its associated slot, if any,
    /// and reset `*this` to a default-constructed state.  Throws nothing.
    void reset() BSLS_KEYWORD_NOEXCEPT;

    /// Swap the contents of `*this` and the specified `other`.  Throws
    /// nothing.
    void swap(SignalerConnection& other) BSLS_KEYWORD_NOEXCEPT;

  public:
    // ACCESSORS

    /// Disconnect the associated slot.  If the slot was already disconnected,
    /// this function has no effect.  This function returns immediately without
    /// waiting on any calls to the signaler that may be in progress.  Any
    /// signal emitted on the corresponding signaler that happens after this
    /// call to `disconnect` completes will not emit to the slot.  Throws
    /// nothing.  Note that it is unspecified if any signal that is emitted
    /// before this function completes will call the slot.
    void disconnect() const BSLS_KEYWORD_NOEXCEPT;

    /// Disconnect the associated slot.  If the slot was already disconnected,
    /// this function has no effect.  This function blocks the calling thread
    /// pending completion of signals emitted on the signaler by any thread,
    /// even if the slot was disconnected prior to this call.  Any signal
    /// emitted on the corresponding signaler that happens after this call to
    /// `disconnect` completes will not emit to the slot.  Throws nothing.  The
    /// behavior is undefined if this method is called from any slot.  Note
    /// that it is unspecified if any signal emitted on the signaler that
    /// begins before this function completes will call the slot.
    void disconnectAndWait() const BSLS_KEYWORD_NOEXCEPT;

    /// Return `true` if the associated slot is connected to the signaler
    /// `*this` was obtained from, and `false` otherwise.  If `*this` does not
    /// have an associated slot (i.e., was default-constructed), return
    /// `false`.
    bool isConnected() const;
};

                        // =============================
                        // class SignalerConnectionGuard
                        // =============================

/// This guard type `has a` `SignalerConnection`, through which it can manage a
/// slot, and when it is destroyed or assigned to it will disconnect that slot.
/// It also contains a boolean `waitOnDisconnect` attribute, which determines
/// whether `disconnect` or `disconnectAndWait` is used to disconnect the slot.
/// The `waitOnDisconnect` attribute is set in constructors from a
/// `SignalerConnection` and propagated when move constructing or move
/// assigning a guard to a different guard.
class SignalerConnectionGuard {

    // PRIVATE DATA
    SignalerConnection d_connection;

    bool               d_waitOnDisconnect;    // determines whether
                                              // `disconnect` or
                                              // `disconnectAndWait` is called
                                              // on `d_connection` at
                                              // destruction or assignment

  private:
    // NOT IMPLEMENTED
    SignalerConnectionGuard(           const SignalerConnectionGuard&)
                                                          BSLS_KEYWORD_DELETED;
    SignalerConnectionGuard& operator=(const SignalerConnectionGuard&)
                                                          BSLS_KEYWORD_DELETED;

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(SignalerConnectionGuard,
                                   bslmf::IsBitwiseMoveable);

  public:
    // CREATORS

    /// Create a `SignalerConnectionGuard` object having no associated slot
    /// with `waitOnDisconnect` set to `false`.
    SignalerConnectionGuard();

    /// Create a `SignalerConnectionGuard` object that refers to and assumes
    /// management of the same slot, if any, as the specified `connection`
    /// object.  Upon destruction or assignment, the optionally specified
    /// `waitOnDisconnect` determines whether `disconnect` or
    /// `disconnectAndWait` will be called on the slot managed by this object,
    /// if any.
    explicit
    SignalerConnectionGuard(
                           const SignalerConnection& connection,
                           bool                      waitOnDisconnect = false);

    /// Create a `SignalerConnectionGuard` that refers to the same slot, if
    /// any, as the specified `connection`, which is left in an unspecified
    /// state.  Optionally specify `waitOnDisconnect` indicating whether
    /// `disconnect` or `disconnectAndWait` will be called on the slot, if any,
    /// managed by this object upon destruction or assignment.  Throws nothing.
    explicit
    SignalerConnectionGuard(bslmf::MovableRef<
           SignalerConnection> connection,
           bool                waitOnDisconnect = false) BSLS_KEYWORD_NOEXCEPT;

    /// Create a `SignalerConnectionGuard` that manages the same slot, if any,
    /// as the specified `original`, which is left in the default-constructed
    /// state.  Copy the `waitOnDisconnect` state from `original`, indicating
    /// whether `disconnect()` or `disconnectAndWait()` will be called on the
    /// slot, if any, contained in this object upon destruction or assignment.
    /// Throws nothing.
    SignalerConnectionGuard(bslmf::MovableRef<SignalerConnectionGuard>
                                               original) BSLS_KEYWORD_NOEXCEPT;

    /// Destroy this object.  If a slot is being managed by this object, call
    /// `disconnect` or `disconnectAndWait` on it, depending upon the value of
    /// `waitOnDisconnect`.
    ~SignalerConnectionGuard();

    // MANIPULATORS

    /// If there is a currently managed slot, call `disconnect` or
    /// `disconnectAndWait` on it, depending on the value of the
    /// `waitOnDisconnect` state.  Make this connection refer to the same slot,
    /// if any, as the specified `rhs`, leaving `rhs` in the
    /// default-constructed state.  Use the `waitOnDisconnect` state of `rhs`,
    /// indicating whether `disconnect()` or `disconnectAndWait()` will be
    /// called on the slot managed by this object upon destruction or
    /// assignment.  Return `*this`.  Throws nothing.
    SignalerConnectionGuard&
                      operator=(bslmf::MovableRef<SignalerConnectionGuard> rhs)
                                                         BSLS_KEYWORD_NOEXCEPT;

    /// Disassociate this guard from its associated slot, if any, and reset
    /// `*this` to a default-constructed state.  Return a connection object
    /// referring to the slot, if any, that this guard was associated with
    /// prior to this call.  Throws nothing.
    SignalerConnection release() BSLS_KEYWORD_NOEXCEPT;

    /// Swap the contents of `*this` and the specified `other`.  Throws
    /// nothing.
    void swap(SignalerConnectionGuard& other) BSLS_KEYWORD_NOEXCEPT;

  public:
    // ACCESSORS

    /// Return a const reference to the connection held by this object.  Throws
    /// nothing.
    const SignalerConnection& connection() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a `bool` that indicates the value that determines whether the
    /// slot, if any, managed by this object will be disconnected using
    /// `disconnect` or `disconnectAndWait`.  Throws nothing.
    bool waitOnDisconnect() const BSLS_KEYWORD_NOEXCEPT;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` referring to the same slot
/// and `false` otherwise.
bool operator==(const SignalerConnection& lhs,
                const SignalerConnection& rhs);

/// Return `false` if the specified `lhs` and `rhs` referring to the same slot
/// and `true` otherwise.
bool operator!=(const SignalerConnection& lhs,
                const SignalerConnection& rhs);

/// Return `true` if the specified `lhs` is less than the specified `rhs` and
/// `false` otherwise.
bool operator<(const SignalerConnection& lhs,
               const SignalerConnection& rhs);

/// Return `true` if the specified `lhs` is grater than the specified `rhs` and
/// `false` otherwise.
bool operator>(const SignalerConnection& lhs,
               const SignalerConnection& rhs);

/// Return `true` if the specified `lhs` is less than or equal to the specified
/// `rhs` and `false` otherwise.
bool operator<=(const SignalerConnection& lhs,
                const SignalerConnection& rhs);

/// Return `true` if the specified `lhs` is greater than or equal to the
/// specified `rhs` and `false` otherwise.
bool operator>=(const SignalerConnection& lhs,
                const SignalerConnection& rhs);

// FREE FUNCTIONS

/// Swap the contents of the specified `a` and `b`.  Throws nothing.
void swap(SignalerConnection& a,
          SignalerConnection& b) BSLS_KEYWORD_NOEXCEPT;

/// Swap the contents of the specified `a` and `b`.  Throws nothing.
void swap(SignalerConnectionGuard& a,
          SignalerConnectionGuard& b) BSLS_KEYWORD_NOEXCEPT;

// ============================================================================
//                           INLINE DEFINITIONS
// ============================================================================

                         // -------------------------
                         // struct Signaler_Invocable
                         // -------------------------

template <class t_SIGNALER>
inline
void Signaler_Invocable<t_SIGNALER, void()>::operator()() const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg(),
                                                            Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1)>::operator()(
                                                             t_ARG1 arg1) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1, class t_ARG2>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2)>::operator()(
                                                             t_ARG1 arg1,
                                                             t_ARG2 arg2) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1, class t_ARG2, class t_ARG3>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2, t_ARG3)>::operator()(
                                                             t_ARG1 arg1,
                                                             t_ARG2 arg2,
                                                             t_ARG3 arg3) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1,
                                         t_ARG2,
                                         t_ARG3,
                                         t_ARG4)>::operator()(
                                                           t_ARG1 arg1,
                                                           t_ARG2 arg2,
                                                           t_ARG3 arg3,
                                                           t_ARG4 arg4) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4,
                          class t_ARG5>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1,
                                         t_ARG2,
                                         t_ARG3,
                                         t_ARG4,
                                         t_ARG5)>::
                                                operator()(t_ARG1 arg1,
                                                           t_ARG2 arg2,
                                                           t_ARG3 arg3,
                                                           t_ARG4 arg4,
                                                           t_ARG5 arg5) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                      bslmf::ForwardingTypeUtil<t_ARG5>::forwardToTarget(arg5),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4,
                          class t_ARG5,
                          class t_ARG6>
inline
void Signaler_Invocable<t_SIGNALER, void(t_ARG1,
                                         t_ARG2,
                                         t_ARG3,
                                         t_ARG4,
                                         t_ARG5,
                                         t_ARG6)>::
                                               operator()(t_ARG1 arg1,
                                                          t_ARG2 arg2,
                                                          t_ARG3 arg3,
                                                          t_ARG4 arg4,
                                                          t_ARG5 arg5,
                                                          t_ARG6 arg6) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                      bslmf::ForwardingTypeUtil<t_ARG5>::forwardToTarget(arg5),
                      bslmf::ForwardingTypeUtil<t_ARG6>::forwardToTarget(arg6),
                      Signaler_NotArg(),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4,
                          class t_ARG5,
                          class t_ARG6,
                          class t_ARG7>
inline
void Signaler_Invocable<t_SIGNALER,
                        void(t_ARG1,
                             t_ARG2,
                             t_ARG3,
                             t_ARG4,
                             t_ARG5,
                             t_ARG6,
                             t_ARG7)>::operator()(t_ARG1 arg1,
                                                  t_ARG2 arg2,
                                                  t_ARG3 arg3,
                                                  t_ARG4 arg4,
                                                  t_ARG5 arg5,
                                                  t_ARG6 arg6,
                                                  t_ARG7 arg7) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                      bslmf::ForwardingTypeUtil<t_ARG5>::forwardToTarget(arg5),
                      bslmf::ForwardingTypeUtil<t_ARG6>::forwardToTarget(arg6),
                      bslmf::ForwardingTypeUtil<t_ARG7>::forwardToTarget(arg7),
                      Signaler_NotArg(),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4,
                          class t_ARG5,
                          class t_ARG6,
                          class t_ARG7,
                          class t_ARG8>
inline
void Signaler_Invocable<t_SIGNALER,
                        void(t_ARG1,
                             t_ARG2,
                             t_ARG3,
                             t_ARG4,
                             t_ARG5,
                             t_ARG6,
                             t_ARG7,
                             t_ARG8)>::operator()(t_ARG1 arg1,
                                                  t_ARG2 arg2,
                                                  t_ARG3 arg3,
                                                  t_ARG4 arg4,
                                                  t_ARG5 arg5,
                                                  t_ARG6 arg6,
                                                  t_ARG7 arg7,
                                                  t_ARG8 arg8) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                      bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                      bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                      bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                      bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                      bslmf::ForwardingTypeUtil<t_ARG5>::forwardToTarget(arg5),
                      bslmf::ForwardingTypeUtil<t_ARG6>::forwardToTarget(arg6),
                      bslmf::ForwardingTypeUtil<t_ARG7>::forwardToTarget(arg7),
                      bslmf::ForwardingTypeUtil<t_ARG8>::forwardToTarget(arg8),
                      Signaler_NotArg());
}

template <class t_SIGNALER, class t_ARG1,
                          class t_ARG2,
                          class t_ARG3,
                          class t_ARG4,
                          class t_ARG5,
                          class t_ARG6,
                          class t_ARG7,
                          class t_ARG8,
                          class t_ARG9>
inline
void
Signaler_Invocable<t_SIGNALER, void(
                       t_ARG1,
                       t_ARG2,
                       t_ARG3,
                       t_ARG4,
                       t_ARG5,
                       t_ARG6,
                       t_ARG7,
                       t_ARG8,
                       t_ARG9)>::operator()(t_ARG1 arg1,
                                            t_ARG2 arg2,
                                            t_ARG3 arg3,
                                            t_ARG4 arg4,
                                            t_ARG5 arg5,
                                            t_ARG6 arg6,
                                            t_ARG7 arg7,
                                            t_ARG8 arg8,
                                            t_ARG9 arg9) const
{
    static_cast<const t_SIGNALER *>(this)->d_signalerNodePtr->invoke(
                     bslmf::ForwardingTypeUtil<t_ARG1>::forwardToTarget(arg1),
                     bslmf::ForwardingTypeUtil<t_ARG2>::forwardToTarget(arg2),
                     bslmf::ForwardingTypeUtil<t_ARG3>::forwardToTarget(arg3),
                     bslmf::ForwardingTypeUtil<t_ARG4>::forwardToTarget(arg4),
                     bslmf::ForwardingTypeUtil<t_ARG5>::forwardToTarget(arg5),
                     bslmf::ForwardingTypeUtil<t_ARG6>::forwardToTarget(arg6),
                     bslmf::ForwardingTypeUtil<t_ARG7>::forwardToTarget(arg7),
                     bslmf::ForwardingTypeUtil<t_ARG8>::forwardToTarget(arg8),
                     bslmf::ForwardingTypeUtil<t_ARG9>::forwardToTarget(arg9));
}

                            // -----------------------
                            // class Signaler_SlotNode
                            // -----------------------

// PRIVATE ACCESSORS
template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(bsl::integral_constant<int, 0>,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg,
                                         ForwardingNotArg) const
{
    d_func();
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 1>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 2>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 3>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 4>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   typename ArgumentType::ForwardingType4 arg4,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 5>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   typename ArgumentType::ForwardingType4 arg4,
                                   typename ArgumentType::ForwardingType5 arg5,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4, arg5);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 6>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   typename ArgumentType::ForwardingType4 arg4,
                                   typename ArgumentType::ForwardingType5 arg5,
                                   typename ArgumentType::ForwardingType6 arg6,
                                   ForwardingNotArg,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4, arg5, arg6);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 7>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   typename ArgumentType::ForwardingType4 arg4,
                                   typename ArgumentType::ForwardingType5 arg5,
                                   typename ArgumentType::ForwardingType6 arg6,
                                   typename ArgumentType::ForwardingType7 arg7,
                                   ForwardingNotArg,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                                   bsl::integral_constant<int, 8>,
                                   typename ArgumentType::ForwardingType1 arg1,
                                   typename ArgumentType::ForwardingType2 arg2,
                                   typename ArgumentType::ForwardingType3 arg3,
                                   typename ArgumentType::ForwardingType4 arg4,
                                   typename ArgumentType::ForwardingType5 arg5,
                                   typename ArgumentType::ForwardingType6 arg6,
                                   typename ArgumentType::ForwardingType7 arg7,
                                   typename ArgumentType::ForwardingType8 arg8,
                                   ForwardingNotArg) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::doInvoke(
                             bsl::integral_constant<int, 9>,
                             typename ArgumentType::ForwardingType1 arg1,
                             typename ArgumentType::ForwardingType2 arg2,
                             typename ArgumentType::ForwardingType3 arg3,
                             typename ArgumentType::ForwardingType4 arg4,
                             typename ArgumentType::ForwardingType5 arg5,
                             typename ArgumentType::ForwardingType6 arg6,
                             typename ArgumentType::ForwardingType7 arg7,
                             typename ArgumentType::ForwardingType8 arg8,
                             typename ArgumentType::ForwardingType9 arg9) const
{
    // NOTE: Does not forward

    d_func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}

// CREATORS
template <class t_PROT>
template <class t_FUNC>
Signaler_SlotNode<t_PROT>::Signaler_SlotNode(
                    const bsl::weak_ptr<SignalerNode>&         signalerNodePtr,
                    BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC)  func,
                    SlotMapKey                                 slotMapKey,
                    bslma::Allocator                          *allocator)
: d_slotMapKey(slotMapKey)
, d_isConnected(true)
, d_signalerNodePtr(signalerNodePtr)
, d_func(bsl::allocator_arg,
         allocator,
         BSLS_COMPILERFEATURES_FORWARD(t_FUNC, func))
{
    BSLS_ASSERT(!signalerNodePtr.expired());
    BSLS_ASSERT(allocator);
}

// MANIPULATORS
template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::disconnect() BSLS_KEYWORD_NOEXCEPT
{
    if (!d_isConnected.testAndSwap(true, false)) {
        return;                                                       // RETURN
    }

    // Notify the associated signaler

    bsl::shared_ptr<SignalerNode> signalerNodePtr = d_signalerNodePtr.lock();
    if (signalerNodePtr) {
        signalerNodePtr->notifyDisconnected(d_slotMapKey);
    }
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::disconnectAndWait() BSLS_KEYWORD_NOEXCEPT
{
    const bool wasConnected = d_isConnected.testAndSwap(true, false);

    // Notify the associated signaler

    bsl::shared_ptr<SignalerNode> signalerNodePtr = d_signalerNodePtr.lock();
    if (signalerNodePtr) {
        if (wasConnected) {
            signalerNodePtr->notifyDisconnected(d_slotMapKey);
        }

        // Synchronize with the call operator.

        signalerNodePtr->synchronizeWait();
    }
}

template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::notifyDisconnected() BSLS_KEYWORD_NOEXCEPT
{
    d_isConnected = false;
}

// ACCESSORS
template <class t_PROT>
inline
void Signaler_SlotNode<t_PROT>::invoke(
                             typename ArgumentType::ForwardingType1 arg1,
                             typename ArgumentType::ForwardingType2 arg2,
                             typename ArgumentType::ForwardingType3 arg3,
                             typename ArgumentType::ForwardingType4 arg4,
                             typename ArgumentType::ForwardingType5 arg5,
                             typename ArgumentType::ForwardingType6 arg6,
                             typename ArgumentType::ForwardingType7 arg7,
                             typename ArgumentType::ForwardingType8 arg8,
                             typename ArgumentType::ForwardingType9 arg9) const
{
    // The only way we are called is from a 'Signaler', which should exist
    // throughout the call and be holding a shared ptr to the 'Signaler_Node'.

    BSLS_ASSERT(!d_signalerNodePtr.expired());

    if (!d_isConnected) {
        // The slot was evidently disconnected by another thread.  Do nothing.

        return;                                                       // RETURN
    }

    typedef typename bslmf::FunctionPointerTraits<t_PROT*>::ArgumentList Args;

    doInvoke(bsl::integral_constant<int, Args::LENGTH>(),
             arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}

template <class t_PROT>
inline
bool Signaler_SlotNode<t_PROT>::isConnected() const
{
    return d_isConnected;
}

                            // -------------------
                            // class Signaler_Node
                            // -------------------

// CREATORS
template <class t_PROT>
Signaler_Node<t_PROT>::Signaler_Node(bslma::Allocator *allocator)
: d_signalerMutex()
, d_slotMap(allocator)
, d_keyId(0)
{
    BSLS_ASSERT(allocator);
}

// MANIPULATORS
template <class t_PROT>
inline
void Signaler_Node<t_PROT>::invoke(
                             typename ArgumentType::ForwardingType1 arg1,
                             typename ArgumentType::ForwardingType2 arg2,
                             typename ArgumentType::ForwardingType3 arg3,
                             typename ArgumentType::ForwardingType4 arg4,
                             typename ArgumentType::ForwardingType5 arg5,
                             typename ArgumentType::ForwardingType6 arg6,
                             typename ArgumentType::ForwardingType7 arg7,
                             typename ArgumentType::ForwardingType8 arg8,
                             typename ArgumentType::ForwardingType9 arg9) const
{
    // Hold this mutex (in read mode), so that disconnects in 'wait' mode can
    // synchronize with the call operator by momentarily locking it for write.

    bslmt::ReadLockGuard<bslmt::ReaderWriterMutex> lock(&d_signalerMutex);

    // 'slotHandle' points in to a node in the skiplist, which has a reference
    // count to prevent it from being deallocated & destroyed by another thread
    // as long as we have 'slotHandle' referring to it.  The node may be
    // removed from the skip list, though, in which case its 'next' pointers
    // will be null.

    SlotPairHandle slotHandle;
    if (d_slotMap.front(&slotHandle) != 0) {
        // No slots.  Do nothing.

        return;                                                       // RETURN
    }

    do {
        const SlotNode   *slotNodePtr = &*slotHandle.data();
        const SlotMapKey  slotMapKey  = slotHandle.key();

        // invoke the slot

        slotNodePtr->invoke(
                         arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);

        if (0 != d_slotMap.skipForward(&slotHandle)) {
            // 'slot' has been removed from the skip list and we can't use the
            // 'next' pointers to get to the next node, but we can still access
            // 'slotMapKey' to tell us where we were and directly look up the
            // next slot after that.

            if (0 != d_slotMap.findUpperBound(&slotHandle, slotMapKey)) {
                // No slots left.  We're done.

                return;                                               // RETURN
            }
        }
    } while (slotHandle);
}

template <class t_PROT>
template <class t_FUNC>
SignalerConnection Signaler_Node<t_PROT>::connect(
                               BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) func,
                               int                                       group)
{
    // create a key the slot will be indexed by

    const SlotMapKey slotMapKey(group, ++d_keyId);

    // create a slot

    bsl::shared_ptr<SlotNode> slotNodePtr = bsl::allocate_shared<SlotNode>(
                                   d_slotMap.allocator(),
                                   this->weak_from_this(),
                                   BSLS_COMPILERFEATURES_FORWARD(t_FUNC, func),
                                   slotMapKey,
                                   d_slotMap.allocator());

    // connect the slot

    d_slotMap.addR(slotMapKey, slotNodePtr);

    // return the connection

    return SignalerConnection(slotNodePtr);

}

template <class t_PROT>
void Signaler_Node<t_PROT>::disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT
{
    SlotPairHandle slotHandle;

    // disconnect all slots in the collection, one by one

    while (d_slotMap.front(&slotHandle) == 0) {
        // notify the slot it's being disconnected

        slotHandle.data()->notifyDisconnected();

        // remove the slot from the collection

        d_slotMap.remove(slotHandle);
    }
}

template <class t_PROT>
void Signaler_Node<t_PROT>::disconnectAllSlotsAndWait() BSLS_KEYWORD_NOEXCEPT
{
    disconnectAllSlots();
    synchronizeWait();
}

template <class t_PROT>
void Signaler_Node<t_PROT>::disconnectGroup(int group) BSLS_KEYWORD_NOEXCEPT
{
    // 'skipForward', below, may fail if a node is removed from the list while
    // we're visiting it, in which case we have to go back to the front and
    // start over.

    SlotPairHandle   slotHandle;
    const SlotMapKey boundary(group, 0);
    while (0 == d_slotMap.findLowerBound(&slotHandle, boundary) &&
                                             slotHandle.key().first == group) {
        do {
            // notify the slot it's being disconnected

            slotHandle.data()->notifyDisconnected();

            SlotPairHandle condemned(slotHandle);

            if (0 != d_slotMap.skipForward(&slotHandle)) {
                slotHandle.release();
            }

            // remove the slot from the collection

            d_slotMap.remove(condemned);
        } while (slotHandle && slotHandle.key().first == group);
    }
}

template <class t_PROT>
void Signaler_Node<t_PROT>::disconnectGroupAndWait(
                                               int group) BSLS_KEYWORD_NOEXCEPT
{
    disconnectGroup(group);
    synchronizeWait();
}

template <class t_PROT>
void Signaler_Node<t_PROT>::notifyDisconnected(
                                   SlotMapKey slotMapKey) BSLS_KEYWORD_NOEXCEPT
{
    typename KeyToSlotMap::PairHandle slotHandle;

    if (d_slotMap.find(&slotHandle, slotMapKey) != 0) {
        // Slot was already removed, probably by some form of 'disconnect*'
        // called on the 'Signaler'.  Do nothing.

        return;                                                       // RETURN
    }

    // remove the slot from the collection

    d_slotMap.remove(slotHandle);
}

template <class t_PROT>
inline
void Signaler_Node<t_PROT>::synchronizeWait() BSLS_KEYWORD_NOEXCEPT
{
    bslmt::WriteLockGuard<bslmt::ReaderWriterMutex> lock(&d_signalerMutex);
}

// ACCESSORS
template <class t_PROT>
inline
bsl::size_t Signaler_Node<t_PROT>::slotCount() const
{
    return d_slotMap.length();
}

                               // --------------
                               // class Signaler
                               // --------------

// CREATORS
template <class t_PROT>
Signaler<t_PROT>::Signaler(bslma::Allocator *basicAllocator)
: d_signalerNodePtr(bsl::allocate_shared<Signaler_Node<t_PROT> >(
                                    basicAllocator,
                                    bslma::Default::allocator(basicAllocator)))
{
    // NOTHING
}

template <class t_PROT>
inline
Signaler<t_PROT>::~Signaler()
{
    d_signalerNodePtr->disconnectAllSlots();
}

// MANIPULATORS
template <class t_PROT>
template <class t_FUNC>
inline
SignalerConnection Signaler<t_PROT>::connect(
                               BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) func,
                               int                                       group)
{
    return d_signalerNodePtr->connect(BSLS_COMPILERFEATURES_FORWARD(t_FUNC,
                                                                    func),
                                      group);
}

template <class t_PROT>
inline
void Signaler<t_PROT>::disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT
{
    d_signalerNodePtr->disconnectAllSlots();
}

template <class t_PROT>
inline
void Signaler<t_PROT>::disconnectAllSlotsAndWait() BSLS_KEYWORD_NOEXCEPT
{
    d_signalerNodePtr->disconnectAllSlotsAndWait();
}

template <class t_PROT>
inline
void Signaler<t_PROT>::disconnectGroup(int group) BSLS_KEYWORD_NOEXCEPT
{
    d_signalerNodePtr->disconnectGroup(group);
}

template <class t_PROT>
inline
void Signaler<t_PROT>::disconnectGroupAndWait(int group) BSLS_KEYWORD_NOEXCEPT
{
    d_signalerNodePtr->disconnectGroupAndWait(group);
}

// ACCESSORS
template <class t_PROT>
inline
bsl::size_t Signaler<t_PROT>::slotCount() const
{
    return d_signalerNodePtr->slotCount();
}

                          // ------------------------
                          // class SignalerConnection
                          // ------------------------

// MANIPULATORS
inline
void SignalerConnection::swap(SignalerConnection& other) BSLS_KEYWORD_NOEXCEPT
{
    d_slotNodeBasePtr.swap(other.d_slotNodeBasePtr);
}

// FREE OPERATORS
inline
bool operator==(const SignalerConnection& lhs, const SignalerConnection& rhs)
{
    return lhs.d_slotNodeBasePtr.rep() == rhs.d_slotNodeBasePtr.rep();
}

inline
bool operator<( const SignalerConnection& lhs, const SignalerConnection& rhs)
{
    return lhs.d_slotNodeBasePtr.owner_before(rhs.d_slotNodeBasePtr);
}

// FREE FUNCTIONS
inline
void swap(SignalerConnection& a,
          SignalerConnection& b) BSLS_KEYWORD_NOEXCEPT
{
    a.swap(b);
}

                      // -----------------------------
                      // class SignalerConnectionGuard
                      // -----------------------------

// ACCESSORS
inline
const SignalerConnection& SignalerConnectionGuard::connection() const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return d_connection;
}

inline
bool SignalerConnectionGuard::waitOnDisconnect() const BSLS_KEYWORD_NOEXCEPT
{
    return d_waitOnDisconnect;
}

// FREE FUNCTIONS
inline
void swap(SignalerConnectionGuard& a,
          SignalerConnectionGuard& b) BSLS_KEYWORD_NOEXCEPT
{
    a.swap(b);
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// NOTICE:
//      Copyright (C) Bloomberg L.P., 2019
//      All Rights Reserved.
//      Property of Bloomberg L.P. (BLP)
//      This software is made available solely pursuant to the
//      terms of a BLP license agreement which governs its use.
// ----------------------------- END-OF-FILE ----------------------------------

