Threads in mms. A C++ class wrapper around pthread.

Everything about the new development branch of mms

Moderator: Moderator

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Fri Mar 13, 2009 12:57 pm

Threads in mms code are sometimes handled via ost::thread and sometimes via pthreads (use of pthreads are to blame on me, because I do hate ost::threads). There are advantages and disadvantages in both approaches.

Ost::thread is 100% C++ stuff, while the pthreads interface is 100% old school C.
Pthreads require defining the thread entry-point in a C manner; if the entry point is a class method, you have to define it static, but this is just a workaround that happens to work only because gcc handles C and C++ calls in the same way (I am speaking of parameter orders).
Pthreads allows much better thread control than ost::thread: the latter is so abstract that you never know what really happens under the hood.
The pthread_create() pthreads call uses a C like convention that forces the developer to do some gymnic with casting, which triggers warnings at compile time.

Just to make pthread closer to C++, yesterday I coded a small pthread wrapper, a class that should address all the issues I listed above. Here it is:

Code: Select all

extern "C" void* wrapper_prerun(void*);
class ThreadWrapperBase{
    pthread_t t_id;
    friend void* wrapper_prerun(void*);
    virtual void thread_entry() = 0;
  protected:
    void * pthis;
  public:
    void run(void* classptr) {
      pthis = classptr;
      pthread_create(&t_id, NULL, wrapper_prerun, this);
    }
    void join() {
      pthread_join(t_id, NULL);
    }
    pthread_t* get_thread_id(){
      return &t_id;
    }
};

void* wrapper_prerun(void* state) {
  reinterpret_cast<ThreadWrapperBase*>(state)->thread_entry();
  return 0;
}

template <typename T, void (T::*M)()>
  class ThreadWrapper : public ThreadWrapperBase {
    void thread_entry(){
      T* pt = static_cast<T*>(pthis);
      (pt->*M)();
    }
  public:
    ThreadWrapper(){
    }

    virtual ~ThreadWrapper(){};
  };


Most pthread wrappers that you find on the Internet are rigid in the sense that require that you derive your class from the wrapper class, overload some member of the latter, etc.

This one doesn't.

Here's a simple proggie that makes use of it:

Code: Select all

class foo {
  private:
  void dosomething1(){
    fprintf (stderr, "thread 1\n");
  }
  void dosemething2(){
    fprintf (stderr, "thread 2\n");
  }
  public:
  ThreadWrapper<foo, &foo::dosomething1> tw1; /* we create a ThreadWrapper object for the class foo
                                               where the thread entry point is foo::dosemething1() */

  ThreadWrapper<foo, &foo::dosemething2> tw2; /* we create another one where the thread entry point
                                                 is foo::dosemething2() */
  foo(){};
  ~foo(){};
};

int main(void) {

  foo * azz = new foo; /* creates our example class */

  azz->tw1.run(&azz); /* launch the 1st thread; note that you have to pass the class base pointer to it */

  azz->tw2.run(&azz); /* launch the 2nd thread; note that you have to pass the class base pointer to it */

  azz->tw1.join(); /* waits for 1st the thread to end */
  azz->tw2.join(); /* waits for 2nd the thread to end */

  delete azz; /* clean-up */

  return 0;
}


Please, let me know what you think of it. Obviously it can be improved, but if you agree on the concept, I'll start adding it to mms.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

User avatar
arj
Site Admin
Posts: 2316
Joined: Thu Dec 01, 2005 8:51 pm
Location: Denmark
Contact:

Re: Threads in mms. A C++ class wrapper around pthread.

Postby arj » Fri Mar 13, 2009 1:35 pm

I have been thinking the same thing lately that two thread approaches is not very good. I really like this approach as it seems to have the best of both worlds. The implementation is also very clean and small which is nice. I couldn't find any problems but just looking at the code.

User avatar
magicamun
master
Posts: 688
Joined: Thu Feb 28, 2008 7:42 am
Location: Germany

Re: Threads in mms. A C++ class wrapper around pthread.

Postby magicamun » Fri Mar 13, 2009 2:42 pm

what would be the benefits of that wrapper regarding functionality/handling of thread-programming inside mms ? I didn't get it fully - sry.

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Fri Mar 13, 2009 4:57 pm

That wrapper encapsulates pthreads into a C++ class. It solves a few problems with the current use of pthreads inside mms (see the 1st post in this thread) and adds the benefits of OO to pthreads, including destructors that, for example, automatically free pthread mutexes and pthread conditions at exit.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Sat Mar 14, 2009 8:48 pm

Ok, this is the README from the pthread library I have coded. Please let me know what you think.


A C++ pthread wrapper for MMS.

It contains the following templates/classes:

template <typename T, void (T::*M)()>
class ThreadWrapper

typename T is the class type whose member is the entry point of the new thread, e.g. myclass.

void (T::*M)() is the entry point. It must be defined as follows: void myclass::myfun()

ThreadWrapper is a wrapper for pthread_create() and pthread_join(). Its members are:
ThreadWrapper(bool join_at_exit = true); it's the constructor. If join_at_exit is true, its destructor
will join the thread.

void run(void* classptr); it launches the new thread; classptr is the pointer to the base class
whose member is the entry point of the said thread.

void join(); It joins the thread.

Care has been taken to prevent running or joining the same thread twice. Don't relay on the destructor calling join() for you.
When the destructor is executed, there's no guarantee that the running thread can access valid data.


Example of usage:

Code: Select all

class foo {
  private:
  void dosomething1(){
    sleep(1);
    fprintf (stderr, "thread 1\n");
  }

  void dosomething2(){
    fprintf (stderr, "thread 2\n");
  }
  public:
  ThreadWrapper<foo, &foo::dosomething1> tw1; /* we create a ThreadWrapper object for the class foo
                                               where the thread entry point is foo::dosemething1() */
  ThreadWrapper<foo, &foo::dosomething2> tw2; /* we create another one where the thread entry point
                                                 is foo::dosomething2() */

  foo(){};
  ~foo(){};
};

int main(void) {

  foo * azz = new foo; /* creates our example class */

  azz->tw1.run(&azz); /* launch the 1st thread; note that you have to pass the class base pointer to it */
  azz->tw2.run(&azz); /* launch the 2nd thread; note that you have to pass the class base pointer to it */
  fprintf(stderr, "ciaoooooooooooooooooooo\n");

  azz->tw1.join(); /* waits for 1st the thread to end */
  azz->tw2.join(); /* waits for 2nd the thread to end */

  delete azz; /* clean-up */

  return 0;
}



class MutexWrapper
MutexWrapper is a wrapper for a pthread "fast" (PTHREAD_MUTEX_FAST_NP) mutex. The mutex is initted by the class constructor
and cleaned by its destructor. Its members are:
int lock(); it calls pthread_mutex_lock()
int unlock(); it calls pthread_mutex_unlock()
int trylock(); it calls pthread_mutex_trylock()


template <int I>
class MutexWrapperEx
MutexWrapperEx is like MutexWrapper, but it lets the developer choose the kind of mutex it needs. Int I must be one of
PTHREAD_MUTEX_FAST_NP, PTHREAD_MUTEX_RECURSIVE_NP or PTHREAD_MUTEX_ERRORCHECK_NP.


class CondMutexWrapper
CondMutexWrapper is like MutexWrapper but it also contains a pthread_cond_t condition. It has the same members as MutexWrapper, plus
the followings:
int cond_wait();
int cond_timedwait(const struct timespec& abstime);
int cond_signal();
int cond_broadcast();

The developer must explicitly lock the mutex calling lock() before calling the members above, and call unlock() when he is done.
The necessary cleanup is done in the destructor.


template< int I >
struct CondMutexWrapperEx
It combines a MutexWrapperEx class to a wrapper for pthread_cond_t condition. See CondMutexWrapper and MutexWrapperEx.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

User avatar
arj
Site Admin
Posts: 2316
Joined: Thu Dec 01, 2005 8:51 pm
Location: Denmark
Contact:

Re: Threads in mms. A C++ class wrapper around pthread.

Postby arj » Mon Mar 16, 2009 7:17 pm

Nice! My minor nitpick of course :D

Don't relay on the destructor calling join() for you. -> Don't rely on the destructor calling join() for you.

Will the mutex class automatically unlock itself once it goes out of scope (meaning in the deconstructor?) or doesn't that make sense (I'm thinking how pthread defines that).

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Tue Mar 17, 2009 9:27 am

arj wrote:Will the mutex class automatically unlock itself once it goes out of scope (meaning in the deconstructor?) or doesn't that make sense (I'm thinking how pthread defines that).


Yes, it automatically unlock itself, but it is not something you can count on. An example will show you why.

Let's say we have MutexWrapper "mutex" created by thread A and shared between thread A and thread B.

1) thread A locks the mutex and does its stuff
2) at the same time thread B tries to lock mutex, but the latter is in use, so thread B waits
3) Thread A ends abruptly for some reason (bug, un-handled signal...) and doesn't have a chance to unlock the mutex. Thread B is still suspended while the destructor is called.
4) mutex is unlocked by its destructor, but the same destructor invalidates it! Thread B wakes up and does its stuff and when it's done calls mutex.unlock() because it's the right thing to do. However mutex, at that time, is no longer a valid object and so mayhem and chaos happen :/
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

User avatar
magicamun
master
Posts: 688
Joined: Thu Feb 28, 2008 7:42 am
Location: Germany

Re: Threads in mms. A C++ class wrapper around pthread.

Postby magicamun » Tue Mar 17, 2009 7:16 pm

lorenzodes - once you have finished work on that and have put the stuff to 1.2.0 i'll adapt "my" plugins accordingly. Perhaps i need some explanation on the "Join"-Stuff.

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Wed Mar 18, 2009 10:03 am

magicamun wrote:lorenzodes - once you have finished work on that and have put the stuff to 1.2.0 i'll adapt "my" plugins accordingly. Perhaps i need some explanation on the "Join"-Stuff.


Work is done already, I am just trying to decide if we need a RAII approach to mutex locks. A RAII approach is what boost::thread uses for mutexes and it works like this:

1) you create and instantiate the mutex
2) instead of calling mutex.lock(), you create a new object (let's call it Lock) that automatically locks the mutex when it is instantiated and frees is when it is destroyed.

I am not fond of such idea, because it allows for sloppy coding and it can't be implemented everywhere in our codebase, although it can be of some limited use.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Wed Mar 18, 2009 12:49 pm

I have added the scope dependant mutex lock, so that no-one can say we don't like the RAII approach :P

Code: Select all

#include <string>
#include <cstdio>
#include "ThreadWrapper.hpp"

class foo {
  public:
    MutexWrapper uffi;
  private:
  void dosomething1(){
    uffi.lock();
    fprintf (stderr, "thread 1\n");
    uffi.unlock();
  }

  void dosomething2(){
    for (int t = 0; t < 5; ++t){
      ScopeLockWrapper locker(&uffi); /* mutex uffi is automatically unlocked when locker goes out of scope */
      fprintf (stderr, "thread 2\n");
    }
  }
  public:
  ThreadWrapper<foo, &foo::dosomething1> tw1; /* we create a ThreadWrapper object for the class foo
                                               where the thread entry point is foo::dosemething1() */

  ThreadWrapper<foo, &foo::dosomething2> tw2; /* we create another one where the thread entry point
                                                 is foo::dosemething2() */

  foo(){}
  ~foo(){}
};


int main(void) {

  foo * azz = new foo; /* creates our example class */
  azz->uffi.lock();
  azz->tw1.run(azz); /* launch the 1st thread; note that you have to pass the class base pointer to it */
  azz->tw2.run(azz); /* launch the 2nd thread; note that you have to pass the class base pointer to it */
  azz->uffi.unlock(); /* unlocks the mutex so that both threads can start */

  azz->tw1.join(); /* waits for the 1st thread to end */
  azz->tw2.join(); /* waits for the 2nd thread to end */
  return 0;
}

"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

User avatar
arj
Site Admin
Posts: 2316
Joined: Thu Dec 01, 2005 8:51 pm
Location: Denmark
Contact:

Re: Threads in mms. A C++ class wrapper around pthread.

Postby arj » Wed Mar 18, 2009 3:20 pm

Sounds good :D

phelin
Posts: 46
Joined: Thu Oct 23, 2008 7:49 pm

Re: Threads in mms. A C++ class wrapper around pthread.

Postby phelin » Tue Mar 24, 2009 7:29 pm

Should this stuff be in the 1.2.0 branch? I got only ThreadWrapper.hpp, not ThreadWrapper.cpp when I checked out the 1.2.0 branch. At the moment I cannot check out anymore (bzr just sits at Transferring:Walking content. 3077/3077) so I cannot confirm.

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Tue Mar 24, 2009 7:43 pm

phelin wrote:Should this stuff be in the 1.2.0 branch? I got only ThreadWrapper.hpp, not ThreadWrapper.cpp when I checked out the 1.2.0 branch. At the moment I cannot check out anymore (bzr just sits at Transferring:Walking content. 3077/3077) so I cannot confirm.


Fixed. My mistake.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"

User avatar
magicamun
master
Posts: 688
Joined: Thu Feb 28, 2008 7:42 am
Location: Germany

Re: Threads in mms. A C++ class wrapper around pthread.

Postby magicamun » Sat Mar 28, 2009 1:23 pm

On Exit of mms i get this and a core - dump:

Code: Select all

Terminating inotify...[Thread 0xabe2cb90 (LWP 10585) exited]
Done.
Terminating background tasks... Done.
Terminating plugins...RENDER: Preparing new image
RENDER: Drawing whole background
[Thread 0xb6041b90 (LWP 10568) exited]
[Thread 0xb6c8bb90 (LWP 10567) exited]
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_audio.so'
[Thread 0xace2eb90 (LWP 10583) exited]
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_movie.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_movie_collection.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_listener.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_pictures.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_python.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_rip.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_tv.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_vbox.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_weather.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_epg.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_audio_player_gst.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_audio_player_xine.so'
[Thread 0xade30b90 (LWP 10581) exited]
xine_dispose
shutdown audio
[Thread 0xae631b90 (LWP 10580) exited]
shutdown video
[Thread 0xaefffb90 (LWP 10579) exited]
[Thread 0xafa00b90 (LWP 10578) exited]
[Thread 0xad62fb90 (LWP 10582) exited]
xine_exit: bye!
[Thread 0xb031db90 (LWP 10577) exited]
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_genericplayer.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_mplayer.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_xine.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_ainput_keyboard.so'
[Thread 0xb0b79b90 (LWP 10576) exited]
Destroying plugin '/usr/local/lib/mms/plugins/lib_ainput_lirc.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_ainput_replay.so'
Destroying plugin '/usr/local/lib/mms/plugins/lib_output_opengl.so'
[Thread 0xb4cdbb90 (LWP 10572) exited]
Destroying plugin '/usr/local/lib/mms/plugins/lib_output_sdl.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_audio.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_movie.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_movie_collection.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_listener.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_pictures.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_python.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_rip.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_tv.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_vbox.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_weather.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_epg.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_audio_player_gst.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_audio_player_xine.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_genericplayer.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_mplayer.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_feature_movie_player_xine.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_ainput_keyboard.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_ainput_lirc.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_ainput_replay.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_output_opengl.so'
Unloading plugin '/usr/local/lib/mms/plugins/lib_output_sdl.so'
Done.
Cleaning up child processes (if any)
Exiting...

Program exited normally.


!!!! CRASH !!!!
This is MMS v. 1.1.0 revno: 2159, build options:
--enable-vbox --enable-weather --enable-gst-audio --enable-opengl --enable-tv --use-internal-ffmpeg --enable-notify-area --enable-debug --enable-python --enable-lirc --enable-rip --enable-listener
--------------------------------------------------
Thread id:             10599
Memory address:        0xb6c8bbd8
Signal code:           0x1
Signal error number:   0x0
--------------------------------------------------
MMS has encountered an unrecoverable error ( Speicherzugriffsfehler ) and is about to end.
Further details: Address not mapped to object.

Please post the information above along with a detailed bug-report
to our bug database: http://bugs.mymediasystem.org/
Also, unless you set your system to do otherwise, a core dump is about to be
created. Load it with gdb as follows:
  $ gdb mms core.10599
Then create a full backtrace as follows:
  (gdb) thread apply all bt
Copy and add the backtrace to the bug report.
For more information about reporting bugs for MMS, please refer to:
http://wiki.mymediasystem.org/wiki/index.php/Bug_reports

So Long, and Thanks for All the Fish


(gdb) bt
No stack.
(gdb) bt full
No stack.
(gdb) quit


gdb mms core gives this :

Code: Select all

(gdb) thread apply all bt

Thread 1 (process 10599):
#0  0xb7e06479 in pthread_join () from /lib/libpthread.so.0
#1  0x081398c6 in ThreadWrapper<Updater, &(Updater::run())>::join (
    this=0x81eef98) at ThreadWrapper.hpp:166
#2  0x081398fe in ~ThreadWrapper (this=0x81eef98) at ThreadWrapper.hpp:175
#3  0x0813678c in ~Updater (this=0x81eeea0) at updater.cpp:39
#4  0x080ed973 in ~ScreenUpdater (this=0x81eeea0) at ../../updater.hpp:162
#5  0x080e3bc8 in __tcf_0 () at singleton.hpp:12
#6  0xb7999599 in exit () from /lib/libc.so.6
#7  0x080e4d6e in mmsSignals::deinit_mms (this=0x81eea20, full_deinit=true)
    at common.cpp:1274
#8  0x080e4e40 in mmsSignals::run (this=0x81eea20) at common.cpp:1196
#9  0x080e4e59 in mmsSignals::pre_run (ptr=0x81eea20) at common.cpp:1172
#10 0xb7e05192 in start_thread () from /lib/libpthread.so.0
#11 0xb7a3002e in clone () from /lib/libc.so.6
(gdb)

lorenzodes
master
Posts: 772
Joined: Sun Mar 11, 2007 4:50 pm
Location: move.l 4.w,a6

Re: Threads in mms. A C++ class wrapper around pthread.

Postby lorenzodes » Sat Mar 28, 2009 3:14 pm

I knew that could happen, it depends on the order the various destructors are called when mms exits. No problem, calling tthread.join() explicitly should fix it.
"I’m not frightened of dying, anytime will do, I don’t mind. Why should I be frightened of dying? There’s no reason for it, you gotta go sometime"


Return to “1.2.x”

Who is online

Users browsing this forum: No registered users and 1 guest