// 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 '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 '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 'SignalerConnection's are transitive and are // provided to facilitate their being stored in an associative container. The // ordering of a '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 ///----- // Suppose we want to implement a GUI button class that allows users to // keep track of its 'press' events. // // First, we declare the 'class': //.. // class Button { // // A pretend GUI button. // // // DATA // int d_numPresses; // // public: // // TYPES // typedef bsl::function<void(int)> OnPressSlotType; // // Slot argument is the number of times the button has been // // pressed. // // private: // // PRIVATE DATA // bdlmt::Signaler<void(int)> d_onPress; // // Signaler argument is the number of times the button has been // // pressed. // // public: // // CREATORS // Button(); // // Construct a 'Button' object. // // // MANIPULATORS // bdlmt::SignalerConnection onPressConnect(const OnPressSlotType& slot); // // Connect the specified 'slot' to this button. // // void press(); // // Simulate user pressing on GUI button. // }; //.. // 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 // ================== struct Signaler_NotArg { // Provides a "Not an Argument" tag type. }; // =========================== // class Signaler_ArgumentType // =========================== template <class t_PROT> struct 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. //: o as the type of argument N itself (in the case of some fundamental //: types) //: o as a const reference (if 'TypeN' is large and either by value or by //: const reference), or //: o 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'. 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 // ========================= template <class t_SIGNALER, class t_PROT> 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> struct Signaler_Invocable<t_SIGNALER, void()> { // ACCESSOR void operator()() const; // 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. }; template <class t_SIGNALER, class t_ARG1> struct Signaler_Invocable<t_SIGNALER, void(t_ARG1)> { // ACCESSOR void operator()(t_ARG1) const; // 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. }; template <class t_SIGNALER, class t_ARG1, class t_ARG2> struct Signaler_Invocable<t_SIGNALER, void(t_ARG1, t_ARG2)> { // ACCESSOR void operator()(t_ARG1, t_ARG2) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6, t_ARG7) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6, t_ARG7, t_ARG8) const; // 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. }; 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 void operator()(t_ARG1, t_ARG2, t_ARG3, t_ARG4, t_ARG5, t_ARG6, t_ARG7, t_ARG8, t_ARG9) const; // 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. }; // ============================ // class Signaler_SlotNode_Base // ============================ 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. protected: // PROTECTED CREATORS virtual ~Signaler_SlotNode_Base(); // Virtual d'tor. public: // MANIPULATORS virtual void disconnect() BSLS_KEYWORD_NOEXCEPT = 0; // 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 disconnectAndWait() 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. // ACCESSOR virtual bool isConnected() const = 0; // Return 'true' if this slot is connected to its associated signaler, // and 'false' otherwise. }; // ======================= // class Signaler_SlotNode // ======================= template <class t_PROT> class Signaler_SlotNode : public Signaler_SlotNode_Base { // 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. private: // PRIVATE TYPES typedef Signaler_ArgumentType<t_PROT> ArgumentType; typedef typename ArgumentType::ForwardingNotArg ForwardingNotArg; typedef Signaler_Node<t_PROT> SignalerNode; public: // PUBLIC TYPE typedef bsl::pair<int, unsigned> SlotMapKey; // 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. private: // PRIVATE DATA SlotMapKey d_slotMapKey; // Slot key containing the call group and the slot ID. Used when // notifying the signaler about disconnection. bsls::AtomicBool d_isConnected; // Set to 'true' on construction, and to 'false' on disconnection. // Used for preventing calling a slot after it has been disconnected. bsl::weak_ptr<SignalerNode> d_signalerNodePtr; // Weak reference to the associated signaler node. bsl::function<t_PROT> d_func; // The target callback. private: // NOT IMPLEMENTED Signaler_SlotNode( const Signaler_SlotNode&) BSLS_KEYWORD_DELETED; Signaler_SlotNode& operator=(const Signaler_SlotNode&) BSLS_KEYWORD_DELETED; private: // PRIVATE ACCESSORS 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; // 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. public: // CREATORS template <class t_FUNC> Signaler_SlotNode( const bsl::weak_ptr<SignalerNode>& signalerNodePtr, BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) slot, SlotMapKey slotMapKey, bslma::Allocator *allocator); // 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. // ~Signaler_SlotNode() = default; // Destroy this object. public: // MANIPULATOR void disconnect() BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_OVERRIDE; // 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 disconnectAndWait() 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 notifyDisconnected() BSLS_KEYWORD_NOEXCEPT; // Notify this slot that is was disconnected from its associated // signaler. Throws nothing. After this function completes, // 'isConnected()' returns 'false'. // ACCESSORS 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; // 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. bool isConnected() const BSLS_KEYWORD_OVERRIDE; // Return 'true' if this slot is connected to its associated signaler, // and 'false' otherwise. }; // =================== // class Signaler_Node // =================== template <class t_PROT> class Signaler_Node : public bsl::enable_shared_from_this<Signaler_Node<t_PROT> > { // 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. 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 mutable bslmt::ReaderWriterMutex d_signalerMutex; // The purpose of this mutex is to implement the waiting behavior of // diconnects in 'wait' mode. KeyToSlotMap d_slotMap; // Thread-safe collection containing slots indexed (and ordered) by // their respective keys. bsls::AtomicUint d_keyId; // For supplying 'second' members of the 'SlotMapKey' values that are // unique to a signaler. private: // NOT IMPLEMENTED Signaler_Node( const Signaler_Node&) BSLS_KEYWORD_DELETED; Signaler_Node& operator=(const Signaler_Node&) BSLS_KEYWORD_DELETED; public: // CREATORS explicit Signaler_Node(bslma::Allocator *allocator); // 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. public: // MANIPULATORS template <class t_FUNC> SignalerConnection connect( BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) slot, int group); // 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. void disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT; // 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 disconnectAllSlotsAndWait() 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 disconnectGroup(int group) 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 disconnectGroupAndWait(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 notifyDisconnected(SlotMapKey slotMapKey) BSLS_KEYWORD_NOEXCEPT; // Notify this signaler that a slot with the specified 'slotMapKey' was // disconnected. Throws nothing. void synchronizeWait() BSLS_KEYWORD_NOEXCEPT; // Block until all signals currently being emitted on the signaler have // completed. public: // ACCESSORS 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; // Called by 'Signaler_Invocable's call operators, passing the // specified 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', // 'arg8' and 'arg9' on to the slots. bsl::size_t slotCount() 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. }; // ============== // class Signaler // ============== template <class t_PROT> class Signaler : public Signaler_Invocable<Signaler<t_PROT>, t_PROT> { // 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. 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 explicit Signaler(bslma::Allocator *basicAllocator = 0); // 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. ~Signaler(); // Destroy this object. Call 'disconnectAllSlots()'. The behavior is // undefined if this function is invoked from a slot connected to this // signaler. public: // MANIPULATORS template <class t_FUNC> SignalerConnection connect( BSLS_COMPILERFEATURES_FORWARD_REF(t_FUNC) slot, int group = 0); // 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. void disconnectAllSlots() BSLS_KEYWORD_NOEXCEPT; // 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 disconnectAllSlotsAndWait() 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 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. 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 disconnectGroupAndWait(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. public: // ACCESSORS // void operator()(ARGS... args) const; // // '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. bsl::size_t slotCount() 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. }; // ======================== // class SignalerConnection // ======================== 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. private: // PRIVATE TYPES typedef Signaler_SlotNode_Base SlotNode_Base; private: // PRIVATE DATA bsl::weak_ptr<SlotNode_Base> d_slotNodeBasePtr; // Weak pointer to the associated slot. // 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 explicit SignalerConnection(const bsl::shared_ptr<SlotNode_Base>& slotNodeBasePtr) BSLS_KEYWORD_NOEXCEPT; // Create 'SignalerConnection' object weakly linked to the specified // 'slotNodeBasePtr'. public: // CREATORS SignalerConnection(); // Create a 'SignalerConnection' object having no associated slot. 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. Throws nothing. SignalerConnection(bslmf::MovableRef<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. // MANIPULATORS SignalerConnection& operator=(const SignalerConnection& rhs); // Make this connection refer to and assume management of the same slot // (if any) as the specified 'rhs' connection. Return '*this'. SignalerConnection& operator=(bslmf::MovableRef<SignalerConnection> rhs) BSLS_KEYWORD_NOEXCEPT; // 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. void reset() BSLS_KEYWORD_NOEXCEPT; // Disassociate this connection object from its associated slot, if // any, and reset '*this' to a default-constructed state. Throws // nothing. void swap(SignalerConnection& other) BSLS_KEYWORD_NOEXCEPT; // Swap the contents of '*this' and the specified 'other'. Throws // nothing. public: // ACCESSORS void disconnect() const BSLS_KEYWORD_NOEXCEPT; // 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 disconnectAndWait() 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. bool isConnected() const; // 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'. }; // ============================= // class SignalerConnectionGuard // ============================= 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. // 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 SignalerConnectionGuard(); // Create a 'SignalerConnectionGuard' object having no associated slot // with 'waitOnDisconnect' set to 'false'. explicit SignalerConnectionGuard( const SignalerConnection& connection, bool waitOnDisconnect = false); // 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(bslmf::MovableRef< SignalerConnection> connection, bool waitOnDisconnect = false) BSLS_KEYWORD_NOEXCEPT; // 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. SignalerConnectionGuard(bslmf::MovableRef<SignalerConnectionGuard> original) 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(); // Destroy this object. If a slot is being managed by this object, // call 'disconnect' or 'disconnectAndWait' on it, depending upon the // value of 'waitOnDisconnect'. // MANIPULATORS SignalerConnectionGuard& operator=(bslmf::MovableRef<SignalerConnectionGuard> rhs) BSLS_KEYWORD_NOEXCEPT; // 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. SignalerConnection release() 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. void swap(SignalerConnectionGuard& other) BSLS_KEYWORD_NOEXCEPT; // Swap the contents of '*this' and the specified 'other'. Throws // nothing. public: // ACCESSORS const SignalerConnection& connection() const BSLS_KEYWORD_NOEXCEPT; // Return a const reference to the connection held by this object. // Throws nothing. bool waitOnDisconnect() 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. }; // FREE OPERATORS bool operator==(const SignalerConnection& lhs, const SignalerConnection& rhs); // 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. // FREE FUNCTIONS 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; // Swap the contents of the specified 'a' and 'b'. Throws nothing. // ============================================================================ // 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 ----------------------------------