X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fpbd%2Fsignals.h;h=df4c9cef29331c5d3b0477143d932e0c7fc4df8e;hb=d19ec8ba46274bd41c251f81e63503183db15f08;hp=7e51ff2a2789cc495bd8326f82376261aa5676f2;hpb=73161988bb80813ae68e4ec9ad676d54169bcb4c;p=ardour.git diff --git a/libs/pbd/pbd/signals.h b/libs/pbd/pbd/signals.h index 7e51ff2a27..df4c9cef29 100644 --- a/libs/pbd/pbd/signals.h +++ b/libs/pbd/pbd/signals.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2009 Paul Davis + Copyright (C) 2009-2012 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,27 +20,159 @@ #ifndef __pbd_signals_h__ #define __pbd_signals_h__ +#include + #include -#include +#include + +#ifdef nil +#undef nil +#endif + +#include -#include #include #include #include +#include +#include +#include +#include "pbd/libpbd_visibility.h" #include "pbd/event_loop.h" +#ifndef NDEBUG +#define DEBUG_PBD_SIGNAL_CONNECTIONS +#endif + +#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS +#include "pbd/stacktrace.h" +#include +#endif + namespace PBD { -typedef boost::signals2::connection UnscopedConnection; -typedef boost::signals2::scoped_connection ScopedConnection; +class LIBPBD_API Connection; -class ScopedConnectionList : public boost::noncopyable +class LIBPBD_API SignalBase +{ +public: + SignalBase () +#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + : _debug_connection (false) +#endif + {} + virtual ~SignalBase () {} + virtual void disconnect (boost::shared_ptr) = 0; +#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + void set_debug_connection (bool yn) { _debug_connection = yn; } +#endif + +protected: + mutable Glib::Threads::Mutex _mutex; +#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + bool _debug_connection; +#endif +}; + +class LIBPBD_API Connection : public boost::enable_shared_from_this +{ +public: + Connection (SignalBase* b, PBD::EventLoop::InvalidationRecord* ir) : _signal (b), _invalidation_record (ir) + { + if (_invalidation_record) { + _invalidation_record->ref (); + } + } + + void disconnect () + { + Glib::Threads::Mutex::Lock lm (_mutex); + if (_signal) { + _signal->disconnect (shared_from_this ()); + _signal = 0; + } + } + + void disconnected () + { + if (_invalidation_record) { + _invalidation_record->unref (); + } + } + + void signal_going_away () + { + Glib::Threads::Mutex::Lock lm (_mutex); + if (_invalidation_record) { + _invalidation_record->unref (); + } + _signal = 0; + } + +private: + Glib::Threads::Mutex _mutex; + SignalBase* _signal; + PBD::EventLoop::InvalidationRecord* _invalidation_record; +}; + +template +class /*LIBPBD_API*/ OptionalLastValue +{ +public: + typedef boost::optional result_type; + + template + result_type operator() (Iter first, Iter last) const { + result_type r; + while (first != last) { + r = *first; + ++first; + } + + return r; + } +}; + +typedef boost::shared_ptr UnscopedConnection; + +class LIBPBD_API ScopedConnection +{ +public: + ScopedConnection () {} + ScopedConnection (UnscopedConnection c) : _c (c) {} + ~ScopedConnection () { + disconnect (); + } + + void disconnect () + { + if (_c) { + _c->disconnect (); + } + } + + ScopedConnection& operator= (UnscopedConnection const & o) + { + if (_c == o) { + return *this; + } + + disconnect (); + _c = o; + return *this; + } + +private: + UnscopedConnection _c; +}; + +class LIBPBD_API ScopedConnectionList : public boost::noncopyable { public: ScopedConnectionList(); - ~ScopedConnectionList (); - + virtual ~ScopedConnectionList (); + void add_connection (const UnscopedConnection& c); void drop_connections (); @@ -48,388 +180,25 @@ class ScopedConnectionList : public boost::noncopyable /* this class is not copyable */ ScopedConnectionList(const ScopedConnectionList&); - /* this lock is shared by all instances of a ScopedConnectionList. - We do not want one mutex per list, and since we only need the lock - when adding or dropping connections, which are generally occuring - in object creation and UI operations, the contention on this - lock is low and not of significant consequence. Even though - boost::signals2 is thread-safe, this additional list of + /* Even though our signals code is thread-safe, this additional list of scoped connections needs to be protected in 2 cases: (1) (unlikely) we make a connection involving a callback on the - same object from 2 threads. (wouldn't that just be appalling + same object from 2 threads. (wouldn't that just be appalling programming style?) - + (2) where we are dropping connections in one thread and adding one from another. */ - static Glib::StaticMutex _lock; + Glib::Threads::Mutex _lock; typedef std::list ConnectionList; ConnectionList _list; }; -template -class Signal0 { -public: - Signal0 () {} - typedef boost::signals2::signal SignalType; - - /** Arrange for @a slot to be executed in the context of @a event_loop - whenever this signal is emitted. Store the connection that represents - this arrangement to @a c. - - NOTE: @a slot will be executed in the same thread that the signal is - emitted in. - */ - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - /** Arrange for @a slot to be executed in the context of @a event_loop - whenever this signal is emitted. Add the connection that represents - this arrangement to @a clist. - - NOTE: @a slot will be executed in the same thread that the signal is - emitted in. - */ - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - /** Arrange for @a slot to be executed in the context of @a event_loop - whenever this signal is emitted. Add the connection that represents - this arrangement to @a clist. - - If the event loop/thread in which @a slot will be executed will - outlive the lifetime of any object referenced in @a slot, - then an InvalidationRecord should be passed, allowing - any request sent to the @a event_loop and not executed - before the object is destroyed to be marked invalid. - - "outliving the lifetime" doesn't have a specific, detailed meaning, - but is best illustrated by two contrasting examples: - - 1) the main GUI event loop/thread - this will outlive more or - less all objects in the application, and thus when arranging for - @a slot to be called in that context, an invalidation record is - highly advisable. - - 2) a secondary event loop/thread which will be destroyed along - with the objects that are typically referenced by @a slot. - Assuming that the event loop is stopped before the objects are - destroyed, there is no reason to pass in an invalidation record, - and MISSING_INVALIDATOR may be used. - */ - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot))); - } - - /** See notes for the ScopedConnectionList variant of this function. This - * differs in that it stores the connection to the signal in a single - * ScopedConnection rather than a ScopedConnectionList. - */ - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)); - } - - /** Emit this signal. This will cause all slots connected to it be executed - in the order that they were connected (cross-thread issues may alter - the precise execution time of cross-thread slots). - */ - - typename SignalType::result_type operator()() { - return _signal (); - } - - /** Return true if there is nothing connected to this signal, false - * otherwise. - */ - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; - -template > -class Signal1 { -public: - Signal1 () {} - typedef boost::signals2::signal SignalType; - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - static void compositor (typename boost::function f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) { - event_loop->call_slot (ir, boost::bind (f, arg)); - } - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1))); - } - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)); - - } - - typename SignalType::result_type operator()(A arg1) { - return _signal (arg1); - } - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; - -template -class Signal2 { -public: - Signal2 () {} - typedef boost::signals2::signal SignalType; - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - static void compositor (typename boost::function f, PBD::EventLoop* event_loop, - EventLoop::InvalidationRecord* ir, - A1 arg1, A2 arg2) { - event_loop->call_slot (ir, boost::bind (f, arg1, arg2)); - } - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2))); - } - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)); - } - - typename SignalType::result_type operator()(A1 arg1, A2 arg2) { - return _signal (arg1, arg2); - } - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; +#include "pbd/signals_generated.h" -template -class Signal3 { -public: - Signal3 () {} - typedef boost::signals2::signal SignalType; - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - static void compositor (typename boost::function f, PBD::EventLoop* event_loop, - EventLoop::InvalidationRecord* ir, - A1 arg1, A2 arg2, A3 arg3) { - event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3)); - } - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); - } - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); - } - - typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) { - return _signal (arg1, arg2, arg3); - } - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; - -template -class Signal4 { -public: - Signal4 () {} - typedef boost::signals2::signal SignalType; - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - static void compositor (typename boost::function f, PBD::EventLoop* event_loop, - EventLoop::InvalidationRecord* ir, - A1 arg1, A2 arg2, A3 arg3, A4 arg4) { - event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4)); - } - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); - } - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); - } - - typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) { - return _signal (arg1, arg2, arg3, arg4); - } - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; - -template -class Signal5 { -public: - Signal5 () {} - typedef boost::signals2::signal SignalType; - - void connect_same_thread (ScopedConnectionList& clist, - const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); - } - - void connect_same_thread (ScopedConnection& c, - const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); - } - - static void compositor (typename boost::function f, PBD::EventLoop* event_loop, - EventLoop::InvalidationRecord* ir, - A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) { - event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4, arg5)); - } - - void connect (ScopedConnectionList& clist, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); - } - - void connect (ScopedConnection& c, - PBD::EventLoop::InvalidationRecord* ir, - const typename SignalType::slot_function_type& slot, - PBD::EventLoop* event_loop) { - if (ir) { - ir->event_loop = event_loop; - } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); - } - - typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) { - return _signal (arg1, arg2, arg3, arg4, arg5); - } - - bool empty() const { return _signal.empty(); } - -private: - SignalType _signal; -}; - } /* namespace */ #endif /* __pbd_signals_h__ */