13
0

switch to use boost::function for UI::call_slot operations, to avoid a serious thread safety issue with libsigc++

git-svn-id: svn://localhost/ardour2/branches/3.0@6355 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-12-11 23:30:48 +00:00
parent 64dc5427e4
commit 401c7091c8
10 changed files with 35 additions and 228 deletions

View File

@ -611,13 +611,13 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
bool soloing() const { return _non_soloed_outs_muted; }
bool listening() const { return _listen_cnt > 0; }
static const sigc::slot<void,SessionEvent*> rt_cleanup;
static const SessionEvent::RTeventCallback rt_cleanup;
void set_solo (boost::shared_ptr<RouteList>, bool, sigc::slot<void,SessionEvent*> after = rt_cleanup, bool group_override = false);
void set_just_one_solo (boost::shared_ptr<Route>, bool, sigc::slot<void,SessionEvent*> after = rt_cleanup);
void set_mute (boost::shared_ptr<RouteList>, bool, sigc::slot<void,SessionEvent*> after = rt_cleanup, bool group_override = false);
void set_listen (boost::shared_ptr<RouteList>, bool, sigc::slot<void,SessionEvent*> after = rt_cleanup, bool group_override = false);
void set_record_enable (boost::shared_ptr<RouteList>, bool, sigc::slot<void,SessionEvent*> after = rt_cleanup, bool group_override = false);
void set_solo (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
void set_just_one_solo (boost::shared_ptr<Route>, bool, SessionEvent::RTeventCallback after = rt_cleanup);
void set_mute (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
void set_listen (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
void set_record_enable (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
sigc::signal<void,bool> SoloActive;
sigc::signal<void> SoloChanged;
@ -1471,8 +1471,8 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
gint _have_rec_enabled_diskstream;
/* realtime "apply to set of routes" operations */
SessionEvent* get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, bool, bool));
SessionEvent* get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, bool, bool));
void rt_set_solo (boost::shared_ptr<RouteList>, bool yn, bool group_override);
void rt_set_just_one_solo (boost::shared_ptr<RouteList>, bool yn, bool /* ignored*/ );

View File

@ -2,6 +2,7 @@
#define __ardour_session_event_h__
#include <list>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <sigc++/signal.h>
@ -66,10 +67,14 @@ struct SessionEvent {
bool second_yes_or_no;
};
boost::shared_ptr<RouteList> routes;
sigc::slot<void> rt_slot; /* what to call in RT context */
sigc::slot<void,SessionEvent*> rt_return; /* called after rt_slot, with this event as an argument */
PBD::UICallback* ui;
/* 4 members to handle a multi-group event handled in RT context */
typedef boost::function<void (SessionEvent*)> RTeventCallback;
boost::shared_ptr<RouteList> routes; /* apply to */
boost::function<void (void)> rt_slot; /* what to call in RT context */
RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */
PBD::UICallback* ui;
std::list<AudioRange> audio_range;
std::list<MusicRange> music_range;

View File

@ -1419,7 +1419,6 @@ void
Region::source_deleted (boost::shared_ptr<Source>)
{
_sources.clear ();
drop_references ();
}
vector<string>

View File

@ -118,7 +118,7 @@ sigc::signal<void> Session::AutoBindingOff;
sigc::signal<void, std::string, std::string> Session::Exported;
static void clean_up_session_event (SessionEvent* ev) { delete ev; }
const sigc::slot<void,SessionEvent*> Session::rt_cleanup (sigc::ptr_fun (&clean_up_session_event));
const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
Session::Session (AudioEngine &eng,
const string& fullpath,

View File

@ -16,6 +16,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <boost/bind.hpp>
#include "pbd/error.h"
#include "pbd/compose.h"
@ -31,11 +33,11 @@ using namespace ARDOUR;
using namespace Glib;
SessionEvent*
Session::get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override,
Session::get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, bool, bool))
{
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = bind (sigc::mem_fun (*this, method), rl, yn, group_override);
ev->rt_slot = boost::bind (method, this, rl, yn, group_override);
ev->rt_return = after;
ev->ui = UICallback::get_ui_for_thread ();
@ -43,7 +45,7 @@ Session::get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void
}
void
Session::set_solo (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override)
Session::set_solo (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo));
}
@ -61,7 +63,7 @@ Session::rt_set_solo (boost::shared_ptr<RouteList> rl, bool yn, bool group_overr
}
void
Session::set_just_one_solo (boost::shared_ptr<Route> r, bool yn, sigc::slot<void,SessionEvent*> after)
Session::set_just_one_solo (boost::shared_ptr<Route> r, bool yn, SessionEvent::RTeventCallback after)
{
/* its a bit silly to have to do this, but it keeps the API for this public method sane (we're
only going to solo one route) and keeps our ability to use get_rt_event() for the internal
@ -92,7 +94,7 @@ Session::rt_set_just_one_solo (boost::shared_ptr<RouteList> just_one, bool yn, b
}
void
Session::set_listen (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override)
Session::set_listen (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_listen));
}
@ -110,7 +112,7 @@ Session::rt_set_listen (boost::shared_ptr<RouteList> rl, bool yn, bool group_ove
}
void
Session::set_mute (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override)
Session::set_mute (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_mute));
}
@ -128,7 +130,7 @@ Session::rt_set_mute (boost::shared_ptr<RouteList> rl, bool yn, bool group_overr
}
void
Session::set_record_enable (boost::shared_ptr<RouteList> rl, bool yn, sigc::slot<void,SessionEvent*> after, bool group_override)
Session::set_record_enable (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override)
{
if (!writable()) {
return;
@ -161,7 +163,7 @@ Session::process_rtop (SessionEvent* ev)
ev->rt_slot ();
if (ev->ui) {
ev->ui->call_slot (bind (ev->rt_return, ev));
ev->ui->call_slot (boost::bind (ev->rt_return, ev));
} else {
warning << string_compose ("programming error: %1", X_("Session RT event queued from thread without a UI - cleanup in RT thread!")) << endmsg;
ev->rt_return (ev);

View File

@ -140,10 +140,10 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
}
template<typename RequestObject> void
AbstractUI<RequestObject>::call_slot (sigc::slot<void> elSlot)
AbstractUI<RequestObject>::call_slot (const boost::function<void()>& f)
{
if (caller_is_self()) {
elSlot ();
f ();
return;
}
@ -153,7 +153,7 @@ AbstractUI<RequestObject>::call_slot (sigc::slot<void> elSlot)
return;
}
req->the_slot = elSlot;
req->the_slot = f;
send_request (req);
}

View File

@ -42,7 +42,7 @@ class AbstractUI : public BaseUI
virtual ~AbstractUI() {}
void register_thread (std::string, pthread_t, std::string, uint32_t num_requests);
void call_slot (sigc::slot<void> el_slot);
void call_slot (const boost::function<void()>&);
protected:
typedef RingBufferNPT<RequestObject> RequestBuffer;

View File

@ -54,7 +54,7 @@ class BaseUI : virtual public sigc::trackable, public PBD::UICallback
struct BaseRequestObject {
RequestType type;
sigc::slot<void> the_slot;
boost::function<void()> the_slot;
};
static RequestType new_request_type();

View File

@ -1,199 +0,0 @@
/*
Copyright (C) 2009 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.
*/
#ifndef __pbd_closure_h__
#define __pbd_closure_h__
#include <glib.h>
/**
* Here we implement thread-safe but lifetime-unsafe closures (aka "functor"), which
* wrap an object and one of its methods, plus zero-or-more arguments, in a convenient,
* ready to use package.
*
* These differ from sigc::slot<> in that they are totally non-invasive with
* respect to the objects referenced by the closure. There is no requirement
* that the object be derived from any particular base class, and nothing
* will be done to the object during the creation of the closure, or its deletion,
* or at any time other than when the object's method is invoked via
* Closure::operator(). As a result, the closure can be constructed and deleted without
* concerns for thread safety. If the object method is thread-safe, then the closure
* can also be invoked in a thread safe fashion.
*
* However, this also means that the closure is not safe against lifetime
* management issues - if the referenced object is deleted before the closure,
* and then closure is invoked via operator(), the results are undefined (but
* will almost certainly be bad). This class should therefore be used only
* where you can guarantee that the referred-to object will outlive the
* life of the closure.
*/
namespace PBD {
struct ClosureBaseImpl {
ClosureBaseImpl() { g_atomic_int_set (&_ref, 0); }
ClosureBaseImpl* ref() { g_atomic_int_inc (&_ref); return this; }
void unref() { if (g_atomic_int_dec_and_test (&_ref)) delete this; }
virtual void operator() () = 0;
protected:
virtual ~ClosureBaseImpl() { }
private:
gint _ref;
};
struct Closure {
Closure () : impl (0) {}
Closure (ClosureBaseImpl* i) : impl (i->ref()) {}
Closure (const Closure& other) : impl (other.impl ? other.impl->ref() : 0) {}
Closure& operator= (const Closure& other) {
if (&other == this) {
return *this;
}
if (impl) {
impl->unref();
}
if (other.impl) {
impl = other.impl->ref();
} else {
impl = 0;
}
return *this;
}
virtual ~Closure () { if (impl) { impl->unref(); } }
/* will crash if impl is unset */
void operator() () const { (*impl)(); }
protected:
ClosureBaseImpl* impl;
};
template<typename T>
struct ClosureImpl0 : public ClosureBaseImpl {
ClosureImpl0 (T& obj, void (T::*m)())
: object (obj), method (m) {}
void operator() () { (object.*method)(); }
private:
T& object;
void (T::*method)();
};
template<typename T, typename A1>
struct ClosureImpl1 : public ClosureBaseImpl
{
ClosureImpl1 (T& obj, void (T::*m)(A1), A1 arg)
: object (obj), method (m), arg1 (arg) {}
void operator() () { (object.*method) (arg1); }
private:
T& object;
void (T::*method)(A1);
A1 arg1;
};
template<typename T, typename A1, typename A2>
struct ClosureImpl2 : public ClosureBaseImpl
{
ClosureImpl2 (T& obj, void (T::*m)( A1, A2), A1 arga, A2 argb)
: object (obj), method (m), arg1 (arga), arg2 (argb) {}
void operator() () { (object.*method) (arg1, arg2); }
private:
T& object;
void (T::*method)(A1, A2);
A1 arg1;
A2 arg2;
};
template<typename T, typename A1, typename A2, typename A3>
struct ClosureImpl3 : public ClosureBaseImpl
{
ClosureImpl3 (T& obj, void (T::*m)( A1, A2, A3), A1 arga, A2 argb, A3 argc)
: object (obj), method (m), arg1 (arga), arg2 (argb), arg3 (argc) {}
void operator() () { (object.*method) (arg1, arg2, arg3); }
private:
T& object;
void (T::*method)(A1, A2, A3);
A1 arg1;
A2 arg2;
A3 arg3;
};
template<typename T>
Closure closure (T& t, void (T::*m)()) {return Closure (new ClosureImpl0<T> (t,m)); }
template<typename T, typename A>
Closure closure (T& t, void (T::*m)(A), A a) { return Closure (new ClosureImpl1<T,A>(t,m,a)); }
template<typename T, typename A1, typename A2>
Closure closure (T& t, void (T::*m)(A1,A2), A1 a1, A2 a2) { return Closure (new ClosureImpl2<T,A1,A2>(t,m, a1, a2)); }
template<typename T, typename A1, typename A2, typename A3>
Closure closure (T& t, void (T::*m)(A1, A2, A3), A1 a1, A2 a2, A3 a3) { return Closure (new ClosureImpl3<T,A1,A2,A3>(t,m , a1, a2, a3)); }
/*--- CALL TIME CLOSURES : these accept arguments at run time */
template<typename A>
struct CTClosureBaseImpl : ClosureBaseImpl {
CTClosureBaseImpl() {}
virtual void operator() () { operator() (A()); }
virtual void operator() (A arg) = 0;
protected:
virtual ~CTClosureBaseImpl() { }
};
template<typename A>
struct CTClosure : public Closure {
CTClosure() {}
CTClosure (CTClosureBaseImpl<A>* i) : Closure (i) {}
CTClosure (const CTClosure& other) : Closure (other) {}
/* will crash if impl is unset */
void operator() (A arg) const { (*(dynamic_cast<CTClosureBaseImpl<A>*> (impl))) (arg); }
};
template<typename T, typename A>
struct CTClosureImpl1 : public CTClosureBaseImpl<A>
{
CTClosureImpl1 (T& obj, void (T::*m)(A))
: object (obj), method (m) {}
void operator() (A call_time_arg) { (object.*method) (call_time_arg); }
private:
T& object;
void (T::*method)(A);
};
/* functor wraps a method that takes 1 arg provided at call-time */
template<typename T, typename A>
CTClosure<A> closure (T& t, void (T::*m)(A)) { return CTClosure<A> (new CTClosureImpl1<T,A>(t,m)); }
}
#endif /* __pbd_closure_h__ */

View File

@ -20,8 +20,8 @@
#ifndef __pbd_ui_callback_h__
#define __pbd_ui_callback_h__
#include <boost/function.hpp>
#include <glibmm/thread.h>
#include <sigc++/slot.h>
namespace PBD
{
@ -32,7 +32,7 @@ class UICallback
UICallback() {}
virtual ~UICallback() {}
virtual void call_slot (sigc::slot<void> theSlot) = 0;
virtual void call_slot (const boost::function<void()>&) = 0;
static UICallback* get_ui_for_thread();
static void set_ui_for_thread (UICallback* ui);