Re: Does gtk have issues with STL?



On Tue, 2008-02-12 at 20:24 +0000, Chris Vine wrote:
> On Tue, 2008-02-12 at 08:56 -0500, Lindley M French wrote:
> > Interesting. So g_idle_add can be safely called from a different thread? Does it have to be a gthread, or will a pthread work as well?
> 
> You can use pthreads under glib without problems.  For Unix-like
> systems, gthreads is only a wrapper for pthreads.
> 
> However, you need to call g_thread_init() to make glib (including
> g_idle_add()) thread safe, whether you are using gthreads or using
> pthreads directly.  You do not need to call gdk_thread_init() if you are
> only invoking GDK/GTK+ functions by message passing via g_idle_add()
> (that is, if you are not calling gdk_threads_enter()/leave()).

Since I see one of your e-mails was asking for a pattern for using
g_idle_add() in C++, here is something I use.  Go to the end to see the
function which actually dispatches the callback via g_idle_add().

Chris
/* Copyright (C) 2008 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1 of
   the License, or (at your option) any later version.

   This library 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
   Lesser General Public License, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the src/utils sub-directory);
   if not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA, 02111-1307, USA.

However, it is not intended that the object code of a program whose
source code instantiates a template from this file should by reason
only of that instantiation be subject to the restrictions of use in
the GNU Lesser General Public License.  With that in mind, the words
"and instantiations of templates (of any length)" shall be treated as
inserted in the fourth paragraph of section 5 of that licence after
the words "and small inline functions (ten lines or less in length)".
This does not affect any other reason why object code may be subject
to the restrictions in that licence (nor for the avoidance of doubt
does it affect the application of section 2 of that licence to
modifications of the source code in this file).

*/

#ifndef CALLBACK_H
#define CALLBACK_H

/* These callback classes are intended for use in cases where a
   callback object may need to be handed between threads and where
   using sigc::slot would therefore be unsafe - this is because
   sigc::slot objects representing non-static methods of a class
   derived from sigc::trackable may invoke sigc::trackable functions
   at slot construction and destruction which are not thread safe.

   The templated helper Callback::make() functions make it trivial to
   create a callback object of the correct type.  A maximum of two
   arguments to pass to the relevant class method is provided for -
   but this can be extended indefinitely.  To bind to static member
   functions (or normal non-class functions), the
   Callback::make_static() helper functions are also available.

   The Callback::post() function is called by worker threads and will
   execute a callback in the main program GMainContext loop.
*/

#include <glib/gmain.h>

#include <iostream>
#include <ostream>

inline void write_error(const char* message) {
  std::cerr << message;
}

namespace Callback {

/* The following Callback class is the interface class that users will
   generally see, and is suitable for passing by void*, which if using
   glib and/or gthreads and/or pthreads is almost certain to happen at
   some stage.

   Because of the class's intended usage, Callback::dispatch() (and
   Callback::operator()()) return void.  The Callback class could be
   templated to provide a return value, but that would affect the
   simplicity of the interface, and if a case were to arise where a
   result is needed, an alternative is for users to pass an argument
   by pointer (or pointer to pointer) rather than have a return value.
*/
class Callback {
public:
  // because we will usually be calling through a base class pointer,
  // it would be more natural to invoke the call back through a normal
  // member function rather than by operator()()
  virtual void dispatch() const = 0;
  // but for those who want it
  void operator()() const {dispatch();}
  virtual ~Callback() {};
};

template <class T> class Callback0: public Callback {
public:
  typedef void (T::* MemFunc)();
private:
  T* obj;
  MemFunc func;
public:
  void dispatch() const {(obj->*func)();}
  Callback0(T& obj_, MemFunc func_): obj(&obj_), func(func_) {}
};

template <class T, class Arg1> class Callback1: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1);
private:
  T* obj;
  MemFunc func;
  Arg1 arg1;
public:
  void dispatch() const {(obj->*func)(arg1);}
  Callback1(T& obj_, MemFunc func_, Arg1 arg1_): obj(&obj_), func(func_), arg1(arg1_) {}
};

template <class T, class Arg1, class Arg2> class Callback2: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1, Arg2);
private:
  T* obj;
  MemFunc func;
  Arg1 arg1;
  Arg2 arg2;
public:
  void dispatch() const {(obj->*func)(arg1, arg2);}
  Callback2(T& obj_, MemFunc func_, Arg1 arg1_, Arg2 arg2_): obj(&obj_), func(func_),
                                                             arg1(arg1_), arg2(arg2_) {}
};

/* const versions, for binding to const methods */

template <class T> class Callback0_const: public Callback {
public:
  typedef void (T::* MemFunc)() const;
private:
  const T* obj;
  MemFunc func;
public:
  void dispatch() const {(obj->*func)();}
  Callback0_const(const T& obj_, MemFunc func_): obj(&obj_), func(func_) {}
};

template <class T, class Arg1> class Callback1_const: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1) const;
private:
  const T* obj;
  MemFunc func;
  Arg1 arg1;
public:
  void dispatch() const {(obj->*func)(arg1);}
  Callback1_const(const T& obj_, MemFunc func_, Arg1 arg1_): obj(&obj_), func(func_), arg1(arg1_) {}
};

template <class T, class Arg1, class Arg2> class Callback2_const: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1, Arg2) const;
private:
  const T* obj;
  MemFunc func;
  Arg1 arg1;
  Arg2 arg2;
public:
  void dispatch() const {(obj->*func)(arg1, arg2);}
  Callback2_const(const T& obj_, MemFunc func_, Arg1 arg1_, Arg2 arg2_): obj(&obj_), func(func_),
                                                                         arg1(arg1_), arg2(arg2_) {}
};

/* for static class methods and non-class functions */

class Callback0_static: public Callback {
public:
  typedef void (*Func)();
private:
  Func func;
public:
  void dispatch() const {func();}
  Callback0_static(Func func_): func(func_) {}
};

template <class Arg1> class Callback1_static: public Callback {
public:
  typedef void (*Func)(Arg1);
private:
  Func func;
  Arg1 arg1;
public:
  void dispatch() const {func(arg1);}
  Callback1_static(Func func_, Arg1 arg1_): func(func_), arg1(arg1_) {}
};

template <class Arg1, class Arg2> class Callback2_static: public Callback {
public:
  typedef void (*Func)(Arg1, Arg2);
private:
  Func func;
  Arg1 arg1;
  Arg2 arg2;
public:
  void dispatch() const {func(arg1, arg2);}
  Callback2_static(Func func_, Arg1 arg1_, Arg2 arg2_): func(func_),
			                                arg1(arg1_), arg2(arg2_) {}
};


// Convenience functions making callback objects on freestore.  These
// can for example be passed as the data argument of the gthread
// functions starting new threads. They are also used by the
// Callback::post() function.

template <class T>
Callback* make(T& t,
	       typename Callback0<T>::MemFunc func) {
  return new Callback0<T>(t, func);
}

template <class T, class Arg1>
Callback* make(T& t,
	       typename Callback1<T, Arg1>::MemFunc func,
	       Arg1 arg1) {
  return new Callback1<T, Arg1>(t, func, arg1);
}

template <class T, class Arg1, class Arg2>
Callback* make(T& t,
	       typename Callback2<T, Arg1, Arg2>::MemFunc func,
	       Arg1 arg1,
	       Arg2 arg2) {
  return new Callback2<T, Arg1, Arg2>(t, func, arg1, arg2);
}

/* const versions, for binding to const methods */

template <class T>
Callback* make(const T& t,
	       typename Callback0_const<T>::MemFunc func) {
  return new Callback0_const<T>(t, func);
}

template <class T, class Arg1>
Callback* make(const T& t,
	       typename Callback1_const<T, Arg1>::MemFunc func,
	       Arg1 arg1) {
  return new Callback1_const<T, Arg1>(t, func, arg1);
}

template <class T, class Arg1, class Arg2>
Callback* make(const T& t,
	       typename Callback2_const<T, Arg1, Arg2>::MemFunc func,
	       Arg1 arg1,
	       Arg2 arg2) {
  return new Callback2_const<T, Arg1, Arg2>(t, func, arg1, arg2);
}

/* for static class methods and non-class functions */

inline Callback* make_static(Callback0_static::Func func) {
  return new Callback0_static(func);
}

template <class Arg1>
Callback* make_static(typename Callback1_static<Arg1>::Func func,
		      Arg1 arg1) {
  return new Callback1_static<Arg1>(func, arg1);
}

template <class Arg1, class Arg2>
Callback* make_static(typename Callback2_static<Arg1, Arg2>::Func func,
		      Arg1 arg1,
		      Arg2 arg2) {
  return new Callback2_static<Arg1, Arg2>(func, arg1, arg2);
}


/*
The Callback::post() function is intended as a means of passing a
callback from a worker thread to the main program loop for execution
by the main program loop.  In that respect, it provides an alternative
to a Glib::Dispatcher object.  It is passed a pointer to a
Callback::Callback object created with a call to Callback::make.

Advantages as against Glib::Dispatcher:

1. If there are a lot of different events requiring callbacks to be
   dispatched in the program from worker threads to the main thread,
   this avoids having separate Glib::Dispatcher objects for each
   event.

2. It is easier to pass arguments with varying values - they can be
   passed as templated arguments to the Callback::make or
   Callback::make_static functions and no special synchronisation is
   normally required (the call to g_idle_add() invokes locking of the
   main loop which will have the effect of ensuring memory
   visibility).  With a Glib::Dispatcher object it may be necessary to
   use an asynchronous queue to pass variable values (or to bind a
   reference to the data, thus normally requiring separate
   synchronisation).

Disadvantages as against Glib::Dispatcher:

1. Less efficient, as a new callback object has to be created on
   freestore every time the callback is invoked.

2. Where callbacks represent non-static methods of a class, it is
   possible that when the main GUI thread executes the callback, the
   relevant object no longer exists.  Either additional steps need to
   be taken to tie object lifetime to the completion of execution of
   the callback, or the function should only be used for methods of
   objects which it is known will be in existence during the whole of
   the time that the program main loop will be running.  (But with
   Glib::Dispatcher the user must ensure that the particular
   Glib::Dispatcher object invoked remains in existence until the last
   invocation of it has been handled by the receiving thread.)

3. Multiple callbacks relevant to a single event cannot be invoked
   from a single call for the event - each callback has to be
   separately dispatched.
*/


extern "C" gboolean callback_wrapper(void*);

inline gboolean callback_wrapper(void* data) {
  const Callback* cb = static_cast<Callback*>(data);
  try {
    cb->dispatch();
  }
  // we can't propagate exceptions from functions with C linkage
  catch (...) {
    write_error("Exception thrown in callback_wrapper() for Callback::post() function\n");
  }
  delete cb;
  return false;
}

inline void post(const Callback* cb) {
  g_idle_add(callback_wrapper, const_cast<Callback*>(cb));
}

} // namespace Callback

#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]