From ae7c1d452d7d80f060d8e5609fae418fd489e799 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 16 May 2012 08:52:43 +0000 Subject: [PATCH] merge 12268:12290 from svn+ssh://ardoursvn@subversion.ardour.org/ardour2/branches/3.0 git-svn-id: svn://localhost/ardour2/branches/3.0-SG@12308 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/region_factory.h | 3 +- libs/ardour/region_factory.cc | 28 +- libs/pbd/pbd/signal.h.py | 214 -------------- libs/pbd/pbd/signals.h | 430 ++++------------------------ libs/pbd/pbd/signals.py | 306 ++++++++++++++++++++ libs/pbd/signals.cc | 3 - libs/pbd/wscript | 2 +- wscript | 105 +++---- 8 files changed, 435 insertions(+), 656 deletions(-) delete mode 100644 libs/pbd/pbd/signal.h.py create mode 100644 libs/pbd/pbd/signals.py diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index dfbcd480dd..d9059fc19e 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -83,8 +83,7 @@ public: static void get_regions_using_source (boost::shared_ptr, std::set >& ); static void remove_regions_using_source (boost::shared_ptr); - static void map_remove (boost::shared_ptr); - static void map_remove_with_equivalents (boost::shared_ptr); + static void map_remove (boost::weak_ptr); static void delete_all_regions (); static const RegionMap& regions() { return region_map; } static uint32_t nregions (); diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index f739483821..f574097fea 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -321,7 +321,7 @@ RegionFactory::map_add (boost::shared_ptr r) region_map.insert (p); } - r->DropReferences.connect_same_thread (region_list_connections, boost::bind (&RegionFactory::map_remove, r)); + r->DropReferences.connect_same_thread (region_list_connections, boost::bind (&RegionFactory::map_remove, boost::weak_ptr (r))); r->PropertyChanged.connect_same_thread ( region_list_connections, @@ -332,8 +332,13 @@ RegionFactory::map_add (boost::shared_ptr r) } void -RegionFactory::map_remove (boost::shared_ptr r) +RegionFactory::map_remove (boost::weak_ptr w) { + boost::shared_ptr r = w.lock (); + if (!r) { + return; + } + Glib::Mutex::Lock lm (region_map_lock); RegionMap::iterator i = region_map.find (r->id()); @@ -342,25 +347,6 @@ RegionFactory::map_remove (boost::shared_ptr r) } } -void -RegionFactory::map_remove_with_equivalents (boost::shared_ptr r) -{ - Glib::Mutex::Lock lm (region_map_lock); - - for (RegionMap::iterator i = region_map.begin(); i != region_map.end(); ) { - RegionMap::iterator tmp = i; - ++tmp; - - if (r->region_list_equivalent (i->second)) { - region_map.erase (i); - } else if (r == i->second) { - region_map.erase (i); - } - - i = tmp; - } -} - boost::shared_ptr RegionFactory::region_by_id (const PBD::ID& id) { diff --git a/libs/pbd/pbd/signal.h.py b/libs/pbd/pbd/signal.h.py deleted file mode 100644 index 70a1a2d88c..0000000000 --- a/libs/pbd/pbd/signal.h.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/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 7cdbcef414..904d51e45f 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 @@ -26,13 +26,71 @@ #include #include #include +#include +#include +#include +#include #include "pbd/event_loop.h" -#include "pbd/signal.h" namespace PBD { +class Connection; + +class SignalBase +{ +public: + virtual ~SignalBase () {} + virtual void disconnect (boost::shared_ptr) = 0; + +protected: + boost::mutex _mutex; +}; + +class Connection : public boost::enable_shared_from_this +{ +public: + Connection (SignalBase* b) : _signal (b) {} + + void disconnect () + { + boost::mutex::scoped_lock lm (_mutex); + if (_signal) { + _signal->disconnect (shared_from_this ()); + } + } + + void signal_going_away () + { + boost::mutex::scoped_lock lm (_mutex); + _signal = 0; + } + +private: + boost::mutex _mutex; + SignalBase* _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; + } +}; + typedef boost::shared_ptr UnscopedConnection; + class ScopedConnection { public: @@ -72,12 +130,7 @@ 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 @@ -88,370 +141,13 @@ class ScopedConnectionList : public boost::noncopyable one from another. */ - static Glib::StaticMutex _lock; + Glib::Mutex _lock; typedef std::list ConnectionList; ConnectionList _list; }; -template -class Signal0 { -public: - 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. - - 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 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->emit (); - } - - /** Return true if there is nothing connected to this signal, false - * otherwise. - */ - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; - -template > -class Signal1 { -public: - 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)); - } - - 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->emit (arg1); - } - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; - -template -class Signal2 { -public: - 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)); - } - - 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->emit (arg1, arg2); - } - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; - -template -class Signal3 { -public: - 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)); - } - - 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->emit (arg1, arg2, arg3); - } - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; - -template -class Signal4 { -public: - 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)); - } - - 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->emit (arg1, arg2, arg3, arg4); - } - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; - -template -class Signal5 { -public: - 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)); - } - - 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->emit (arg1, arg2, arg3, arg4, arg5); - } - - bool empty() const { return _signal->empty(); } - -private: - boost::shared_ptr _signal; -}; +#include "pbd/signals_generated.h" } /* namespace */ diff --git a/libs/pbd/pbd/signals.py b/libs/pbd/pbd/signals.py new file mode 100644 index 0000000000..f3dfd05d82 --- /dev/null +++ b/libs/pbd/pbd/signals.py @@ -0,0 +1,306 @@ +#!/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(""" + /** The slots that this signal will call on emission */ + typedef std::map, slot_function_type> Slots; + Slots _slots; +""", file=f) + + print("public:", file=f) + print("", file=f) + print("\t~Signal%d () {" % n, file=f) + + print("\t\tboost::mutex::scoped_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::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(""" + /** 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. + */ + + void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) { + c = _connect (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. + */ + + void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) { + clist.add_connection (_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 slot_function_type& slot, + PBD::EventLoop* event_loop) { + + if (ir) { + ir->event_loop = event_loop; + } +""", 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 (boost::bind (&compositor, slot, event_loop, ir%s)));" % p, file=f) + + print(""" + } + + /** 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 slot_function_type& slot, + PBD::EventLoop* event_loop) { + + if (ir) { + ir->event_loop = event_loop; + } +""", file=f) + print("\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p, file=f) + print("\t}", file=f) + + print(""" + /** 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). + */ +""", 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\tboost::mutex::scoped_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::iterator i = s.begin(); i != s.end(); ++i) {" % typename, file=f) + print(""" + /* 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; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (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(""" + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } +""", 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(""" + boost::shared_ptr _connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (this)); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + }""", file=f) + + print(""" + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; +""", file=f) + +for i in range(0, 6): + signal(f, i, False) + signal(f, i, True) diff --git a/libs/pbd/signals.cc b/libs/pbd/signals.cc index b37c6e211a..cafb98f6bf 100644 --- a/libs/pbd/signals.cc +++ b/libs/pbd/signals.cc @@ -22,15 +22,12 @@ using namespace PBD; -Glib::StaticMutex ScopedConnectionList::_lock = GLIBMM_STATIC_MUTEX_INIT; - ScopedConnectionList::ScopedConnectionList() { } ScopedConnectionList::~ScopedConnectionList() { - std::cout << "~ScopedConnectionList " << this << " " << PBD::demangled_name (*this) << "\n"; drop_connections (); } diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 7e48f40019..23a63e40a1 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -56,7 +56,7 @@ def configure(conf): def build(bld): # Make signal.h using signal.h.py - bld(rule = 'python ${SRC} ${TGT}', source = 'pbd/signal.h.py', target = 'pbd/signal.h') + bld(rule = 'python ${SRC} ${TGT}', source = 'pbd/signals.py', target = 'pbd/signals_generated.h') # Library obj = bld(features = 'cxx cxxshlib') diff --git a/wscript b/wscript index c4dd56fe7e..93c6434e93 100644 --- a/wscript +++ b/wscript @@ -123,52 +123,51 @@ def set_compiler_flags (conf,opt): optimization_flags = [] debug_flags = [] - # guess at the platform, used to define compiler flags - - config_guess = os.popen("tools/config.guess").read()[:-1] - - config_cpu = 0 - config_arch = 1 - config_kernel = 2 - config_os = 3 - config = config_guess.split ("-") + u = os.uname () + cpu = u[4] + platform = u[0].lower() + version = u[2] if opt.gprofile: debug_flags = [ '-pg' ] else: - if config[config_arch] != 'apple': + if platform != 'darwin': debug_flags = [ '-rdynamic' ] # waf adds -O0 -g itself. thanks waf! # Autodetect if opt.dist_target == 'auto': - if config[config_arch] == 'apple': + if platform == 'darwin': # The [.] matches to the dot after the major version, "." would match any character - if re.search ("darwin[0-7][.]", config[config_kernel]) != None: + if re.search ("^[0-7][.]", version) != None: conf.env['build_target'] = 'panther' - elif re.search ("darwin8[.]", config[config_kernel]) != None: + elif re.search ("^8[.]", version) != None: conf.env['build_target'] = 'tiger' - else: + elif re.search ("^9[.]", version) != None: conf.env['build_target'] = 'leopard' + elif re.search ("^10[.]", version) != None: + conf.env['build_target'] = 'snowleopard' + else: + conf.env['build_target'] = 'lion' else: - if re.search ("x86_64", config[config_cpu]) != None: + if re.search ("x86_64", cpu) != None: conf.env['build_target'] = 'x86_64' - elif re.search("i[0-5]86", config[config_cpu]) != None: + elif re.search("i[0-5]86", cpu) != None: conf.env['build_target'] = 'i386' - elif re.search("powerpc", config[config_cpu]) != None: + elif re.search("powerpc", config_cpu) != None: conf.env['build_target'] = 'powerpc' else: conf.env['build_target'] = 'i686' else: conf.env['build_target'] = opt.dist_target - if config[config_cpu] == 'powerpc' and conf.env['build_target'] != 'none': + if cpu == 'powerpc' and conf.env['build_target'] != 'none': # # Apple/PowerPC optimization options # # -mcpu=7450 does not reliably work with gcc 3.* # if opt.dist_target == 'panther' or opt.dist_target == 'tiger': - if config[config_arch] == 'apple': + if platform == 'darwin': # optimization_flags.extend ([ "-mcpu=7450", "-faltivec"]) # to support g3s but still have some optimization for above optimization_flags.extend ([ "-mcpu=G3", "-mtune=7450"]) @@ -179,7 +178,7 @@ def set_compiler_flags (conf,opt): optimization_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"]) optimization_flags.extend (["-Os"]) - elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None)) and conf.env['build_target'] != 'none': + elif ((re.search ("i[0-9]86", cpu) != None) or (re.search ("x86_64", cpu) != None)) and conf.env['build_target'] != 'none': # @@ -188,11 +187,11 @@ def set_compiler_flags (conf,opt): # distingush 32 and 64 bit assembler # - if (re.search ("(i[0-9]86|x86_64)", config[config_cpu]) != None): + if (re.search ("(i[0-9]86|x86_64)", cpu) != None): debug_flags.append ("-DARCH_X86") optimization_flags.append ("-DARCH_X86") - if config[config_kernel] == 'linux' : + if platform == 'linux' : # # determine processor flags via /proc/cpuinfo @@ -210,9 +209,9 @@ def set_compiler_flags (conf,opt): if "3dnow" in x86_flags: optimization_flags.append ("-m3dnow") - if config[config_cpu] == "i586": + if cpu == "i586": optimization_flags.append ("-march=i586") - elif config[config_cpu] == "i686": + elif cpu == "i686": optimization_flags.append ("-march=i686") if ((conf.env['build_target'] == 'i686') or (conf.env['build_target'] == 'x86_64')) and build_host_supports_sse: @@ -223,7 +222,7 @@ def set_compiler_flags (conf,opt): # optimization section if conf.env['FPU_OPTIMIZATION']: - if conf.env['build_target'] == 'tiger' or conf.env['build_target'] == 'leopard': + if sys.platform == 'darwin': optimization_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS"); debug_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS"); conf.env.append_value('LINKFLAGS', "-framework Accelerate") @@ -271,9 +270,9 @@ def set_compiler_flags (conf,opt): conf.define ('IS_OSX', 0) # - # save off guessed arch element in an env + # save off CPU element in an env # - conf.define ('CONFIG_ARCH', config[config_arch]) + conf.define ('CONFIG_ARCH', cpu) # # ARCH="..." overrides all @@ -316,14 +315,23 @@ def set_compiler_flags (conf,opt): conf.env.append_value('CXXFLAGS', '-DDEBUG_DENORMAL_EXCEPTION') if opt.universal: - if not Options.options.nocarbon: - conf.env.append_value('CFLAGS', ["-arch", "i386", "-arch", "ppc"]) - conf.env.append_value('CXXFLAGS', ["-arch", "i386", "-arch", "ppc"]) - conf.env.append_value('LINKFLAGS', ["-arch", "i386", "-arch", "ppc"]) + if opt.generic: + print ('Specifying Universal and Generic builds at the same time is not supported') + sys.exit (1) else: - conf.env.append_value('CFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) - conf.env.append_value('CXXFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) - conf.env.append_value('LINKFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) + if not Options.options.nocarbon: + conf.env.append_value('CFLAGS', ["-arch", "i386", "-arch", "ppc"]) + conf.env.append_value('CXXFLAGS', ["-arch", "i386", "-arch", "ppc"]) + conf.env.append_value('LINKFLAGS', ["-arch", "i386", "-arch", "ppc"]) + else: + conf.env.append_value('CFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) + conf.env.append_value('CXXFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) + conf.env.append_value('LINKFLAGS', ["-arch", "x86_64", "-arch", "i386", "-arch", "ppc"]) + else: + if opt.generic: + conf.env.append_value('CFLAGS', ['-arch', 'i386']) + conf.env.append_value('CXXFLAGS', ['-arch', 'i386']) + conf.env.append_value('LINKFLAGS', ['-arch', 'i386']) # # warnings flags @@ -404,6 +412,8 @@ def options(opt): help='Compile with support for Frontier Designs Tranzport (if libusb is available)') opt.add_option('--universal', action='store_true', default=False, dest='universal', help='Compile as universal binary (OS X ONLY, requires that external libraries are universal)') + opt.add_option('--generic', action='store_true', default=False, dest='generic', + help='Compile with -march=generic') opt.add_option('--versioned', action='store_true', default=False, dest='versioned', help='Add revision information to executable name inside the build directory') opt.add_option('--windows-vst', action='store_true', default=False, dest='windows_vst', @@ -469,8 +479,7 @@ def configure(conf): conf.define ('HAVE_COREAUDIO', 1) conf.define ('AUDIOUNIT_SUPPORT', 1) - if not Options.options.nocarbon: - conf.define ('WITH_CARBON', 1) + if not Options.options.freebie: conf.define ('AU_STATE_SUPPORT', 1) @@ -525,9 +534,12 @@ def configure(conf): if not Options.options.freebie: conf.env.append_value('CXXFLAGS_AUDIOUNITS', "-DAU_STATE_SUPPORT") - if not Options.options.nocarbon: + + if re.search ("^[1-9][0-9]\.", os.uname()[2]) == None and not Options.options.nocarbon: conf.env.append_value('CXXFLAGS_AUDIOUNITS', "-DWITH_CARBON") conf.env.append_value('LINKFLAGS_AUDIOUNITS', ['-framework', 'Carbon']) + else: + print ('No Carbon support available for this build\n') if Options.options.soundgrid: conf.env.append_value ('CXXFLAGS_SOUNDGRID', [ '-D__MACOS__', '-DUSE_SOUNDGRID', '-I/Volumes/Work/paul/ardour/3.0-SG/soundgrid' ]) @@ -548,8 +560,6 @@ def configure(conf): if Options.options.also_libdir != '': conf.env.append_value('LDFLAGS', '-L' + Options.options.also_libdir) - autowaf.check_header(conf, 'cxx', 'boost/signals2.hpp', mandatory = True) - if Options.options.boost_sp_debug: conf.env.append_value('CXXFLAGS', '-DBOOST_SP_ENABLE_DEBUG_HOOKS') @@ -570,12 +580,6 @@ def configure(conf): autowaf.check_pkg(conf, 'giomm-2.4', uselib_store='GIOMM', atleast_version='2.2') autowaf.check_pkg(conf, 'libcurl', uselib_store='CURL', atleast_version='7.0.0') - for i in children: - sub_config_and_use(conf, i) - - # Fix utterly braindead FLAC include path to not smash assert.h - conf.env['INCLUDES_FLAC'] = [] - conf.check_cc(function_name='dlopen', header_name='dlfcn.h', linkflags='-ldl', uselib_store='DL') # Tell everyone that this is a waf build @@ -629,10 +633,14 @@ def configure(conf): if not conf.is_defined('HAVE_CPPUNIT'): conf.env['BUILD_TESTS'] = False - - set_compiler_flags (conf, Options.options) + for i in children: + sub_config_and_use(conf, i) + + # Fix utterly braindead FLAC include path to not smash assert.h + conf.env['INCLUDES_FLAC'] = [] + config_text = open('libs/ardour/config_text.cc', "w") config_text.write('''#include "ardour/ardour.h" namespace ARDOUR { @@ -678,6 +686,7 @@ const char* const ardour_config_info = "\\n\\ write_config_text('Tranzport', opts.tranzport) write_config_text('Unit tests', conf.env['BUILD_TESTS']) write_config_text('Universal binary', opts.universal) + write_config_text('Generic x86 CPU', opts.generic) write_config_text('Windows VST support', opts.windows_vst) write_config_text('Wiimote support', opts.wiimote) write_config_text('Windows key', opts.windows_key) @@ -745,7 +754,7 @@ def i18n_mo(bld): bld.recurse (i18n_children) def install_not_supported(bld): - print 'Installing Ardour 3 is currently unsupported. Run it via the command ./ardev from within the gtk2_ardour directory.' + print ('Installing Ardour 3 is currently unsupported. Run it via the command ./ardev from within the gtk2_ardour directory.') sys.exit (1) from waflib import Build