From 16dd8528c8d52f6c9abdb21b77759c323ebb4a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Dom=C3=ADnguez?= Date: Sun, 18 Aug 2024 19:48:21 +0200 Subject: [PATCH] Make SignalN template variadic --- libs/pbd/pbd/signals.h | 556 ++++++++++++++++++++++++++++++++++++++-- libs/pbd/pbd/signals.py | 338 ------------------------ libs/pbd/wscript | 4 - 3 files changed, 537 insertions(+), 361 deletions(-) delete mode 100644 libs/pbd/pbd/signals.py diff --git a/libs/pbd/pbd/signals.h b/libs/pbd/pbd/signals.h index b9d8e3a4aa..8fbb921fb0 100644 --- a/libs/pbd/pbd/signals.h +++ b/libs/pbd/pbd/signals.h @@ -57,6 +57,10 @@ namespace PBD { class LIBPBD_API Connection; +class LIBPBD_API ScopedConnection; + +class LIBPBD_API ScopedConnectionList; + class LIBPBD_API SignalBase { public: @@ -80,6 +84,202 @@ protected: #endif }; +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; + } +}; + +template +class SignalWithCombiner; + +template +class SignalWithCombiner : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + +private: + + /** The slots that this signal will call on emission */ + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + static void compositor (typename boost::function f, + EventLoop* event_loop, + EventLoop::InvalidationRecord* ir, A... a); + + ~SignalWithCombiner (); + + void connect_same_thread (ScopedConnection& c, const slot_function_type& slot); + void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot); + void connect (ScopedConnectionList& clist, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop); + void connect (ScopedConnection& c, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop); + + typename Combiner::result_type operator() (A... a); + + bool empty () const { + Glib::Threads::Mutex::Lock lm (_mutex); + return _slots.empty (); + } + + size_t size () const { + Glib::Threads::Mutex::Lock lm (_mutex); + return _slots.size (); + } + +private: + + friend class Connection; + + std::shared_ptr _connect (PBD::EventLoop::InvalidationRecord* ir, slot_function_type f); + void disconnect (std::shared_ptr c); + +}; + +template +class SignalWithCombiner : public SignalBase +{ +public: + + typedef boost::function slot_function_type; + +private: + + /** The slots that this signal will call on emission */ + typedef std::map, slot_function_type> Slots; + Slots _slots; + +public: + + static void compositor (typename boost::function f, + EventLoop* event_loop, + EventLoop::InvalidationRecord* ir, A... a); + + ~SignalWithCombiner (); + + void connect_same_thread (ScopedConnection& c, const slot_function_type& slot); + void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot); + void connect (ScopedConnectionList& clist, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop); + void connect (ScopedConnection& c, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop); + void operator() (A... a); + + bool empty () const { + Glib::Threads::Mutex::Lock lm (_mutex); + return _slots.empty (); + } + + size_t size () const { + Glib::Threads::Mutex::Lock lm (_mutex); + return _slots.size (); + } + +private: + + friend class Connection; + + std::shared_ptr _connect (PBD::EventLoop::InvalidationRecord* ir, + slot_function_type f); + void disconnect (std::shared_ptr c); + +}; + +template +using DefaultCombiner = OptionalLastValue; + +template +class Signal; + +template +class Signal : public SignalWithCombiner, R(A...)> {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal0 : public SignalWithCombiner {}; + +template +class Signal0 : public Signal {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal1 : public SignalWithCombiner {}; + +template +class Signal1 : public Signal {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal2 : public SignalWithCombiner {}; + +template +class Signal2 : public Signal {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal3 : public SignalWithCombiner {}; + +template +class Signal3 : public Signal {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal4 : public SignalWithCombiner {}; + +template +class Signal4 : public Signal {}; + +template > +class +#ifndef NDEBUG +[[deprecated("Use Signal or SignalWithCombiner if you need a specific combiner instead.")]] +#endif +Signal5 : public SignalWithCombiner {}; + +template +class Signal5 : public Signal {}; + class LIBPBD_API Connection : public std::enable_shared_from_this { public: @@ -139,24 +339,6 @@ private: 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 std::shared_ptr UnscopedConnection; class LIBPBD_API ScopedConnection @@ -222,7 +404,343 @@ class LIBPBD_API ScopedConnectionList : public boost::noncopyable ConnectionList _scoped_connection_list; }; -#include "pbd/signals_generated.h" +template +void +SignalWithCombiner::compositor (typename boost::function f, + EventLoop* event_loop, + EventLoop::InvalidationRecord* ir, A... a) +{ + event_loop->call_slot (ir, boost::bind (f, a...)); +} + +template +void +SignalWithCombiner::compositor (typename boost::function f, + EventLoop* event_loop, + EventLoop::InvalidationRecord* ir, A... a) +{ + event_loop->call_slot (ir, boost::bind (f, a...)); +} + +template +SignalWithCombiner::~SignalWithCombiner () +{ + _in_dtor.store (true, std::memory_order_release); + Glib::Threads::Mutex::Lock lm (_mutex); + /* Tell our connection objects that we are going away, so they don't try to call us */ + for (typename Slots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) { + i->first->signal_going_away (); + } +} + +template +SignalWithCombiner::~SignalWithCombiner () +{ + _in_dtor.store (true, std::memory_order_release); + Glib::Threads::Mutex::Lock lm (_mutex); + /* Tell our connection objects that we are going away, so they don't try to call us */ + for (typename Slots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) { + i->first->signal_going_away (); + } +} + +/** Arrange for @a slot to be executed whenever this signal is emitted. + * Store the connection that represents this arrangement in @a c. + * + * NOTE: @a slot will be executed in the same thread that the signal is + * emitted in. + */ + +template +void +SignalWithCombiner::connect_same_thread (ScopedConnection& c, + const slot_function_type& slot) +{ + c = _connect (0, slot); +} + +template +void +SignalWithCombiner::connect_same_thread (ScopedConnection& c, + const slot_function_type& slot) +{ + c = _connect (0, slot); +} + +/** Arrange for @a slot to be executed 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. + */ + +template +void +SignalWithCombiner::connect_same_thread (ScopedConnectionList& clist, + const slot_function_type& slot) +{ + clist.add_connection (_connect (0, slot)); +} + +template +void +SignalWithCombiner::connect_same_thread (ScopedConnectionList& clist, + const slot_function_type& slot) +{ + clist.add_connection (_connect (0, 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. + */ + +template +void +SignalWithCombiner::connect (ScopedConnectionList& clist, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop) +{ + if (ir) { + ir->event_loop = event_loop; + } + + clist.add_connection (_connect (ir, [slot, event_loop, ir](A... a) { + return compositor(slot, event_loop, ir, a...); + })); +} + +template +void +SignalWithCombiner::connect (ScopedConnectionList& clist, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop) +{ + if (ir) { + ir->event_loop = event_loop; + } + + clist.add_connection (_connect (ir, [slot, event_loop, ir](A... a) { + return compositor(slot, event_loop, ir, a...); + })); +} + +/** 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. + */ + +template +void +SignalWithCombiner::connect (ScopedConnection& c, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop) +{ + if (ir) { + ir->event_loop = event_loop; + } + + c = _connect (ir, [slot, event_loop, ir](A... a) { + return compositor(slot, event_loop, ir, a...); + }); +} + +template +void +SignalWithCombiner::connect (ScopedConnection& c, + PBD::EventLoop::InvalidationRecord* ir, + const slot_function_type& slot, + PBD::EventLoop* event_loop) +{ + if (ir) { + ir->event_loop = event_loop; + } + + c = _connect (ir, [slot, event_loop, ir](A... a) { + return compositor(slot, event_loop, ir, a...); + }); +} + +/** 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). + */ + +template +typename Combiner::result_type +SignalWithCombiner::operator() (A... a) +{ + /* First, take a copy of our list of slots as it is now */ + + Slots s; + { + Glib::Threads::Mutex::Lock lm (_mutex); + s = _slots; + } + + std::list r; + for (typename Slots::const_iterator i = s.begin(); i != s.end(); ++i) { + + /* We may have just called a slot, and this may have resulted in + * disconnection of other slots from us. The list copy means that + * this won't cause any problems with invalidated iterators, but we + * must check to see if the slot we are about to call is still on the list. + */ + bool still_there = false; + { + Glib::Threads::Mutex::Lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + r.push_back ((i->second)(a...)); + } + } + + /* Call our combiner to do whatever is required to the result values */ + Combiner c; + return c (r.begin(), r.end()); +} + +template +void +SignalWithCombiner::operator() (A... a) +{ + /* First, take a copy of our list of slots as it is now */ + + Slots s; + { + Glib::Threads::Mutex::Lock lm (_mutex); + s = _slots; + } + + for (typename Slots::const_iterator i = s.begin(); i != s.end(); ++i) { + + /* We may have just called a slot, and this may have resulted in + * disconnection of other slots from us. The list copy means that + * this won't cause any problems with invalidated iterators, but we + * must check to see if the slot we are about to call is still on the list. + */ + bool still_there = false; + { + Glib::Threads::Mutex::Lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) { + (i->second)(a...); + } + } +} + +template +std::shared_ptr +SignalWithCombiner::_connect (PBD::EventLoop::InvalidationRecord* ir, + slot_function_type f) +{ + std::shared_ptr c (new Connection (this, ir)); + Glib::Threads::Mutex::Lock lm (_mutex); + _slots[c] = f; + #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + if (_debug_connection) { + std::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl; + stacktrace (std::cerr, 10); + } + #endif + return c; +} + +template +std::shared_ptr +SignalWithCombiner::_connect (PBD::EventLoop::InvalidationRecord* ir, + slot_function_type f) +{ + std::shared_ptr c (new Connection (this, ir)); + Glib::Threads::Mutex::Lock lm (_mutex); + _slots[c] = f; + #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + if (_debug_connection) { + std::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl; + stacktrace (std::cerr, 10); + } + #endif + return c; +} + +template +void +SignalWithCombiner::disconnect (std::shared_ptr c) +{ + /* ~ScopedConnection can call this concurrently with our d'tor */ + Glib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK); + while (!lm.locked()) { + if (_in_dtor.load (std::memory_order_acquire)) { + /* d'tor signal_going_away() took care of everything already */ + return; + } + /* Spin */ + lm.try_acquire (); + } + _slots.erase (c); + lm.release (); + + c->disconnected (); + #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + if (_debug_connection) { + std::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl; + stacktrace (std::cerr, 10); + } + #endif +} + +template +void +SignalWithCombiner::disconnect (std::shared_ptr c) +{ + /* ~ScopedConnection can call this concurrently with our d'tor */ + Glib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK); + while (!lm.locked()) { + if (_in_dtor.load (std::memory_order_acquire)) { + /* d'tor signal_going_away() took care of everything already */ + return; + } + /* Spin */ + lm.try_acquire (); + } + _slots.erase (c); + lm.release (); + + c->disconnected (); + #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS + if (_debug_connection) { + std::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl; + stacktrace (std::cerr, 10); + } + #endif +} } /* namespace */ diff --git a/libs/pbd/pbd/signals.py b/libs/pbd/pbd/signals.py deleted file mode 100644 index 6535ddde2e..0000000000 --- a/libs/pbd/pbd/signals.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/python -# -# 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -# -# This file generates the header signals_generated.h, which -# will be put in build/libs/pbd/pbd by waf. -# -# It is probably easier to read build/libs/pbd/pbd/signals_generated.h -# than this if you want to read the code! -# - -from __future__ import print_function -import sys - -if len(sys.argv) < 2: - print('Syntax: %s ' % sys.argv[0]) - sys.exit(1) - -f = open(sys.argv[1], 'w') - -print("/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n", file=f) - -# Produce a comma-separated string from a list of substrings, -# giving an optional prefix to each substring -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 - -# Generate one SignalN class definition -# @param f File to write to -# @param n Number of parameters -# @param v True to specialize the template for a void return type -def signal(f, n, v): - - # The parameters in the form A1, A2, A3, ... - An = [] - for i in range(0, n): - An.append("A%d" % (i + 1)) - - # The parameters in the form A1 a1, A2 a2, A3 a3, ... - Anan = [] - for a in An: - Anan.append('%s %s' % (a, a.lower())) - - # The parameters in the form a1, a2, a3, ... - an = [] - for a in An: - an.append(a.lower()) - - # If the template is fully specialized, use of typename SomeTypedef::iterator is illegal - # in c++03 (should use just SomeTypedef::iterator) [although use of typename is ok in c++0x] - # http://stackoverflow.com/questions/6076015/typename-outside-of-template - if n == 0 and v: - typename = "" - else: - typename = "typename " - - if v: - print("/** A signal with %d parameters (specialisation for a void return) */" % n, file=f) - else: - print("/** A signal with %d parameters */" % n, file=f) - if v: - print("template <%s>" % comma_separated(An, "typename "), file=f) - print("class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An)), file=f) - else: - print("template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue "], "typename "), file=f) - print("class Signal%d : public SignalBase" % n, file=f) - - print("{", file=f) - print("public:", file=f) - print("", file=f) - if v: - print("\ttypedef boost::function slot_function_type;" % comma_separated(An), file=f) - print("\ttypedef void result_type;", file=f) - else: - print("\ttypedef boost::function slot_function_type;" % comma_separated(An), file=f) - print("\ttypedef boost::optional result_type;", file=f) - - print("", file=f) - - print("private:", file=f) - - print(""" -\t/** The slots that this signal will call on emission */ -\ttypedef std::map, slot_function_type> Slots; -\tSlots _slots; -""", file=f) - - print("public:", file=f) - print("", file=f) - print("\t~Signal%d () {" % n, file=f) - - print("\t\t_in_dtor.store (true, std::memory_order_release);", file=f) - print("\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f) - print("\t\t/* Tell our connection objects that we are going away, so they don't try to call us */", file=f) - print("\t\tfor (%sSlots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) {" % typename, file=f) - - print("\t\t\ti->first->signal_going_away ();", file=f) - print("\t\t}", file=f) - print("\t}", file=f) - print("", file=f) - - if n == 0: - p = "" - q = "" - else: - p = ", %s" % comma_separated(Anan) - q = ", %s" % comma_separated(an) - - print("\tstatic void compositor (%sboost::function f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir%s) {" % (typename, comma_separated(An), p), file=f) - print("\t\tevent_loop->call_slot (ir, boost::bind (f%s));" % q, file=f) - print("\t}", file=f) - - print(""" -\t/** Arrange for @a slot to be executed whenever this signal is emitted. -\t * Store the connection that represents this arrangement in @a c. -\t * -\t * NOTE: @a slot will be executed in the same thread that the signal is -\t * emitted in. -\t */ - -\tvoid connect_same_thread (ScopedConnection& c, const slot_function_type& slot) { -\t\tc = _connect (0, slot); -\t} - -\t/** Arrange for @a slot to be executed whenever this signal is emitted. -\t * Add the connection that represents this arrangement to @a clist. -\t * -\t * NOTE: @a slot will be executed in the same thread that the signal is -\t * emitted in. -\t */ - -\tvoid connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) { -\t\tclist.add_connection (_connect (0, slot)); -\t} - -\t/** Arrange for @a slot to be executed in the context of @a event_loop -\t * whenever this signal is emitted. Add the connection that represents -\t * this arrangement to @a clist. -\t * -\t * If the event loop/thread in which @a slot will be executed will -\t * outlive the lifetime of any object referenced in @a slot, -\t * then an InvalidationRecord should be passed, allowing -\t * any request sent to the @a event_loop and not executed -\t * before the object is destroyed to be marked invalid. -\t * -\t * "outliving the lifetime" doesn't have a specific, detailed meaning, -\t * but is best illustrated by two contrasting examples: -\t * -\t * 1) the main GUI event loop/thread - this will outlive more or -\t * less all objects in the application, and thus when arranging for -\t * @a slot to be called in that context, an invalidation record is -\t * highly advisable. -\t * -\t * 2) a secondary event loop/thread which will be destroyed along -\t * with the objects that are typically referenced by @a slot. -\t * Assuming that the event loop is stopped before the objects are -\t * destroyed, there is no reason to pass in an invalidation record, -\t * and MISSING_INVALIDATOR may be used. -\t */ - -\tvoid connect (ScopedConnectionList& clist, -\t PBD::EventLoop::InvalidationRecord* ir, -\t const slot_function_type& slot, -\t PBD::EventLoop* event_loop) { - -\t\tif (ir) { -\t\t\tir->event_loop = event_loop; -\t\t} -""", file=f) - u = [] - for i in range(0, n): - u.append("_%d" % (i + 1)) - - if n == 0: - p = "" - else: - p = ", %s" % comma_separated(u) - - print("\t\tclist.add_connection (_connect (ir, boost::bind (&compositor, slot, event_loop, ir%s)));" % p, file=f) - - print(""" -\t} - -\t/** See notes for the ScopedConnectionList variant of this function. This -\t * differs in that it stores the connection to the signal in a single -\t * ScopedConnection rather than a ScopedConnectionList. -\t */ - -\tvoid connect (ScopedConnection& c, -\t PBD::EventLoop::InvalidationRecord* ir, -\t const slot_function_type& slot, -\t PBD::EventLoop* event_loop) { - -\t\tif (ir) { -\t\t\tir->event_loop = event_loop; -\t\t} -""", file=f) - print("\t\tc = _connect (ir, boost::bind (&compositor, slot, event_loop, ir%s));" % p, file=f) - print("\t}", file=f) - - print(""" -\t/** Emit this signal. This will cause all slots connected to it be executed -\t * in the order that they were connected (cross-thread issues may alter -\t * the precise execution time of cross-thread slots). -\t */ -""", file=f) - - if v: - print("\tvoid operator() (%s)" % comma_separated(Anan), file=f) - else: - print("\ttypename C::result_type operator() (%s)" % comma_separated(Anan), file=f) - print("\t{", file=f) - print("\t\t/* First, take a copy of our list of slots as it is now */", file=f) - print("", file=f) - print("\t\tSlots s;", file=f) - print("\t\t{", file=f) - print("\t\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f) - print("\t\t\ts = _slots;", file=f) - print("\t\t}", file=f) - print("", file=f) - if not v: - print("\t\tstd::list r;", file=f) - print("\t\tfor (%sSlots::const_iterator i = s.begin(); i != s.end(); ++i) {" % typename, file=f) - print(""" -\t\t\t/* We may have just called a slot, and this may have resulted in -\t\t\t * disconnection of other slots from us. The list copy means that -\t\t\t * this won't cause any problems with invalidated iterators, but we -\t\t\t * must check to see if the slot we are about to call is still on the list. -\t\t\t */ -\t\t\tbool still_there = false; -\t\t\t{ -\t\t\t\tGlib::Threads::Mutex::Lock lm (_mutex); -\t\t\t\tstill_there = _slots.find (i->first) != _slots.end (); -\t\t\t} - -\t\t\tif (still_there) {""", file=f) - if v: - print("\t\t\t\t(i->second)(%s);" % comma_separated(an), file=f) - else: - print("\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an), file=f) - print("\t\t\t}", file=f) - print("\t\t}", file=f) - print("", file=f) - if not v: - print("\t\t/* Call our combiner to do whatever is required to the result values */", file=f) - print("\t\tC c;", file=f) - print("\t\treturn c (r.begin(), r.end());", file=f) - print("\t}", file=f) - - print(""" -\tbool empty () const { -\t\tGlib::Threads::Mutex::Lock lm (_mutex); -\t\treturn _slots.empty (); -\t} -""", file=f) - print(""" -\tsize_t size () const { -\t\tGlib::Threads::Mutex::Lock lm (_mutex); -\t\treturn _slots.size (); -\t} -""", file=f) - - if v: - tp = comma_separated(["void"] + An) - else: - tp = comma_separated(["R"] + An + ["C"]) - - print("private:", file=f) - print("", file=f) - print("\tfriend class Connection;", file=f) - - print(""" -\tstd::shared_ptr _connect (PBD::EventLoop::InvalidationRecord* ir, slot_function_type f) -\t{ -\t\tstd::shared_ptr c (new Connection (this, ir)); -\t\tGlib::Threads::Mutex::Lock lm (_mutex); -\t\t_slots[c] = f; -#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS -\t\tif (_debug_connection) { -\t\t\tstd::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl; -\t\t\tPBD::stacktrace (std::cerr, 10); -\t\t} -#endif -\t\treturn c; -\t}""", file=f) - - print(""" -\tvoid disconnect (std::shared_ptr c) -\t{ -\t\t/* ~ScopedConnection can call this concurrently with our d'tor */ -\t\tGlib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK); -\t\twhile (!lm.locked()) { -\t\t\tif (_in_dtor.load (std::memory_order_acquire)) { -\t\t\t/* d'tor signal_going_away() took care of everything already */ -\t\t\t\treturn; -\t\t\t} -\t\t\t/* Spin */ -\t\t\tlm.try_acquire (); -\t\t} -\t\t_slots.erase (c); -\t\tlm.release (); - -\t\tc->disconnected (); -#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS -\t\tif (_debug_connection) { -\t\t\tstd::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl; -\t\t\tPBD::stacktrace (std::cerr, 10); -\t\t} -#endif -\t} - -}; -""", file=f) - -for i in range(0, 6): - signal(f, i, False) - signal(f, i, True) diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 8380e97d28..2f40732065 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -143,9 +143,6 @@ def build(bld): {'LIBPBD_VERSION' : LIBPBD_VERSION, 'LIBPBD_MAJOR_VERSION' : LIBPBD_MAJOR_VERSION}) - # Make signals_generated.h using signals.py - bld(rule = sys.executable + ' ${SRC} ${TGT}', source = 'pbd/signals.py', target = 'pbd/signals_generated.h', name="pbdsignals", features='use', ext_out=['.h']) - # Library if bld.is_defined ('INTERNAL_SHARED_LIBS'): obj = bld.shlib(features = 'cxx cxxshlib', source=libpbd_sources) @@ -163,7 +160,6 @@ def build(bld): obj.includes = ['.'] obj.name = 'libpbd' obj.target = 'pbd' - obj.use = 'pbdsignals' obj.uselib = 'GLIBMM SIGCPP XML UUID SNDFILE GIOMM ARCHIVE CURL' if sys.platform == 'darwin': TaskGen.task_gen.mappings['.mm'] = TaskGen.task_gen.mappings['.cc']