Chris@16: /* Chris@16: Template for Signa1, Signal2, ... classes that support signals Chris@16: with 1, 2, ... parameters Chris@16: Chris@16: Begin: 2007-01-23 Chris@16: */ Chris@16: // Copyright Frank Mori Hess 2007-2008 Chris@16: // Chris@16: // Use, modification and Chris@16: // distribution is subject to the Boost Software License, Version Chris@16: // 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // This file is included iteratively, and should not be protected from multiple inclusion Chris@16: Chris@16: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: #define BOOST_SIGNALS2_NUM_ARGS BOOST_PP_ITERATION() Chris@16: #else Chris@16: #define BOOST_SIGNALS2_NUM_ARGS 1 Chris@16: #endif Chris@16: Chris@16: // R, T1, T2, ..., TN, Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex Chris@16: #define BOOST_SIGNALS2_SIGNAL_TEMPLATE_INSTANTIATION \ Chris@16: BOOST_SIGNALS2_SIGNATURE_TEMPLATE_INSTANTIATION(BOOST_SIGNALS2_NUM_ARGS), \ Chris@16: Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: namespace signals2 Chris@16: { Chris@16: namespace detail Chris@16: { Chris@16: // helper for bound_extended_slot_function that handles specialization for void return Chris@16: template Chris@16: class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: { Chris@16: public: Chris@16: typedef R result_type; Chris@16: template Chris@16: result_type operator()(ExtendedSlotFunction &func, const connection &conn Chris@16: BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FULL_FORWARD_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: return func(conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FORWARDED_ARGS(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: }; Chris@16: #ifdef BOOST_NO_VOID_RETURNS Chris@16: template<> Chris@16: class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: { Chris@16: public: Chris@16: typedef result_type_wrapper::type result_type; Chris@16: template Chris@16: result_type operator()(ExtendedSlotFunction &func, const connection &conn Chris@16: BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FULL_FORWARD_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: func(conn BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FORWARDED_ARGS(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: return result_type(); Chris@16: } Chris@16: }; Chris@16: #endif Chris@16: // wrapper around an signalN::extended_slot_function which binds the Chris@16: // connection argument so it looks like a normal Chris@16: // signalN::slot_function Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: { Chris@16: public: Chris@16: typedef typename result_type_wrapper::type result_type; Chris@16: BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS)(const ExtendedSlotFunction &fun): Chris@16: _fun(fun), _connection(new connection) Chris@16: {} Chris@16: void set_connection(const connection &conn) Chris@16: { Chris@16: *_connection = conn; Chris@16: } Chris@16: Chris@16: #if BOOST_SIGNALS2_NUM_ARGS > 0 Chris@16: template Chris@16: #endif // BOOST_SIGNALS2_NUM_ARGS > 0 Chris@101: result_type operator()(BOOST_SIGNALS2_FULL_FORWARD_ARGS(BOOST_SIGNALS2_NUM_ARGS)) Chris@16: { Chris@16: return BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: () Chris@16: (_fun, *_connection BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FORWARDED_ARGS(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: // const overload Chris@16: #if BOOST_SIGNALS2_NUM_ARGS > 0 Chris@16: template Chris@16: #endif // BOOST_SIGNALS2_NUM_ARGS > 0 Chris@101: result_type operator()(BOOST_SIGNALS2_FULL_FORWARD_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: return BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_INVOKER_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: () Chris@16: (_fun, *_connection BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) Chris@101: BOOST_SIGNALS2_FORWARDED_ARGS(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: template Chris@16: bool operator==(const T &other) const Chris@16: { Chris@16: return _fun == other; Chris@16: } Chris@16: private: Chris@16: BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS)() Chris@16: {} Chris@16: Chris@16: ExtendedSlotFunction _fun; Chris@16: boost::shared_ptr _connection; Chris@16: }; Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION Chris@16: { Chris@16: public: Chris@16: typedef SlotFunction slot_function_type; Chris@16: // typedef slotN slot_type; Chris@16: typedef BOOST_SIGNALS2_SLOT_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: slot_type; Chris@16: typedef ExtendedSlotFunction extended_slot_function_type; Chris@16: // typedef slotN+1 extended_slot_type; Chris@16: typedef BOOST_SIGNALS2_EXTENDED_SLOT_TYPE(BOOST_SIGNALS2_NUM_ARGS) extended_slot_type; Chris@16: typedef typename nonvoid::type nonvoid_slot_result_type; Chris@16: private: Chris@16: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: class slot_invoker; Chris@16: #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: typedef variadic_slot_invoker slot_invoker; Chris@16: #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: typedef slot_call_iterator_cache slot_call_iterator_cache_type; Chris@16: typedef typename group_key::type group_key_type; Chris@16: typedef shared_ptr > connection_body_type; Chris@16: typedef grouped_list connection_list_type; Chris@16: typedef BOOST_SIGNALS2_BOUND_EXTENDED_SLOT_FUNCTION_N(BOOST_SIGNALS2_NUM_ARGS) Chris@16: bound_extended_slot_function_type; Chris@16: public: Chris@16: typedef Combiner combiner_type; Chris@16: typedef typename result_type_wrapper::type result_type; Chris@16: typedef Group group_type; Chris@16: typedef GroupCompare group_compare_type; Chris@16: typedef typename detail::slot_call_iterator_t > slot_call_iterator; Chris@16: Chris@16: BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)(const combiner_type &combiner_arg, Chris@16: const group_compare_type &group_compare): Chris@16: _shared_state(new invocation_state(connection_list_type(group_compare), combiner_arg)), Chris@16: _garbage_collector_it(_shared_state->connection_bodies().end()) Chris@16: {} Chris@16: // connect slot Chris@16: connection connect(const slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: return nolock_connect(slot, position); Chris@16: } Chris@16: connection connect(const group_type &group, Chris@16: const slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: return nolock_connect(group, slot, position); Chris@16: } Chris@16: // connect extended slot Chris@16: connection connect_extended(const extended_slot_type &ext_slot, connect_position position = at_back) Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: bound_extended_slot_function_type bound_slot(ext_slot.slot_function()); Chris@16: slot_type slot = replace_slot_function(ext_slot, bound_slot); Chris@16: connection conn = nolock_connect(slot, position); Chris@16: bound_slot.set_connection(conn); Chris@16: return conn; Chris@16: } Chris@16: connection connect_extended(const group_type &group, Chris@16: const extended_slot_type &ext_slot, connect_position position = at_back) Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: bound_extended_slot_function_type bound_slot(ext_slot.slot_function()); Chris@16: slot_type slot = replace_slot_function(ext_slot, bound_slot); Chris@16: connection conn = nolock_connect(group, slot, position); Chris@16: bound_slot.set_connection(conn); Chris@16: return conn; Chris@16: } Chris@16: // disconnect slot(s) Chris@16: void disconnect_all_slots() Chris@16: { Chris@16: shared_ptr local_state = Chris@16: get_readable_state(); Chris@16: typename connection_list_type::iterator it; Chris@16: for(it = local_state->connection_bodies().begin(); Chris@16: it != local_state->connection_bodies().end(); ++it) Chris@16: { Chris@16: (*it)->disconnect(); Chris@16: } Chris@16: } Chris@16: void disconnect(const group_type &group) Chris@16: { Chris@16: shared_ptr local_state = Chris@16: get_readable_state(); Chris@16: group_key_type group_key(grouped_slots, group); Chris@16: typename connection_list_type::iterator it; Chris@16: typename connection_list_type::iterator end_it = Chris@16: local_state->connection_bodies().upper_bound(group_key); Chris@16: for(it = local_state->connection_bodies().lower_bound(group_key); Chris@16: it != end_it; ++it) Chris@16: { Chris@16: (*it)->disconnect(); Chris@16: } Chris@16: } Chris@16: template Chris@16: void disconnect(const T &slot) Chris@16: { Chris@16: typedef mpl::bool_<(is_convertible::value)> is_group; Chris@16: do_disconnect(slot, is_group()); Chris@16: } Chris@16: // emit signal Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) Chris@16: { Chris@16: shared_ptr local_state; Chris@16: typename connection_list_type::iterator it; Chris@16: { Chris@16: unique_lock list_lock(_mutex); Chris@16: // only clean up if it is safe to do so Chris@16: if(_shared_state.unique()) Chris@16: nolock_cleanup_connections(false, 1); Chris@16: /* Make a local copy of _shared_state while holding mutex, so we are Chris@16: thread safe against the combiner or connection list getting modified Chris@16: during invocation. */ Chris@16: local_state = _shared_state; Chris@16: } Chris@16: slot_invoker invoker = slot_invoker(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: slot_call_iterator_cache_type cache(invoker); Chris@16: invocation_janitor janitor(cache, *this, &local_state->connection_bodies()); Chris@16: return detail::combiner_invoker() Chris@16: ( Chris@16: local_state->combiner(), Chris@16: slot_call_iterator(local_state->connection_bodies().begin(), local_state->connection_bodies().end(), cache), Chris@16: slot_call_iterator(local_state->connection_bodies().end(), local_state->connection_bodies().end(), cache) Chris@16: ); Chris@16: } Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: shared_ptr local_state; Chris@16: typename connection_list_type::iterator it; Chris@16: { Chris@16: unique_lock list_lock(_mutex); Chris@16: // only clean up if it is safe to do so Chris@16: if(_shared_state.unique()) Chris@16: nolock_cleanup_connections(false, 1); Chris@16: /* Make a local copy of _shared_state while holding mutex, so we are Chris@16: thread safe against the combiner or connection list getting modified Chris@16: during invocation. */ Chris@16: local_state = _shared_state; Chris@16: } Chris@16: slot_invoker invoker = slot_invoker(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: slot_call_iterator_cache_type cache(invoker); Chris@16: invocation_janitor janitor(cache, *this, &local_state->connection_bodies()); Chris@16: return detail::combiner_invoker() Chris@16: ( Chris@16: local_state->combiner(), Chris@16: slot_call_iterator(local_state->connection_bodies().begin(), local_state->connection_bodies().end(), cache), Chris@16: slot_call_iterator(local_state->connection_bodies().end(), local_state->connection_bodies().end(), cache) Chris@16: ); Chris@16: } Chris@16: std::size_t num_slots() const Chris@16: { Chris@16: shared_ptr local_state = Chris@16: get_readable_state(); Chris@16: typename connection_list_type::iterator it; Chris@16: std::size_t count = 0; Chris@16: for(it = local_state->connection_bodies().begin(); Chris@16: it != local_state->connection_bodies().end(); ++it) Chris@16: { Chris@16: if((*it)->connected()) ++count; Chris@16: } Chris@16: return count; Chris@16: } Chris@16: bool empty() const Chris@16: { Chris@16: shared_ptr local_state = Chris@16: get_readable_state(); Chris@16: typename connection_list_type::iterator it; Chris@16: for(it = local_state->connection_bodies().begin(); Chris@16: it != local_state->connection_bodies().end(); ++it) Chris@16: { Chris@16: if((*it)->connected()) return false; Chris@16: } Chris@16: return true; Chris@16: } Chris@16: combiner_type combiner() const Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: return _shared_state->combiner(); Chris@16: } Chris@16: void set_combiner(const combiner_type &combiner_arg) Chris@16: { Chris@16: unique_lock lock(_mutex); Chris@16: if(_shared_state.unique()) Chris@16: _shared_state->combiner() = combiner_arg; Chris@16: else Chris@16: _shared_state.reset(new invocation_state(*_shared_state, combiner_arg)); Chris@16: } Chris@16: private: Chris@16: typedef Mutex mutex_type; Chris@16: Chris@16: // slot_invoker is passed to slot_call_iterator_t to run slots Chris@16: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: class slot_invoker Chris@16: { Chris@16: public: Chris@16: typedef nonvoid_slot_result_type result_type; Chris@16: // typename add_reference::type Chris@16: #define BOOST_SIGNALS2_ADD_REF_TYPE(z, n, data) \ Chris@16: typename add_reference::type Chris@16: // typename add_reference::type argn Chris@16: #define BOOST_SIGNALS2_ADD_REF_ARG(z, n, data) \ Chris@16: BOOST_SIGNALS2_ADD_REF_TYPE(~, n, ~) \ Chris@16: BOOST_SIGNALS2_SIGNATURE_ARG_NAME(~, n, ~) Chris@16: // typename add_reference::type arg1, typename add_reference::type arg2, ..., typename add_reference::type argn Chris@16: #define BOOST_SIGNALS2_ADD_REF_ARGS(arity) \ Chris@16: BOOST_PP_ENUM(arity, BOOST_SIGNALS2_ADD_REF_ARG, ~) Chris@16: slot_invoker(BOOST_SIGNALS2_ADD_REF_ARGS(BOOST_SIGNALS2_NUM_ARGS)) BOOST_PP_EXPR_IF(BOOST_SIGNALS2_NUM_ARGS, :) Chris@16: #undef BOOST_SIGNALS2_ADD_REF_ARGS Chris@16: Chris@16: // m_argn Chris@16: #define BOOST_SIGNALS2_M_ARG_NAME(z, n, data) BOOST_PP_CAT(m_arg, BOOST_PP_INC(n)) Chris@16: // m_argn ( argn ) Chris@16: #define BOOST_SIGNALS2_MISC_STATEMENT(z, n, data) \ Chris@16: BOOST_SIGNALS2_M_ARG_NAME(~, n, ~) ( BOOST_SIGNALS2_SIGNATURE_ARG_NAME(~, n, ~) ) Chris@16: // m_arg1(arg1), m_arg2(arg2), ..., m_argn(argn) Chris@16: BOOST_PP_ENUM(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_MISC_STATEMENT, ~) Chris@16: #undef BOOST_SIGNALS2_MISC_STATEMENT Chris@16: {} Chris@16: result_type operator ()(const connection_body_type &connectionBody) const Chris@16: { Chris@16: result_type *resolver = 0; Chris@16: return m_invoke(connectionBody, Chris@16: resolver); Chris@16: } Chris@16: private: Chris@16: // declare assignment operator private since this class might have reference or const members Chris@16: slot_invoker & operator=(const slot_invoker &); Chris@16: Chris@16: #define BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT(z, n, data) \ Chris@16: BOOST_SIGNALS2_ADD_REF_TYPE(~, n, ~) BOOST_SIGNALS2_M_ARG_NAME(~, n, ~) ; Chris@16: BOOST_PP_REPEAT(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT, ~) Chris@16: #undef BOOST_SIGNALS2_ADD_REF_M_ARG_STATEMENT Chris@16: #undef BOOST_SIGNALS2_ADD_REF_ARG Chris@16: #undef BOOST_SIGNALS2_ADD_REF_TYPE Chris@16: Chris@16: // m_arg1, m_arg2, ..., m_argn Chris@16: #define BOOST_SIGNALS2_M_ARG_NAMES(arity) BOOST_PP_ENUM(arity, BOOST_SIGNALS2_M_ARG_NAME, ~) Chris@16: result_type m_invoke(const connection_body_type &connectionBody, Chris@16: const void_type *) const Chris@16: { Chris@16: connectionBody->slot.slot_function()(BOOST_SIGNALS2_M_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: return void_type(); Chris@16: } Chris@16: result_type m_invoke(const connection_body_type &connectionBody, ...) const Chris@16: { Chris@16: return connectionBody->slot.slot_function()(BOOST_SIGNALS2_M_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: }; Chris@16: #undef BOOST_SIGNALS2_M_ARG_NAMES Chris@16: #undef BOOST_SIGNALS2_M_ARG_NAME Chris@16: Chris@16: #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: // a struct used to optimize (minimize) the number of shared_ptrs that need to be created Chris@16: // inside operator() Chris@16: class invocation_state Chris@16: { Chris@16: public: Chris@16: invocation_state(const connection_list_type &connections_in, Chris@16: const combiner_type &combiner_in): _connection_bodies(new connection_list_type(connections_in)), Chris@16: _combiner(new combiner_type(combiner_in)) Chris@16: {} Chris@16: invocation_state(const invocation_state &other, const connection_list_type &connections_in): Chris@16: _connection_bodies(new connection_list_type(connections_in)), Chris@16: _combiner(other._combiner) Chris@16: {} Chris@16: invocation_state(const invocation_state &other, const combiner_type &combiner_in): Chris@16: _connection_bodies(other._connection_bodies), Chris@16: _combiner(new combiner_type(combiner_in)) Chris@16: {} Chris@16: connection_list_type & connection_bodies() { return *_connection_bodies; } Chris@16: const connection_list_type & connection_bodies() const { return *_connection_bodies; } Chris@16: combiner_type & combiner() { return *_combiner; } Chris@16: const combiner_type & combiner() const { return *_combiner; } Chris@16: private: Chris@16: invocation_state(const invocation_state &); Chris@16: Chris@16: shared_ptr _connection_bodies; Chris@16: shared_ptr _combiner; Chris@16: }; Chris@16: // Destructor of invocation_janitor does some cleanup when a signal invocation completes. Chris@16: // Code can't be put directly in signal's operator() due to complications from void return types. Chris@101: class invocation_janitor: noncopyable Chris@16: { Chris@16: public: Chris@16: typedef BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) signal_type; Chris@16: invocation_janitor Chris@16: ( Chris@16: const slot_call_iterator_cache_type &cache, Chris@16: const signal_type &sig, Chris@16: const connection_list_type *connection_bodies Chris@16: ):_cache(cache), _sig(sig), _connection_bodies(connection_bodies) Chris@16: {} Chris@16: ~invocation_janitor() Chris@16: { Chris@16: // force a full cleanup of disconnected slots if there are too many Chris@16: if(_cache.disconnected_slot_count > _cache.connected_slot_count) Chris@16: { Chris@16: _sig.force_cleanup_connections(_connection_bodies); Chris@16: } Chris@16: } Chris@16: private: Chris@16: const slot_call_iterator_cache_type &_cache; Chris@16: const signal_type &_sig; Chris@16: const connection_list_type *_connection_bodies; Chris@16: }; Chris@16: Chris@16: // clean up disconnected connections Chris@16: void nolock_cleanup_connections_from(bool grab_tracked, Chris@16: const typename connection_list_type::iterator &begin, unsigned count = 0) const Chris@16: { Chris@16: BOOST_ASSERT(_shared_state.unique()); Chris@16: typename connection_list_type::iterator it; Chris@16: unsigned i; Chris@16: for(it = begin, i = 0; Chris@16: it != _shared_state->connection_bodies().end() && (count == 0 || i < count); Chris@16: ++i) Chris@16: { Chris@16: bool connected; Chris@16: { Chris@16: unique_lock lock(**it); Chris@16: if(grab_tracked) Chris@16: (*it)->nolock_slot_expired(); Chris@16: connected = (*it)->nolock_nograb_connected(); Chris@16: }// scoped lock destructs here, safe to erase now Chris@16: if(connected == false) Chris@16: { Chris@16: it = _shared_state->connection_bodies().erase((*it)->group_key(), it); Chris@16: }else Chris@16: { Chris@16: ++it; Chris@16: } Chris@16: } Chris@16: _garbage_collector_it = it; Chris@16: } Chris@16: // clean up a few connections in constant time Chris@16: void nolock_cleanup_connections(bool grab_tracked, unsigned count) const Chris@16: { Chris@16: BOOST_ASSERT(_shared_state.unique()); Chris@16: typename connection_list_type::iterator begin; Chris@16: if(_garbage_collector_it == _shared_state->connection_bodies().end()) Chris@16: { Chris@16: begin = _shared_state->connection_bodies().begin(); Chris@16: }else Chris@16: { Chris@16: begin = _garbage_collector_it; Chris@16: } Chris@16: nolock_cleanup_connections_from(grab_tracked, begin, count); Chris@16: } Chris@16: /* Make a new copy of the slot list if it is currently being read somewhere else Chris@16: */ Chris@16: void nolock_force_unique_connection_list() Chris@16: { Chris@16: if(_shared_state.unique() == false) Chris@16: { Chris@16: _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies())); Chris@16: nolock_cleanup_connections_from(true, _shared_state->connection_bodies().begin()); Chris@16: }else Chris@16: { Chris@16: /* We need to try and check more than just 1 connection here to avoid corner Chris@16: cases where certain repeated connect/disconnect patterns cause the slot Chris@16: list to grow without limit. */ Chris@16: nolock_cleanup_connections(true, 2); Chris@16: } Chris@16: } Chris@16: // force a full cleanup of the connection list Chris@16: void force_cleanup_connections(const connection_list_type *connection_bodies) const Chris@16: { Chris@16: unique_lock list_lock(_mutex); Chris@16: // if the connection list passed in as a parameter is no longer in use, Chris@16: // we don't need to do any cleanup. Chris@16: if(&_shared_state->connection_bodies() != connection_bodies) Chris@16: { Chris@16: return; Chris@16: } Chris@16: if(_shared_state.unique() == false) Chris@16: { Chris@16: _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies())); Chris@16: } Chris@16: nolock_cleanup_connections_from(false, _shared_state->connection_bodies().begin()); Chris@16: } Chris@16: shared_ptr get_readable_state() const Chris@16: { Chris@16: unique_lock list_lock(_mutex); Chris@16: return _shared_state; Chris@16: } Chris@16: connection_body_type create_new_connection(const slot_type &slot) Chris@16: { Chris@16: nolock_force_unique_connection_list(); Chris@16: return connection_body_type(new connection_body(slot)); Chris@16: } Chris@16: void do_disconnect(const group_type &group, mpl::bool_ /* is_group */) Chris@16: { Chris@16: disconnect(group); Chris@16: } Chris@16: template Chris@16: void do_disconnect(const T &slot, mpl::bool_ /* is_group */) Chris@16: { Chris@16: shared_ptr local_state = Chris@16: get_readable_state(); Chris@16: typename connection_list_type::iterator it; Chris@16: for(it = local_state->connection_bodies().begin(); Chris@16: it != local_state->connection_bodies().end(); ++it) Chris@16: { Chris@16: unique_lock lock(**it); Chris@16: if((*it)->slot.slot_function() == slot) Chris@16: { Chris@16: (*it)->nolock_disconnect(); Chris@16: }else Chris@16: { Chris@16: // check for wrapped extended slot Chris@16: bound_extended_slot_function_type *fp; Chris@16: fp = (*it)->slot.slot_function().template target(); Chris@16: if(fp && *fp == slot) Chris@16: { Chris@16: (*it)->nolock_disconnect(); Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: // connect slot Chris@16: connection nolock_connect(const slot_type &slot, connect_position position) Chris@16: { Chris@16: connection_body_type newConnectionBody = Chris@16: create_new_connection(slot); Chris@16: group_key_type group_key; Chris@16: if(position == at_back) Chris@16: { Chris@16: group_key.first = back_ungrouped_slots; Chris@16: _shared_state->connection_bodies().push_back(group_key, newConnectionBody); Chris@16: }else Chris@16: { Chris@16: group_key.first = front_ungrouped_slots; Chris@16: _shared_state->connection_bodies().push_front(group_key, newConnectionBody); Chris@16: } Chris@16: newConnectionBody->set_group_key(group_key); Chris@16: return connection(newConnectionBody); Chris@16: } Chris@16: connection nolock_connect(const group_type &group, Chris@16: const slot_type &slot, connect_position position) Chris@16: { Chris@16: connection_body_type newConnectionBody = Chris@16: create_new_connection(slot); Chris@16: // update map to first connection body in group if needed Chris@16: group_key_type group_key(grouped_slots, group); Chris@16: newConnectionBody->set_group_key(group_key); Chris@16: if(position == at_back) Chris@16: { Chris@16: _shared_state->connection_bodies().push_back(group_key, newConnectionBody); Chris@16: }else // at_front Chris@16: { Chris@16: _shared_state->connection_bodies().push_front(group_key, newConnectionBody); Chris@16: } Chris@16: return connection(newConnectionBody); Chris@16: } Chris@16: Chris@16: // _shared_state is mutable so we can do force_cleanup_connections during a const invocation Chris@16: mutable shared_ptr _shared_state; Chris@16: mutable typename connection_list_type::iterator _garbage_collector_it; Chris@16: // connection list mutex must never be locked when attempting a blocking lock on a slot, Chris@16: // or you could deadlock. Chris@16: mutable mutex_type _mutex; Chris@16: }; Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); Chris@16: } Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION: public signal_base, Chris@16: public detail::BOOST_SIGNALS2_STD_FUNCTIONAL_BASE Chris@16: (typename detail::result_type_wrapper::type) Chris@16: { Chris@16: typedef detail::BOOST_SIGNALS2_SIGNAL_IMPL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: impl_class; Chris@16: public: Chris@16: typedef detail::BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: weak_signal_type; Chris@16: friend class detail::BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: ; Chris@16: Chris@16: typedef SlotFunction slot_function_type; Chris@16: // typedef slotN slot_type; Chris@16: typedef typename impl_class::slot_type slot_type; Chris@16: typedef typename impl_class::extended_slot_function_type extended_slot_function_type; Chris@16: typedef typename impl_class::extended_slot_type extended_slot_type; Chris@16: typedef typename slot_function_type::result_type slot_result_type; Chris@16: typedef Combiner combiner_type; Chris@16: typedef typename impl_class::result_type result_type; Chris@16: typedef Group group_type; Chris@16: typedef GroupCompare group_compare_type; Chris@16: typedef typename impl_class::slot_call_iterator Chris@16: slot_call_iterator; Chris@16: typedef typename mpl::identity::type signature_type; Chris@16: Chris@16: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: Chris@16: // typedef Tn argn_type; Chris@16: #define BOOST_SIGNALS2_MISC_STATEMENT(z, n, data) \ Chris@16: typedef BOOST_PP_CAT(T, BOOST_PP_INC(n)) BOOST_PP_CAT(BOOST_PP_CAT(arg, BOOST_PP_INC(n)), _type); Chris@16: BOOST_PP_REPEAT(BOOST_SIGNALS2_NUM_ARGS, BOOST_SIGNALS2_MISC_STATEMENT, ~) Chris@16: #undef BOOST_SIGNALS2_MISC_STATEMENT Chris@16: #if BOOST_SIGNALS2_NUM_ARGS == 1 Chris@16: typedef arg1_type argument_type; Chris@16: #elif BOOST_SIGNALS2_NUM_ARGS == 2 Chris@16: typedef arg1_type first_argument_type; Chris@16: typedef arg2_type second_argument_type; Chris@16: #endif Chris@16: Chris@16: template class arg : public Chris@16: detail::BOOST_SIGNALS2_PREPROCESSED_ARG_N_TYPE_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: Chris@16: {}; Chris@16: Chris@16: BOOST_STATIC_CONSTANT(int, arity = BOOST_SIGNALS2_NUM_ARGS); Chris@16: Chris@16: #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: Chris@16: template class arg Chris@16: { Chris@16: public: Chris@16: typedef typename detail::variadic_arg_type::type type; Chris@16: }; Chris@16: BOOST_STATIC_CONSTANT(int, arity = sizeof...(Args)); Chris@16: Chris@16: #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: Chris@16: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)(const combiner_type &combiner_arg = combiner_type(), Chris@16: const group_compare_type &group_compare = group_compare_type()): Chris@16: _pimpl(new impl_class(combiner_arg, group_compare)) Chris@16: {}; Chris@16: virtual ~BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)() Chris@16: { Chris@16: } Chris@101: Chris@101: //move support Chris@101: #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) Chris@101: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)( Chris@101: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) && other) Chris@101: { Chris@101: using std::swap; Chris@101: swap(_pimpl, other._pimpl); Chris@101: }; Chris@101: Chris@101: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) & Chris@101: operator=(BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) && rhs) Chris@101: { Chris@101: if(this == &rhs) Chris@101: { Chris@101: return *this; Chris@101: } Chris@101: _pimpl.reset(); Chris@101: using std::swap; Chris@101: swap(_pimpl, rhs._pimpl); Chris@101: return *this; Chris@101: } Chris@101: #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) Chris@101: Chris@16: connection connect(const slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: return (*_pimpl).connect(slot, position); Chris@16: } Chris@16: connection connect(const group_type &group, Chris@16: const slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: return (*_pimpl).connect(group, slot, position); Chris@16: } Chris@16: connection connect_extended(const extended_slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: return (*_pimpl).connect_extended(slot, position); Chris@16: } Chris@16: connection connect_extended(const group_type &group, Chris@16: const extended_slot_type &slot, connect_position position = at_back) Chris@16: { Chris@16: return (*_pimpl).connect_extended(group, slot, position); Chris@16: } Chris@16: void disconnect_all_slots() Chris@16: { Chris@16: (*_pimpl).disconnect_all_slots(); Chris@16: } Chris@16: void disconnect(const group_type &group) Chris@16: { Chris@16: (*_pimpl).disconnect(group); Chris@16: } Chris@16: template Chris@16: void disconnect(const T &slot) Chris@16: { Chris@16: (*_pimpl).disconnect(slot); Chris@16: } Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) Chris@16: { Chris@16: return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: std::size_t num_slots() const Chris@16: { Chris@16: return (*_pimpl).num_slots(); Chris@16: } Chris@16: bool empty() const Chris@16: { Chris@16: return (*_pimpl).empty(); Chris@16: } Chris@16: combiner_type combiner() const Chris@16: { Chris@16: return (*_pimpl).combiner(); Chris@16: } Chris@16: void set_combiner(const combiner_type &combiner_arg) Chris@16: { Chris@16: return (*_pimpl).set_combiner(combiner_arg); Chris@16: } Chris@16: void swap(BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) & other) Chris@16: { Chris@16: using std::swap; Chris@16: swap(_pimpl, other._pimpl); Chris@16: } Chris@16: protected: Chris@16: virtual shared_ptr lock_pimpl() const Chris@16: { Chris@16: return _pimpl; Chris@16: } Chris@16: private: Chris@16: shared_ptr Chris@16: _pimpl; Chris@16: }; Chris@16: Chris@16: #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: // free swap function for signalN classes, findable by ADL Chris@16: template Chris@16: void swap( Chris@16: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) &sig1, Chris@16: BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) &sig2 ) Chris@16: { Chris@16: sig1.swap(sig2); Chris@16: } Chris@16: #endif Chris@16: Chris@16: namespace detail Chris@16: { Chris@16: // wrapper class for storing other signals as slots with automatic lifetime tracking Chris@16: template Chris@16: class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS); Chris@16: Chris@16: template Chris@16: class BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: BOOST_SIGNALS2_SIGNAL_TEMPLATE_SPECIALIZATION Chris@16: { Chris@16: public: Chris@16: typedef typename BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: ::result_type Chris@16: result_type; Chris@16: Chris@16: BOOST_SIGNALS2_WEAK_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: (const BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS) Chris@16: Chris@16: &signal): Chris@16: _weak_pimpl(signal._pimpl) Chris@16: {} Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) Chris@16: { Chris@16: shared_ptr > Chris@16: shared_pimpl(_weak_pimpl.lock()); Chris@16: if(shared_pimpl == 0) boost::throw_exception(expired_slot()); Chris@16: return (*shared_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) const Chris@16: { Chris@16: shared_ptr > Chris@16: shared_pimpl(_weak_pimpl.lock()); Chris@16: if(shared_pimpl == 0) boost::throw_exception(expired_slot()); Chris@16: return (*shared_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); Chris@16: } Chris@16: private: Chris@16: boost::weak_ptr > _weak_pimpl; Chris@16: }; Chris@16: Chris@16: #ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: template Chris@16: class extended_signature: public variadic_extended_signature Chris@16: {}; Chris@16: #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: template Chris@16: class extended_signature; Chris@16: // partial template specialization Chris@16: template Chris@16: class extended_signature Chris@16: { Chris@16: public: Chris@16: // typename function_traits::result_type ( Chris@16: // const boost::signals2::connection &, Chris@16: // typename function_traits::arg1_type, Chris@16: // typename function_traits::arg2_type, Chris@16: // ..., Chris@16: // typename function_traits::argn_type) Chris@16: #define BOOST_SIGNALS2_EXT_SIGNATURE(arity, Signature) \ Chris@16: typename function_traits::result_type ( \ Chris@16: const boost::signals2::connection & BOOST_SIGNALS2_PP_COMMA_IF(BOOST_SIGNALS2_NUM_ARGS) \ Chris@16: BOOST_PP_ENUM(arity, BOOST_SIGNALS2_SIGNATURE_TO_ARGN_TYPE, Signature) ) Chris@16: typedef function function_type; Chris@16: #undef BOOST_SIGNALS2_EXT_SIGNATURE Chris@16: }; Chris@16: Chris@16: template Chris@16: class signalN; Chris@16: // partial template specialization Chris@16: template Chris@16: class signalN Chris@16: { Chris@16: public: Chris@16: typedef BOOST_SIGNALS2_SIGNAL_CLASS_NAME(BOOST_SIGNALS2_NUM_ARGS)< Chris@16: BOOST_SIGNALS2_PORTABLE_SIGNATURE(BOOST_SIGNALS2_NUM_ARGS, Signature), Chris@16: Combiner, Group, Chris@16: GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex> type; Chris@16: }; Chris@16: Chris@16: #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES Chris@16: Chris@16: } // namespace detail Chris@16: } // namespace signals2 Chris@16: } // namespace boost Chris@16: Chris@16: #undef BOOST_SIGNALS2_NUM_ARGS Chris@16: #undef BOOST_SIGNALS2_SIGNAL_TEMPLATE_INSTANTIATION