From 1e0fd01815473b9d20d3144d437e2f7a8a9e680e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 14 May 2012 17:18:48 +0000 Subject: [PATCH] drop boost::signals2 and replace with carl's solution which continues to rely on boost::function and boost::bind but alters two important semantics of signals2: (1) when a Connection object is disconnected, the slot ("functor") associated with the connection is destroyed immediately, unlike signals2 where this is deferred to a subsequent connect/emit call on the signal (2) if one functor called by the signal disconnects another Connection, the functor represented by the Connection will NOT be called during the current signal emission (signals2 copies the slot list at the start of emission and calls everything in the slot list). this change fixes some very nasty crashes apparently caused by boost::signals2 assuming that the memory referenced by a functor remains valid after a disconnect (google will show other developers who had issues with this). git-svn-id: svn://localhost/ardour2/branches/3.0@12265 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/io.h | 2 +- libs/ardour/io.cc | 4 +- libs/gtkmm2ext/gtkmm2ext/binding_proxy.h | 4 +- libs/pbd/base_ui.cc | 3 - libs/pbd/pbd/signal.h | 903 +++++++++++++++++++++++ libs/pbd/pbd/signal.h.py | 214 ++++++ libs/pbd/pbd/signals.h | 143 ++-- libs/pbd/signals.cc | 2 + libs/pbd/test/signals_test.cc | 48 ++ libs/pbd/test/signals_test.h | 4 + libs/pbd/test/xpath.cc | 14 +- 11 files changed, 1267 insertions(+), 74 deletions(-) create mode 100644 libs/pbd/pbd/signal.h create mode 100644 libs/pbd/pbd/signal.h.py diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 2451611b1b..1ba27a7449 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -152,7 +152,7 @@ class IO : public SessionObject, public Latent typedef bool result_type; template - bool operator() (Iter first, Iter last) const { + result_type operator() (Iter first, Iter last) const { bool r = false; while (first != last) { if (*first) { diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a278852e77..26432c66ef 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -256,8 +256,8 @@ IO::remove_port (boost::shared_ptr port, void* src) ChanCount after = before; after.set (port->type(), after.get (port->type()) - 1); - bool const r = PortCountChanging (after); /* EMIT SIGNAL */ - if (r) { + boost::optional const r = PortCountChanging (after); /* EMIT SIGNAL */ + if (r.get_value_or (false)) { return -1; } diff --git a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h index 69d1ebdaad..b541582f6f 100644 --- a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h +++ b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include "pbd/signals.h" namespace PBD { class Controllable; @@ -50,7 +50,7 @@ class BindingProxy : public sigc::trackable boost::shared_ptr controllable; guint bind_button; guint bind_statemask; - boost::signals2::scoped_connection learning_connection; + PBD::ScopedConnection learning_connection; void learning_finished (); bool prompter_hiding (GdkEventAny *); }; diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index c246a50656..14a3ef644d 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -74,7 +74,6 @@ void BaseUI::main_thread () { DEBUG_TRACE (DEBUG::EventLoop, string_compose ("%1: event loop running in thread %2\n", name(), pthread_self())); - std::cerr << string_compose ("%1: event loop running in thread %2\n", name(), pthread_self()); set_event_loop_for_thread (this); thread_init (); _main_loop->get_context()->signal_idle().connect (sigc::mem_fun (*this, &BaseUI::signal_running)); @@ -104,9 +103,7 @@ BaseUI::run () Glib::Mutex::Lock lm (_run_lock); run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true); - std::cerr << "wait for " << name() << " thread to start\n"; _running.wait (_run_lock); - std::cerr << "\tthread now running\n"; } void diff --git a/libs/pbd/pbd/signal.h b/libs/pbd/pbd/signal.h new file mode 100644 index 0000000000..5eb8520a6f --- /dev/null +++ b/libs/pbd/pbd/signal.h @@ -0,0 +1,903 @@ + +/** THIS FILE IS AUTOGENERATED: DO NOT EDIT. + * + * This file is generated by signals.h.py. + */ + +#include +#include +#include +#include +#include +#include +#include "pbd/stacktrace.h" + +namespace PBD { + +class Connection; + +class SignalBase : public boost::enable_shared_from_this +{ +public: + virtual ~SignalBase () {} + virtual void disconnect (boost::shared_ptr) = 0; + +protected: + boost::mutex _mutex; +}; + +class Connection : public boost::enable_shared_from_this +{ +public: + Connection (boost::shared_ptr b) : _signal (b) {} + + void disconnect () + { + if (_signal) { + _signal->disconnect (shared_from_this ()); + } + } + +private: + boost::shared_ptr _signal; +}; + +template +class 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; + } +}; + +template > +class SimpleSignal0 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit () + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)()); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal0); + } + +private: + + friend class Connection; + + SimpleSignal0 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template <> +class SimpleSignal0 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit () + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)) + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { +#else + for (Slots::iterator i = s.begin(); i != s.end(); ++i) { +#endif + + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal0); + } + +private: + + friend class Connection; + + SimpleSignal0 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template > +class SimpleSignal1 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit (A1 a1) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a1)); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal1); + } + +private: + + friend class Connection; + + SimpleSignal1 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template +class SimpleSignal1 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit (A1 a1) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a1); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal1); + } + +private: + + friend class Connection; + + SimpleSignal1 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template > +class SimpleSignal2 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit (A1 a1, A2 a2) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a1, a2)); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal2); + } + +private: + + friend class Connection; + + SimpleSignal2 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template +class SimpleSignal2 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit (A1 a1, A2 a2) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a1, a2); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal2); + } + +private: + + friend class Connection; + + SimpleSignal2 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template > +class SimpleSignal3 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit (A1 a1, A2 a2, A3 a3) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a1, a2, a3)); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal3); + } + +private: + + friend class Connection; + + SimpleSignal3 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template +class SimpleSignal3 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit (A1 a1, A2 a2, A3 a3) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a1, a2, a3); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal3); + } + +private: + + friend class Connection; + + SimpleSignal3 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template > +class SimpleSignal4 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit (A1 a1, A2 a2, A3 a3, A4 a4) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a1, a2, a3, a4)); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal4); + } + +private: + + friend class Connection; + + SimpleSignal4 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template +class SimpleSignal4 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit (A1 a1, A2 a2, A3 a3, A4 a4) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a1, a2, a3, a4); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal4); + } + +private: + + friend class Connection; + + SimpleSignal4 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template > +class SimpleSignal5 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef boost::optional result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + typename C::result_type emit (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + std::list r; + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a1, a2, a3, a4, a5)); + } + } + C c; + return c (r.begin(), r.end()); + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal5); + } + +private: + + friend class Connection; + + SimpleSignal5 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +template +class SimpleSignal5 : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + typedef void result_type; +private: + + + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } + + void emit (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) + { + Slots s; + { + boost::mutex::scoped_lock lm (_mutex); + s = _slots; + } + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { + + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a1, a2, a3, a4, a5); + } + } + } + + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } + + static boost::shared_ptr > create () + { + return boost::shared_ptr > (new SimpleSignal5); + } + +private: + + friend class Connection; + + SimpleSignal5 () {} + + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; + +} diff --git a/libs/pbd/pbd/signal.h.py b/libs/pbd/pbd/signal.h.py new file mode 100644 index 0000000000..70a1a2d88c --- /dev/null +++ b/libs/pbd/pbd/signal.h.py @@ -0,0 +1,214 @@ +#!/usr/bin/python + +import sys + +if len(sys.argv) < 2: + print 'Syntax: %s ' % sys.argv[0] + sys.exit(1) + +f = open(sys.argv[1], 'w') + +print >>f,""" +/** THIS FILE IS AUTOGENERATED: DO NOT EDIT. + * + * This file is generated by signals.h.py. + */ + +#include +#include +#include +#include +#include +#include +#include "pbd/stacktrace.h" + +namespace PBD { + +class Connection; + +class SignalBase : public boost::enable_shared_from_this +{ +public: + virtual ~SignalBase () {} + virtual void disconnect (boost::shared_ptr) = 0; + +protected: + boost::mutex _mutex; +}; + +class Connection : public boost::enable_shared_from_this +{ +public: + Connection (boost::shared_ptr b) : _signal (b) {} + + void disconnect () + { + if (_signal) { + _signal->disconnect (shared_from_this ()); + } + } + +private: + boost::shared_ptr _signal; +}; + +template +class 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; + } +}; +""" + +def comma_separated(n, prefix = ""): + r = "" + for i in range(0, len(n)): + if i > 0: + r += ", " + r += "%s%s" % (prefix, n[i]) + return r + +def simple_signal(f, n, v): + + An = [] + for i in range(0, n): + An.append("A%d" % (i + 1)) + + if v: + print >>f,"template <%s>" % comma_separated(An, "typename ") + print >>f,"class SimpleSignal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An)) + else: + print >>f,"template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue "], "typename ") + print >>f,"class SimpleSignal%d : public SignalBase" % n + + print >>f,"{" + print >>f,"public:" + print >>f,"" + if v: + print >>f,"\ttypedef boost::function slot_function_type;" % comma_separated(An) + print >>f,"\ttypedef void result_type;" + else: + print >>f,"\ttypedef boost::function slot_function_type;" % comma_separated(An) + print >>f,"\ttypedef boost::optional result_type;" + + print >>f,"private:" + print >>f,"" + + print >>f,""" + typedef std::map, slot_function_type> Slots; + Slots _slots; +""" + + print >>f,"public:" + print >>f,"" + print >>f,""" + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } +""" + + Anan = [] + for a in An: + Anan.append('%s %s' % (a, a.lower())) + + an = [] + for a in An: + an.append(a.lower()) + + if v: + print >>f,"\tvoid emit (%s)" % comma_separated(Anan) + else: + print >>f,"\ttypename C::result_type emit (%s)" % comma_separated(Anan) + print >>f,"\t{" + print >>f,"\t\tSlots s;" + print >>f,"\t\t{" + print >>f,"\t\t\tboost::mutex::scoped_lock lm (_mutex);" + print >>f,"\t\t\ts = _slots;" + print >>f,"\t\t}" + if not v: + print >>f,"\t\tstd::list r;" + if n == 0 and v: + print >>f,""" +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)) + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { +#else + for (Slots::iterator i = s.begin(); i != s.end(); ++i) { +#endif +""" + else: + print >>f,"\t\tfor (typename Slots::iterator i = s.begin(); i != s.end(); ++i) {" + + print >>f,""" + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) {""" + if v: + print >>f,"\t\t\t\t(i->second)(%s);" % comma_separated(an) + else: + print >>f,"\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an) + print >>f,"\t\t\t}" + print >>f,"\t\t}" + if not v: + print >>f,"\t\tC c;" + print >>f,"\t\treturn c (r.begin(), r.end());" + print >>f,"\t}" + + print >>f,""" + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } +""" + + + if v: + tp = comma_separated(["void"] + An) + else: + tp = comma_separated(["R"] + An + ["C"]) + + print >>f,"\tstatic boost::shared_ptr > create ()" % (n, tp) + print >>f,"\t{" + print >>f,"\t\treturn boost::shared_ptr > (new SimpleSignal%d<%s>);" % (n, tp, n, tp) + print >>f,"\t}" + + print >>f,"" + print >>f,"private:" + print >>f,"" + print >>f,"\tfriend class Connection;" + print >>f,"" + print >>f,"\tSimpleSignal%d () {}" % n + + print >>f,""" + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; +""" + + +for i in range(0, 6): + simple_signal(f, i, False) + simple_signal(f, i, True) + +print >>f,"}" diff --git a/libs/pbd/pbd/signals.h b/libs/pbd/pbd/signals.h index cfada19879..7cdbcef414 100644 --- a/libs/pbd/pbd/signals.h +++ b/libs/pbd/pbd/signals.h @@ -23,23 +23,47 @@ #include #include -#include #include #include #include #include "pbd/event_loop.h" +#include "pbd/signal.h" namespace PBD { -typedef boost::signals2::connection UnscopedConnection; -typedef boost::signals2::scoped_connection ScopedConnection; +typedef boost::shared_ptr UnscopedConnection; +class ScopedConnection +{ +public: + ScopedConnection () {} + ScopedConnection (UnscopedConnection c) : _c (c) {} + ~ScopedConnection () { + disconnect (); + } + void disconnect () + { + if (_c) { + _c->disconnect (); + } + } + + ScopedConnection& operator= (UnscopedConnection const & o) + { + _c = o; + return *this; + } + +private: + UnscopedConnection _c; +}; + class ScopedConnectionList : public boost::noncopyable { public: ScopedConnectionList(); - ~ScopedConnectionList (); + virtual ~ScopedConnectionList (); void add_connection (const UnscopedConnection& c); void drop_connections (); @@ -73,8 +97,9 @@ class ScopedConnectionList : public boost::noncopyable template class Signal0 { public: - Signal0 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal0 SignalType; + + Signal0 () : _signal (SignalType::create ()) {} /** Arrange for @a slot to be executed whenever this signal is emitted. Store the connection that represents this arrangement in @a c. @@ -85,7 +110,7 @@ public: void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } /** Arrange for @a slot to be executed whenever this signal is emitted. @@ -97,7 +122,7 @@ public: void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } /** Arrange for @a slot to be executed in the context of @a event_loop @@ -132,7 +157,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot))); + clist.add_connection (_signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot))); } /** See notes for the ScopedConnectionList variant of this function. This @@ -147,7 +172,7 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)); + 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 @@ -156,33 +181,33 @@ public: */ typename SignalType::result_type operator()() { - return _signal (); + return _signal->emit (); } /** Return true if there is nothing connected to this signal, false * otherwise. */ - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; -template > +template > class Signal1 { public: - Signal1 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal1 SignalType; + Signal1 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) { @@ -196,7 +221,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1))); } void connect (ScopedConnection& c, @@ -206,34 +231,34 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)); + c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1)); } typename SignalType::result_type operator()(A arg1) { - return _signal (arg1); + return _signal->emit (arg1); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal2 { public: - Signal2 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal2 SignalType; + Signal2 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -249,7 +274,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2))); } void connect (ScopedConnection& c, @@ -259,33 +284,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)); + 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); + return _signal->emit (arg1, arg2); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal3 { public: - Signal3 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal3 SignalType; + Signal3 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -301,7 +326,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); } void connect (ScopedConnection& c, @@ -311,33 +336,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); + 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); + return _signal->emit (arg1, arg2, arg3); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal4 { public: - Signal4 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal4 SignalType; + Signal4 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -353,7 +378,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); } void connect (ScopedConnection& c, @@ -363,33 +388,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); + 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); + return _signal->emit (arg1, arg2, arg3, arg4); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal5 { public: - Signal5 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal5 SignalType; + Signal5 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -405,7 +430,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); } void connect (ScopedConnection& c, @@ -415,17 +440,17 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); + 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); + return _signal->emit (arg1, arg2, arg3, arg4, arg5); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; } /* namespace */ diff --git a/libs/pbd/signals.cc b/libs/pbd/signals.cc index 62cabff091..b37c6e211a 100644 --- a/libs/pbd/signals.cc +++ b/libs/pbd/signals.cc @@ -18,6 +18,7 @@ */ #include "pbd/signals.h" +#include "pbd/demangle.h" using namespace PBD; @@ -29,6 +30,7 @@ ScopedConnectionList::ScopedConnectionList() ScopedConnectionList::~ScopedConnectionList() { + std::cout << "~ScopedConnectionList " << this << " " << PBD::demangled_name (*this) << "\n"; drop_connections (); } diff --git a/libs/pbd/test/signals_test.cc b/libs/pbd/test/signals_test.cc index f635ea63da..9ca1bf536b 100644 --- a/libs/pbd/test/signals_test.cc +++ b/libs/pbd/test/signals_test.cc @@ -1,6 +1,8 @@ #include "signals_test.h" #include "pbd/signals.h" +using namespace std; + CPPUNIT_TEST_SUITE_REGISTRATION (SignalsTest); class Emitter { @@ -12,10 +14,30 @@ public: PBD::Signal0 Fred; }; +static int N = 0; + void receiver () { + ++N; +} +void +SignalsTest::testEmission () +{ + Emitter* e = new Emitter; + PBD::ScopedConnection c; + e->Fred.connect_same_thread (c, boost::bind (&receiver)); + + N = 0; + e->emit (); + e->emit (); + CPPUNIT_ASSERT_EQUAL (2, N); + + e->Fred.connect_same_thread (c, boost::bind (&receiver)); + N = 0; + e->emit (); + CPPUNIT_ASSERT_EQUAL (2, N); } void @@ -31,3 +53,29 @@ SignalsTest::testDestruction () CPPUNIT_ASSERT (true); } +class Receiver : public PBD::ScopedConnectionList +{ +public: + Receiver (Emitter* e) { + e->Fred.connect_same_thread (*this, boost::bind (&Receiver::receiver, this)); + } + + void receiver () { + cout << "Receiver::receiver\n"; + ++N; + } +}; + +void +SignalsTest::testScopedConnectionList () +{ + Emitter* e = new Emitter; + Receiver* r = new Receiver (e); + + N = 0; + e->emit (); + delete r; + e->emit (); + + CPPUNIT_ASSERT_EQUAL (1, N); +} diff --git a/libs/pbd/test/signals_test.h b/libs/pbd/test/signals_test.h index 9a66564705..8beb02ab1e 100644 --- a/libs/pbd/test/signals_test.h +++ b/libs/pbd/test/signals_test.h @@ -4,9 +4,13 @@ class SignalsTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE (SignalsTest); + CPPUNIT_TEST (testEmission); CPPUNIT_TEST (testDestruction); + CPPUNIT_TEST (testScopedConnectionList); CPPUNIT_TEST_SUITE_END (); public: + void testEmission (); void testDestruction (); + void testScopedConnectionList (); }; diff --git a/libs/pbd/test/xpath.cc b/libs/pbd/test/xpath.cc index 15062c6132..f0e976eabd 100644 --- a/libs/pbd/test/xpath.cc +++ b/libs/pbd/test/xpath.cc @@ -13,12 +13,12 @@ static string const prefix = "../libs/pbd/test/"; void XPathTest::testMisc () { - cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl; +// cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl; XMLTree doc(prefix + "RosegardenPatchFile.xml"); // "//bank" gives as last element an empty element libxml bug???? boost::shared_ptr result = doc.find("//bank[@name]"); - cout << "Found " << result->size() << " banks" << endl; +// cout << "Found " << result->size() << " banks" << endl; assert(result->size() == 8); // int counter = 1; for(XMLSharedNodeList::const_iterator i = result->begin(); i != result->end(); ++i) { @@ -31,7 +31,7 @@ XPathTest::testMisc () } } - cout << endl << endl << "Test 2: RosegardenPatchFile.xml: Find all programs whose program name contains 'Latin'" << endl; +// cout << endl << endl << "Test 2: RosegardenPatchFile.xml: Find all programs whose program name contains 'Latin'" << endl; result = doc.find("/rosegarden-data/studio/device/bank/program[contains(@name, 'Latin')]"); assert(result->size() == 5); @@ -53,7 +53,7 @@ XPathTest::testMisc () // "' with id: " << (*i)->property("id")->value() << endl; } - cout << endl << endl << "Test 4: TestSession.ardour: Find all elements with an 'id' and 'name' attribute" << endl; +// cout << endl << endl << "Test 4: TestSession.ardour: Find all elements with an 'id' and 'name' attribute" << endl; result = doc2.find("//*[@id and @name]"); @@ -65,7 +65,7 @@ XPathTest::testMisc () // "' and name: " << (*i)->property("name")->value() << endl; } - cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl; +// cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl; // We have to allocate a new document here, or we get segfaults XMLTree doc3(prefix + "ProtoolsPatchFile.midnam"); @@ -81,7 +81,7 @@ XPathTest::testMisc () } } - cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Find attribute nodes" << endl; +// cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Find attribute nodes" << endl; result = doc3.find("//@Value"); for(XMLSharedNodeList::const_iterator i = result->begin(); i != result->end(); ++i) { @@ -90,7 +90,7 @@ XPathTest::testMisc () // << " value: " << node->attribute_value() << endl; } - cout << endl << endl << "Test 6: ProtoolsPatchFile.midnam: Find available channels on 'Name Set 1'" << endl; +// cout << endl << endl << "Test 6: ProtoolsPatchFile.midnam: Find available channels on 'Name Set 1'" << endl; result = doc3.find( "//ChannelNameSet[@Name = 'Name Set 1']//AvailableChannel[@Available = 'true']/@Channel");