Posix thread exiting and destructors

Discussion in 'C++' started by Boltar, Apr 10, 2007.

  1. Boltar

    Boltar Guest

    Hi

    I'm writing a threading class using posix threads on unix with each
    thread being run by an object instance. One thing I'm not sure about
    is , if I do the following:

    myclass::~myclass()
    {
    :
    : do stuff
    :
    pthread_exit(&status);
    }


    If I exit the thread at the end of the destructor will all the memory
    used for the object be free'd? My suspicion is it won't and I actually
    need to exit the thread outside of the object itself, but I thought
    I'd check on here first.

    Thanks for any help

    B2003
    Boltar, Apr 10, 2007
    #1
    1. Advertising

  2. On 10 Apr, 10:31, "Boltar" <> wrote:
    > Hi
    >
    > I'm writing a threading class using posix threads on unix with each
    > thread being run by an object instance. One thing I'm not sure about
    > is , if I do the following:
    >
    > myclass::~myclass()
    > {
    > :
    > : do stuff
    > :
    > pthread_exit(&status);
    >
    > }
    >
    > If I exit the thread at the end of the destructor will all the memory
    > used for the object be free'd? My suspicion is it won't and I actually
    > need to exit the thread outside of the object itself, but I thought
    > I'd check on here first.


    All memory used by an object will be freed after the destructor of the
    object has been run. Beware that any memory allocated with new should
    be deleted, all non-pointer members will be correctly dealt with
    automatically. If you are wondering if all memory used by the thread
    (such as TLS) will be freed on pthread_exit() you should ask in a
    group for POSIX threads since it's not part of the C++ language.

    --
    Erik Wikström
    =?iso-8859-1?q?Erik_Wikstr=F6m?=, Apr 10, 2007
    #2
    1. Advertising

  3. Boltar

    Boltar Guest

    On Apr 10, 9:38 am, "Erik Wikström" <>
    wrote:
    > All memory used by an object will be freed after the destructor of the
    > object has been run. Beware that any memory allocated with new should
    > be deleted, all non-pointer members will be correctly dealt with
    > automatically. If you are wondering if all memory used by the thread
    > (such as TLS) will be freed on pthread_exit() you should ask in a
    > group for POSIX threads since it's not part of the C++ language.


    No , I was just concerned about the memory of the object itself. If
    the thread exits , will the code that free's the object actually be
    called since the execution context no longer exists?

    B2003
    Boltar, Apr 10, 2007
    #3
  4. Boltar wrote:
    > On Apr 10, 9:38 am, "Erik Wikström" <>
    > wrote:
    >
    >>All memory used by an object will be freed after the destructor of the
    >>object has been run. Beware that any memory allocated with new should
    >>be deleted, all non-pointer members will be correctly dealt with
    >>automatically. If you are wondering if all memory used by the thread
    >>(such as TLS) will be freed on pthread_exit() you should ask in a
    >>group for POSIX threads since it's not part of the C++ language.

    >
    >
    > No , I was just concerned about the memory of the object itself. If
    > the thread exits , will the code that free's the object actually be
    > called since the execution context no longer exists?


    Don't call exit there. You'll need to call pthread_join at least.

    This is more on topic for news:comp.programming.threads so I suggest you
    go there.

    Also, it's important to have an ability to wait on a thread's exit. If
    you have other threads looking at your thread object asking to be
    awoken, how do you do that ?

    This code shows how I did it, it works well...
    http://www.google.com/codesearch?hl...c/austria/posix/code/at_posix_thread_cpp.i#a0

    Take a look at TaskContext::start_routine

    It is the responsibility of other threads to delete the thread object.

    The austria c++ stuff is made such that no system headers are needed in
    application code and it is implemented for win32 and posix.

    For a topcoder app, I used the austria C++ api but I ripped out the
    layer that hit pthreads. It should be easier to tell what's going on here.

    http://www.topcoder.com/longcontest/?module=ViewProblemSolution&pm=6217&rd=9974&cr=20908764&subnum=4

    #include <pthread.h>

    namespace tc
    {
    // All the posix thread stuff goes here - API conforms to Austria C++

    // ======== MutexAttr =================================================
    class MutexAttr
    {
    public:

    MutexAttr( int i_kind )
    {
    pthread_mutexattr_init( m_attr );
    if ( pthread_mutexattr_settype( m_attr, i_kind ) != 0 )
    {
    abort();
    }
    }

    ~MutexAttr()
    {
    pthread_mutexattr_destroy( m_attr );
    }

    pthread_mutexattr_t * GetAttr()
    {
    return m_attr;
    }

    pthread_mutexattr_t m_attr[ 1 ];

    };

    MutexAttr g_MA_Fast( PTHREAD_MUTEX_FAST_NP );
    MutexAttr g_MA_Recursive( PTHREAD_MUTEX_RECURSIVE_NP );
    MutexAttr g_MA_Check( PTHREAD_MUTEX_ERRORCHECK_NP );

    // ======== Mutex =====================================================

    class Conditional;
    class Mutex
    {

    public:
    friend class Conditional;

    // ======== MutexType =============================================

    enum MutexType
    {
    NonRecursive,

    Recursive,

    Checking
    };


    // ======== Mutex =================================================

    Mutex( MutexType i_type = NonRecursive )
    {
    pthread_mutex_t * l_mutex = m_mutex_context.m_data;

    switch ( i_type )
    {
    case NonRecursive :
    {
    int l_result = pthread_mutex_init( l_mutex,
    g_MA_Fast.GetAttr() );

    if ( l_result != 0 )
    {
    abort();
    }

    break;
    }
    case Recursive :
    {
    int l_result = pthread_mutex_init( l_mutex,
    g_MA_Recursive.GetAttr() );

    if ( l_result != 0 )
    {
    abort();
    }

    break;
    }
    case Checking :
    {
    int l_result = pthread_mutex_init( l_mutex,
    g_MA_Check.GetAttr() );

    if ( l_result != 0 )
    {
    abort();
    }

    break;
    }
    default :
    {
    abort();
    }
    }
    }



    // ======== Mutex =================================================

    virtual ~Mutex()
    {
    pthread_mutex_t * l_mutex = m_mutex_context.m_data;

    int l_result = pthread_mutex_destroy( l_mutex );

    if ( l_result != 0 )
    {
    // trying to destroy a mutex that is locked
    abort();
    }
    }


    // ======== Lock ==================================================

    void Lock()
    {
    pthread_mutex_t * l_mutex = m_mutex_context.m_data;

    int l_result = pthread_mutex_lock( l_mutex );

    if ( l_result != 0 )
    {
    if ( l_result == EINVAL )
    {
    abort();
    }

    if ( l_result == EDEADLK )
    {
    abort();
    }

    abort();
    }
    }


    // ======== TryLock ===============================================

    bool TryLock()
    {
    pthread_mutex_t * l_mutex = m_mutex_context.m_data;

    int l_result = pthread_mutex_trylock( l_mutex );

    if ( EBUSY == l_result )
    {
    return false;
    }

    if ( l_result != 0 )
    {
    if ( l_result == EINVAL )
    {
    abort();
    }

    abort();
    }

    return true;
    }


    // ======== Unlock ================================================

    void Unlock()
    {
    pthread_mutex_t * l_mutex = m_mutex_context.m_data;

    int l_result = pthread_mutex_unlock( l_mutex );

    if ( l_result != 0 )
    {
    if ( l_result == EINVAL )
    {
    abort();
    }

    if ( l_result == EPERM )
    {
    abort();
    }

    abort();
    }
    }

    struct MutexContext
    {
    pthread_mutex_t m_data[1];
    };

    private:

    /**
    * m_mutex_context is a system dependant context variable.
    */

    MutexContext m_mutex_context;

    // copy constructor and assignment operator are private and
    // unimplemented. It is illegal to copy a mutex.
    Mutex( const Mutex & );
    Mutex & operator= ( const Mutex & );
    };

    // ======== Conditional ===============================================
    // condition variable wrapper

    class Conditional
    {
    public:

    // ======== Conditional ===========================================

    Conditional( Mutex & i_mutex )
    : m_mutex( i_mutex.m_mutex_context.m_data )
    {
    int l_result = pthread_cond_init(
    m_cond,
    static_cast<const pthread_condattr_t *>( 0 )
    );

    if ( l_result != 0 )
    {
    abort();
    }

    }

    // destructor
    virtual ~Conditional()
    {
    int l_result = pthread_cond_destroy(
    m_cond
    );

    if ( l_result != 0 )
    {
    abort();
    }
    }

    // ======== Wait ==================================================
    void Wait()
    {
    int l_result = pthread_cond_wait( m_cond, m_mutex );

    if ( l_result != 0 )
    {
    abort();
    }
    }

    // ======== Post ==================================================
    void Post()
    {
    int l_result = pthread_cond_signal( m_cond );

    if ( l_result != 0 )
    {
    abort();
    }
    }


    // ======== PostAll ===============================================
    void PostAll()
    {
    int l_result = pthread_cond_broadcast( m_cond );

    if ( l_result != 0 )
    {
    abort();
    }
    }


    private:

    pthread_mutex_t * m_mutex;
    pthread_cond_t m_cond[ 1 ];

    // copy constructor and assignment operator are private and
    // unimplemented. It is illegal to copy a Conditional.
    Conditional( const Conditional & );
    Conditional & operator= ( const Conditional & );

    };

    // ======== ConditionalMutex ==========================================

    class ConditionalMutex
    : public Mutex,
    public Conditional
    {
    public:


    // ======== ConditionalMutex ======================================

    ConditionalMutex( MutexType i_type = NonRecursive )
    : Mutex( i_type ),
    Conditional( * static_cast< Mutex * >( this ) )
    {
    }


    virtual ~ConditionalMutex() {}

    private:
    ConditionalMutex( const ConditionalMutex & );
    ConditionalMutex & operator= ( const ConditionalMutex & );
    };


    // ======== Lock ================================================

    template <typename w_MutexType>
    class Lock
    {
    public:

    w_MutexType & m_mutex;

    Lock( w_MutexType & io_mutex )
    : m_mutex( io_mutex )
    {
    m_mutex.Lock();
    }

    ~Lock()
    {
    m_mutex.Unlock();
    }

    void Wait()
    {
    return m_mutex.Wait();
    }

    void Post()
    {
    m_mutex.Post();
    }

    void PostAll()
    {
    m_mutex.PostAll();
    }

    private:

    // must not allow copy or assignment so make
    // these methods private.
    Lock( const Lock & );
    Lock & operator=( const Lock & );
    };

    // ======== Unlock =============================================

    template <typename w_MutexType>
    class Unlock
    {
    public:

    w_MutexType & m_mutex;

    Unlock( w_MutexType & io_mutex )
    : m_mutex( io_mutex )
    {
    m_mutex.Unlock();
    }

    ~Unlock()
    {
    m_mutex.Lock();
    }

    private:

    // must not allow copy or assignment so make
    // these methods private.
    Unlock( const Unlock & );
    Unlock & operator=( const Unlock & );
    };

    // ======== TryLock ===================================================
    template< typename w_MutexType >
    class TryLock
    {
    w_MutexType & m_mutex;

    bool m_is_acquired;

    public:

    TryLock( w_MutexType & io_mutex )
    : m_mutex( io_mutex ),
    m_is_acquired( false )
    {

    m_is_acquired = m_mutex.TryLock();
    }

    inline ~TryLock()
    {
    if ( m_is_acquired )
    {
    m_mutex.Unlock();
    }
    }

    void SetAquired( bool i_is_acquired )
    {
    m_is_acquired = i_is_acquired;
    }

    bool IsAcquired() const
    {
    return m_is_acquired;
    }

    private:

    /* Unimplemented. */
    TryLock( const TryLock & );
    TryLock & operator=( const TryLock & );

    };


    // ======== Task ======================================================

    class Task
    {
    public:

    typedef int TaskID;


    // ======== Task ==================================================

    Task()
    : m_started( false ),
    m_completed( false ),
    m_is_joined( false )
    {
    int l_result = pthread_create(
    & m_thread_id,
    static_cast<const pthread_attr_t *>( 0 ),
    & start_routine,
    static_cast<void *>( this )
    );

    if ( 0 != l_result )
    {
    abort();
    }
    }

    // ======== ~Task =================================================

    virtual ~Task()
    {
    Wait();
    }


    // ======== Work ==================================================

    virtual void Work() = 0;

    // ======== Start =================================================

    void Start()
    {
    if ( ! m_started )
    {
    // Wake this thread
    Lock<ConditionalMutex> l_lock( m_thread_cond_mutex );

    m_started = true;
    l_lock.Post();
    }
    }

    // ======== Wait ==================================================

    void Wait()
    {
    if ( ! m_is_joined )
    {
    // Wait here to be started
    Lock<ConditionalMutex> l_lock( m_wait_cond_mutex );

    while ( ! m_completed )
    {
    l_lock.Wait();
    }

    // Need to call join here ...

    if ( ! m_is_joined )
    {
    m_is_joined = true;

    void * l_return_value;

    int l_result = pthread_join(
    m_thread_id,
    & l_return_value
    );

    if ( 0 != l_result )
    {
    abort();
    }
    }

    } // l_lock is unlocked here

    }

    // ======== GetThisId =============================================

    TaskID GetThisId()
    {
    return m_thread_id;
    }


    // ======== GetSelfId =============================================

    static TaskID GetSelfId()
    {
    return ::pthread_self();
    }

    private:

    //
    // Can't copy a task.
    Task( const Task & );
    Task & operator= ( const Task & );

    pthread_t m_thread_id;

    volatile bool m_started;

    volatile bool m_completed;

    volatile bool m_is_joined;

    ConditionalMutex m_thread_cond_mutex;

    ConditionalMutex m_wait_cond_mutex;


    static void * start_routine( void * i_task )
    {
    Task * l_this_task = static_cast<Task *>( i_task );

    {
    // Wait here to be started
    Lock<ConditionalMutex> l_lock(
    l_this_task->m_thread_cond_mutex );

    while ( ! l_this_task->m_started )
    {
    l_lock.Wait();
    }
    }

    // do the work ...
    l_this_task->Work();

    {
    // Wake all the waiters.
    Lock<ConditionalMutex> l_lock(
    l_this_task->m_wait_cond_mutex );

    l_this_task->m_completed = true;
    l_lock.PostAll();
    }

    return 0;
    }


    };


    // ======== Barrier ===================================================

    class Barrier
    {
    public:

    Barrier(
    unsigned i_thread_count,
    ConditionalMutex & i_cond_mutex
    )
    : m_thread_count( i_thread_count ),
    m_cond_mutex( i_cond_mutex ),
    m_count()
    {
    }

    unsigned Enter()
    {
    unsigned l_num;
    Lock<ConditionalMutex> l_lock( m_cond_mutex );
    l_num = m_count ++;

    if ( ( m_thread_count - 1 ) == l_num )
    {
    l_lock.PostAll();
    }
    else
    {
    l_lock.Wait();
    }
    return l_num;
    }

    unsigned m_thread_count;
    ConditionalMutex & m_cond_mutex;
    volatile unsigned m_count;
    };

    } // namespace tc
    Gianni Mariani, Apr 10, 2007
    #4
  5. Boltar

    James Kanze Guest

    On Apr 10, 10:31 am, "Boltar" <> wrote:

    > I'm writing a threading class using posix threads on unix with each
    > thread being run by an object instance. One thing I'm not sure about
    > is , if I do the following:


    > myclass::~myclass()
    > {
    > :
    > : do stuff
    > :
    > pthread_exit(&status);
    > }


    > If I exit the thread at the end of the destructor will all the memory
    > used for the object be free'd?


    Maybe. C++ doesn't define threading, so it doesn't say what
    would happen here, and Posix doesn't define a C++ binding, so
    each implementation is on its own. In a somewhat similar (but
    not precisely identical) situation, I found that Sun CC and g++
    (both under Solaris) behaved differently.

    > My suspicion is it won't and I actually need to exit the
    > thread outside of the object itself, but I thought I'd check
    > on here first.


    I'd forget about pthread_cancel and pthread_exit within C++.
    Make other arrangements, throwing an exception when you want to
    terminate. Catch the exception in the `extern "C"' function you
    used to start the thread, and go on from there.

    It's also not too clear to me what kind of a class could want to
    do an exit in its destructor. pthread_exit has semantics more
    or less like throwing an exception, and that's generally
    something you want to avoid in a destructor. Please explain
    what you're trying to do.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 10, 2007
    #5
  6. Boltar

    bjeremy Guest

    James Kanze wrote:
    > On Apr 10, 10:31 am, "Boltar" <> wrote:
    >
    > > I'm writing a threading class using posix threads on unix with each
    > > thread being run by an object instance. One thing I'm not sure about
    > > is , if I do the following:

    >
    > > myclass::~myclass()
    > > {
    > > :
    > > : do stuff
    > > :
    > > pthread_exit(&status);
    > > }

    >
    > > If I exit the thread at the end of the destructor will all the memory
    > > used for the object be free'd?

    >


    I wasn't 100% on what you were trying to do... but was this close?

    #ifndef Thread_H
    #define Thread_H

    #define _REENTRANT

    class Thread
    {
    pthread_t thread;
    static void * dispatch(void *);

    protected:
    virtual void run() = 0;
    public:
    virtual ~Thread(){}
    void start();
    void join();
    };

    #endif

    ..cpp file
    -------------------------
    #include "Thread.h"

    void * Thread::dispatch(void * ptr)
    {
    if (!ptr)
    return 0;

    static_cast<Thread *>(ptr)->run();
    pthread_exit(ptr);

    return 0;
    }

    void Thread::start()
    {
    pthread_create(&thread, 0, Thread::dispatch, this);
    }

    void Thread::join()
    {
    pthread_join(thread, (void **) 0);
    }

    This is an ABC, as you can see the derived class must implement a
    run() function... When you derived from this, you will call the
    start() method wich will create the pthread.. .the creation will
    callback the start() function which will execute the derived classes
    run(). after run() finishes, pthread_exit() will be called to clean up
    the memory.
    bjeremy, Apr 10, 2007
    #6
  7. Boltar

    Boltar Guest

    On Apr 10, 4:49 pm, "James Kanze" <> wrote:
    > It's also not too clear to me what kind of a class could want to
    > do an exit in its destructor. pthread_exit has semantics more
    > or less like throwing an exception, and that's generally
    > something you want to avoid in a destructor. Please explain
    > what you're trying to do.


    Its a TCP server. Each incoming connection gets its own thread which
    is encapsulated in a connection object. I would have just used fork()
    but I want the threads to access some global stats and I couldn't be
    bothered with the hassle of shared memory and associated locking
    issues. When the session is over or the remote client closes the
    connection the thread has to exit and the object be destroyed. However
    going by my own tests and whats been written here I've now just
    created a procedural function which the object calls which deletes the
    calling object then exits the thread.

    In reponse to another post , I don't need to do a join on an exiting
    thread because they're all running detached. The parent thread doesn't
    need to care about them , its just a fire and forget dispatcher.

    B2003
    Boltar, Apr 10, 2007
    #7
  8. Boltar wrote:
    > On Apr 10, 4:49 pm, "James Kanze" <> wrote:
    >
    >>It's also not too clear to me what kind of a class could want to
    >>do an exit in its destructor. pthread_exit has semantics more
    >>or less like throwing an exception, and that's generally
    >>something you want to avoid in a destructor. Please explain
    >>what you're trying to do.

    >
    >
    > Its a TCP server. Each incoming connection gets its own thread which
    > is encapsulated in a connection object. I would have just used fork()
    > but I want the threads to access some global stats and I couldn't be
    > bothered with the hassle of shared memory and associated locking
    > issues.


    You will still have "locking" issues wether you use shared memory or
    threads (where memory is implicitly shared).

    > ... When the session is over or the remote client closes the
    > connection the thread has to exit and the object be destroyed.


    Considered using a thread pool?

    > ... However
    > going by my own tests and whats been written here I've now just
    > created a procedural function which the object calls which deletes the
    > calling object then exits the thread.
    >
    > In reponse to another post , I don't need to do a join on an exiting
    > thread because they're all running detached. The parent thread doesn't
    > need to care about them , its just a fire and forget dispatcher.


    That's true until you try to exit. The main thread must wait until all
    threads have cleaned up before it exits. You will have strange
    behaviour if you don't.
    Gianni Mariani, Apr 10, 2007
    #8
  9. Boltar

    Boltar Guest

    On Apr 10, 11:00 pm, Gianni Mariani <> wrote:
    > Boltar wrote:
    > > On Apr 10, 4:49 pm, "James Kanze" <> wrote:

    >
    > >>It's also not too clear to me what kind of a class could want to
    > >>do an exit in its destructor. pthread_exit has semantics more
    > >>or less like throwing an exception, and that's generally
    > >>something you want to avoid in a destructor. Please explain
    > >>what you're trying to do.

    >
    > > Its a TCP server. Each incoming connection gets its own thread which
    > > is encapsulated in a connection object. I would have just used fork()
    > > but I want the threads to access some global stats and I couldn't be
    > > bothered with the hassle of shared memory and associated locking
    > > issues.

    >
    > You will still have "locking" issues wether you use shared memory or
    > threads (where memory is implicitly shared).


    True, but with threads you can use mutexes. You can't do that with
    seperate processes (and despite what some people think you can't map
    mutexes to shared memory since if the underlying mutex code uses
    pointers you're screwed). For processes you have to use SysV
    semaphores (yuck) or a locking file (yuck again).

    >
    > > ... When the session is over or the remote client closes the
    > > connection the thread has to exit and the object be destroyed.

    >
    > Considered using a thread pool?


    Can't be bothered :) Its not a high throughput server, the overhead of
    creating/destroying threads to keep the code simple is worth it.

    > That's true until you try to exit. The main thread must wait until all
    > threads have cleaned up before it exits. You will have strange
    > behaviour if you don't.


    If the parent thread ever exits other than via a crash it'll cause an
    entire process halt so thats not an issue.

    B2003
    Boltar, Apr 11, 2007
    #9
  10. Boltar

    Ian Collins Guest

    Boltar wrote:
    > On Apr 10, 11:00 pm, Gianni Mariani <> wrote:
    >
    >>Boltar wrote:
    >>
    >>>On Apr 10, 4:49 pm, "James Kanze" <> wrote:

    >>
    >>>>It's also not too clear to me what kind of a class could want to
    >>>>do an exit in its destructor. pthread_exit has semantics more
    >>>>or less like throwing an exception, and that's generally
    >>>>something you want to avoid in a destructor. Please explain
    >>>>what you're trying to do.

    >>
    >>>Its a TCP server. Each incoming connection gets its own thread which
    >>>is encapsulated in a connection object. I would have just used fork()
    >>>but I want the threads to access some global stats and I couldn't be
    >>>bothered with the hassle of shared memory and associated locking
    >>>issues.

    >>
    >>You will still have "locking" issues wether you use shared memory or
    >>threads (where memory is implicitly shared).

    >
    >
    > True, but with threads you can use mutexes. You can't do that with
    > seperate processes (and despite what some people think you can't map
    > mutexes to shared memory since if the underlying mutex code uses
    > pointers you're screwed). For processes you have to use SysV
    > semaphores (yuck) or a locking file (yuck again).
    >

    <OT>Decent operating systems support process shared mutex and you can
    put them in shared memory.</OT>

    --
    Ian Collins.
    Ian Collins, Apr 11, 2007
    #10
  11. Boltar

    Boltar Guest

    On Apr 11, 9:55 am, Ian Collins <> wrote:
    > <OT>Decent operating systems support process shared mutex and you can
    > put them in shared memory.</OT>


    Maybe , maybe not. Would you be willing to be portability on it always
    working?

    B2003
    Boltar, Apr 11, 2007
    #11
  12. Boltar

    James Kanze Guest

    On Apr 10, 6:17 pm, "bjeremy" <> wrote:

    [...]
    > #ifndef Thread_H
    > #define Thread_H


    > #define _REENTRANT


    > class Thread
    > {
    > pthread_t thread;
    > static void * dispatch(void *);


    > protected:
    > virtual void run() = 0;
    > public:
    > virtual ~Thread(){}
    > void start();
    > void join();
    > };
    > #endif


    > .cpp file
    > -------------------------
    > #include "Thread.h"


    > void * Thread::dispatch(void * ptr)
    > {
    > if (!ptr)
    > return 0;


    > static_cast<Thread *>(ptr)->run();
    > pthread_exit(ptr);


    You don't need to call pthread_exit here---in fact, in many
    cases, you don't want to (since calling pthread_exit will, at
    least in some implementations, prevent the destructors of local
    variables from running). Just return. (Actually, you don't
    want to ever call pthread_exit if you're using C++, because
    implementations---even different compilers on the same
    platform---don't seem to agree as to what it should do.)

    > return 0;
    > }


    > void Thread::start()
    > {
    > pthread_create(&thread, 0, Thread::dispatch, this);


    Attention! This shouldn't compile (although g++ has a bug, and
    doesn't detect the error). The type of function required by
    pthread_create is `extern "C"', and a member function, even
    static, can never be `extern "C"'.

    > }


    > void Thread::join()
    > {
    > pthread_join(thread, (void **) 0);
    > }


    > This is an ABC, as you can see the derived class must implement a
    > run() function... When you derived from this, you will call the
    > start() method wich will create the pthread.. .the creation will
    > callback the start() function which will execute the derived classes
    > run(). after run() finishes, pthread_exit() will be called to clean up
    > the memory.


    What relationship to his question?

    He was talking about calling pthread_exit in a destructor, which
    makes me think that he probably had detached threads in mind.
    For detached threads, I don't use a class at all; just a
    (template) function, which takes a functional object (or a
    pointer to function) as parameter.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 11, 2007
    #12
  13. Boltar

    James Kanze Guest

    On Apr 10, 11:47 pm, "Boltar" <> wrote:
    > On Apr 10, 4:49 pm, "James Kanze" <> wrote:


    > > It's also not too clear to me what kind of a class could want to
    > > do an exit in its destructor. pthread_exit has semantics more
    > > or less like throwing an exception, and that's generally
    > > something you want to avoid in a destructor. Please explain
    > > what you're trying to do.


    > Its a TCP server. Each incoming connection gets its own thread which
    > is encapsulated in a connection object. I would have just used fork()
    > but I want the threads to access some global stats and I couldn't be
    > bothered with the hassle of shared memory and associated locking
    > issues. When the session is over or the remote client closes the
    > connection the thread has to exit and the object be destroyed. However
    > going by my own tests and whats been written here I've now just
    > created a procedural function which the object calls which deletes the
    > calling object then exits the thread.


    Yes. You need the procedural function anyway, to start the
    thread. You might as well delete the object there.

    My real question concerning your initial design, I guess, was
    who was going to call the destructor. A thread is a function,
    an asynchronous function, but a function.

    > In reponse to another post , I don't need to do a join on an exiting
    > thread because they're all running detached. The parent thread doesn't
    > need to care about them , its just a fire and forget dispatcher.


    And of course, you never stop the server, so there's no worry
    about calling exit with running threads (which can cause various
    problems as well):). If not, for a clean shutdown (especially
    given that pthread_cancel doesn't work in C++), you need a
    thread counter, and a flag which signals that shutdown is
    requested, which the threads poll from time to time. The flag
    must be protected by a mutex, and the thread counter by a
    condition variable (since it will be decremented at the end of
    the procedural function, just before the return). Set the flag,
    and wait until the counter is 0, then return from main.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 11, 2007
    #13
  14. Boltar

    James Kanze Guest

    On Apr 11, 10:38 am, "Boltar" <> wrote:
    > On Apr 10, 11:00 pm, Gianni Mariani <> wrote:
    > > Boltar wrote:
    > > > On Apr 10, 4:49 pm, "James Kanze" <> wrote:


    > > >>It's also not too clear to me what kind of a class could want to
    > > >>do an exit in its destructor. pthread_exit has semantics more
    > > >>or less like throwing an exception, and that's generally
    > > >>something you want to avoid in a destructor. Please explain
    > > >>what you're trying to do.


    > > > Its a TCP server. Each incoming connection gets its own thread which
    > > > is encapsulated in a connection object. I would have just used fork()
    > > > but I want the threads to access some global stats and I couldn't be
    > > > bothered with the hassle of shared memory and associated locking
    > > > issues.


    > > You will still have "locking" issues wether you use shared memory or
    > > threads (where memory is implicitly shared).


    > True, but with threads you can use mutexes. You can't do that with
    > seperate processes (and despite what some people think you can't map
    > mutexes to shared memory since if the underlying mutex code uses
    > pointers you're screwed).


    Some people, in this case, being the authors of the Posix
    standard. See pthread_mutexattr_setpshared() in the standard,
    for example. Note that this functionality is optional---as is
    threading, for that matter. From personal experience, it is
    present under Solaris and Linux, however, and I would expect it
    to be fairly widespread.

    I've used it under Solaris with no problem, with the mutex
    itself in a mmap'ed file mapped into and shared between two
    processes.

    It's also true that the Posix standard (and the rationale) speak
    of allocating memory on the heap, etc. in pthread_mutex_lock. I
    suspect that this could only be made to work if thread
    process-shared synchronization was not supported; at any rate,
    it's the system's problem to make it work, not yours.

    > For processes you have to use SysV
    > semaphores (yuck) or a locking file (yuck again).


    You have to have some memory which is common to the processes
    involved. This can be shared memory, but I've found that
    typically, mmap'ing is easier and works better.

    > > > ... When the session is over or the remote client closes the
    > > > connection the thread has to exit and the object be destroyed.


    > > Considered using a thread pool?


    > Can't be bothered :) Its not a high throughput server, the overhead of
    > creating/destroying threads to keep the code simple is worth it.


    Yes. It's the sort of thing that if you already have the
    existing code, why not, but it's not worth the hassle to develop
    it new.

    > > That's true until you try to exit. The main thread must wait until all
    > > threads have cleaned up before it exits. You will have strange
    > > behaviour if you don't.


    > If the parent thread ever exits other than via a crash it'll cause an
    > entire process halt so thats not an issue.


    There's a lot that happens between the moment the parent thread
    calls exit, and the moment the process actually stops running.
    If other threads are running during that time, or at least if
    they're doing anything non-trivial, you can sometimes get
    surprising results. It's not too difficult keep count of your
    threads, set a global flag for a clean shutdown, and wait until
    the count is 0.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 11, 2007
    #14
  15. Boltar wrote:
    > On Apr 10, 11:00 pm, Gianni Mariani <> wrote:

    ....
    >
    > True, but with threads you can use mutexes. You can't do that with
    > seperate processes (and despite what some people think you can't map
    > mutexes to shared memory since if the underlying mutex code uses
    > pointers you're screwed). For processes you have to use SysV
    > semaphores (yuck) or a locking file (yuck again).


    Wimp - implement your own mutex using relative pointers if you have to.

    >
    >
    >>>... When the session is over or the remote client closes the
    >>>connection the thread has to exit and the object be destroyed.

    >>
    >>Considered using a thread pool?

    >
    >
    > Can't be bothered :) Its not a high throughput server, the overhead of
    > creating/destroying threads to keep the code simple is worth it.


    I find that once you have a nice threadpool library, it can make life
    much simpler and you can do a few things that you really can't do if the
    thread was going to exit. i.e. A thread object can't really delete
    itself while a threadpool hook can quite happily delete itself.

    >
    >
    >>That's true until you try to exit. The main thread must wait until all
    >>threads have cleaned up before it exits. You will have strange
    >>behaviour if you don't.

    >
    >
    > If the parent thread ever exits other than via a crash it'll cause an
    > entire process halt so thats not an issue.


    That would make it not a very modular system. This would make it
    difficult to test and testability should be a primary deliverable to any
    quality code.
    Gianni Mariani, Apr 11, 2007
    #15
  16. James Kanze wrote:
    ....
    >
    > Yes. You need the procedural function anyway, to start the
    > thread. You might as well delete the object there.


    That won't work all the time. A clean shutdown requires that the main
    thread be kept alive until after all other threads have reliably
    terminated. This means (possibly other meanings too) that every thread
    object must be deleted by a thread other than the thread itself before
    exit from main. Creating a system that does otherwise will have you
    searching for the elusive bug - I've been there too often - random
    segv's on exit or shutdown of a threaded object.

    >
    > My real question concerning your initial design, I guess, was
    > who was going to call the destructor. A thread is a function,
    > an asynchronous function, but a function.


    A thread is a thread. Multiple threads may execute the same function at
    the same time.

    >
    >
    >>In reponse to another post , I don't need to do a join on an exiting
    >>thread because they're all running detached. The parent thread doesn't
    >>need to care about them , its just a fire and forget dispatcher.

    >
    >
    > And of course, you never stop the server, so there's no worry
    > about calling exit with running threads (which can cause various
    > problems as well):). If not, for a clean shutdown (especially
    > given that pthread_cancel doesn't work in C++), you need a
    > thread counter, and a flag which signals that shutdown is
    > requested, which the threads poll from time to time. The flag
    > must be protected by a mutex, and the thread counter by a
    > condition variable (since it will be decremented at the end of
    > the procedural function, just before the return). Set the flag,
    > and wait until the counter is 0, then return from main.


    Right - somthing like that ...
    Gianni Mariani, Apr 11, 2007
    #16
  17. Boltar

    bjeremy Guest


    > You don't need to call pthread_exit here---in fact, in many
    > cases, you don't want to (since calling pthread_exit will, at
    > least in some implementations, prevent the destructors of local


    Yeah.. actually I re-read the specs, it said that there is an
    implicit pthread_exit when the function that was passed into
    pthread_create, returns... so I guess you are right, it was not needed

    >
    > > void Thread::start()
    > > {
    > > pthread_create(&thread, 0, Thread::dispatch, this);

    >
    > Attention! This shouldn't compile (although g++ has a bug, and
    > doesn't detect the error). The type of function required by
    > pthread_create is `extern "C"', and a member function, even
    > static, can never be `extern "C"'.
    >

    Actually.. I haven't run it on a compiler that won't accept this..
    MSVC, g++, gcc.. all seem fine with it... but I'll double check the
    specs on this... I didn't see anything offhand that says I can not use
    a static member... but if you can point me too a specific reference
    that would be cool...

    >
    > What relationship to his question?
    >
    > He was talking about calling pthread_exit in a destructor, which
    > makes me think that he probably had detached threads in mind.
    > For detached threads, I don't use a class at all; just a
    > (template) function, which takes a functional object (or a
    > pointer to function) as parameter.
    >

    Yeah... I thought I covered that by starting off saying I wasn't 100%
    on what he was trying to do... This was supposed to be a thread class
    without having to use pthread_exit in the destructor... in order to
    avoid the issue altogether...

    Now.. if the OP is creating a TCP server, and its not a multi-core or
    dual processor machine... I wouldn't use threads at all... its a
    waste, multiplexing is much faster on a single processor... If it is
    multi-core, the previous recommendation of the thread pool would
    probably be the better design and also lets you sidestep the whole
    issue of the thread destructor.
    bjeremy, Apr 11, 2007
    #17
  18. Boltar

    Boltar Guest

    On Apr 11, 6:39 pm, "bjeremy" <> wrote:
    > Now.. if the OP is creating a TCP server, and its not a multi-core or
    > dual processor machine... I wouldn't use threads at all... its a
    > waste, multiplexing is much faster on a single processor... If it is
    > multi-core, the previous recommendation of the thread pool would
    > probably be the better design and also lets you sidestep the whole
    > issue of the thread destructor.


    I would have multiplexed , but each connection will be sending lots of
    data and I waws concerned about write blocks. Just because the
    select() mask flag says the socket is ready to write doesn't mean
    it'll accept everything you throw. With multiplexing every socket
    would have to be non blocking so a blocked socket doesn't hang every
    other session and I'd have to keep tabs of retries etc. With threads
    I can just let a socket happily block and it won't affect anything
    else.

    B2003
    Boltar, Apr 12, 2007
    #18
  19. Boltar

    James Kanze Guest

    On Apr 11, 7:39 pm, "bjeremy" <> wrote:

    > > > void Thread::start()
    > > > {
    > > > pthread_create(&thread, 0, Thread::dispatch, this);


    > > Attention! This shouldn't compile (although g++ has a bug, and
    > > doesn't detect the error). The type of function required by
    > > pthread_create is `extern "C"', and a member function, even
    > > static, can never be `extern "C"'.


    > Actually.. I haven't run it on a compiler that won't accept this..
    > MSVC, g++, gcc.. all seem fine with it...


    I've not tried with VC++, but I know that this is a bug in g++.
    Sun CC complains.

    > but I'll double check the
    > specs on this... I didn't see anything offhand that says I can not use
    > a static member... but if you can point me too a specific reference
    > that would be cool...


    Well, technically, of course, it depends on the declaration of
    pthread_create, and an implementation could overload it to take
    either (knowing that behind the scenes there is no difference),
    much in the same way the standard *requires* the library to
    overload functions from the C library, like atexit, sort and
    bsearch, which take pointers to functions. Neither Linux nor
    Solaris do this, however, so it is probably best not to count on
    it, and to just suppose that there is a single pthread_create,
    declared:
    extern "C" {
    extern int pthread_create( pthread_t*, pthread_attr_t*,
    void *(*)( void* ),
    void* ) ;
    }

    Which means that the type of the third argument is `extern "C"
    void* (*)( void* )'; only a function with "C" language linkage
    can be used. (Note the end of §7.5/1: "Two function types with
    different language linkages are distinct types even if they are
    otherwise identical.") Finally, the last normative sentence in
    §7.5/4 says "A C language linkage is ignored for the names of
    class members and the member function type of class member
    functions", so that even if you try to declare the static member
    function to have "C" linkage, it is ignored. (I don't know the
    reason for this last rule; it would make sense to allow static
    member functions to have C linkage.)

    [...]
    > Now.. if the OP is creating a TCP server, and its not a multi-core or
    > dual processor machine... I wouldn't use threads at all... its a
    > waste, multiplexing is much faster on a single processor...


    Not necessarily. And multiple threads won't necessarily be
    faster on a multi-core, here, since there is almost no CPU
    involved anyway; it's all IO.

    > If it is
    > multi-core, the previous recommendation of the thread pool would
    > probably be the better design and also lets you sidestep the whole
    > issue of the thread destructor.


    If you have the code for a thread pool handy, by all means use
    it. Otherwise, I doubt that it's worth the effort necessary to
    develop it.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 12, 2007
    #19
  20. Boltar

    James Kanze Guest

    On Apr 11, 3:58 pm, Gianni Mariani <> wrote:
    > James Kanze wrote:
    >
    > ...
    > > Yes. You need the procedural function anyway, to start the
    > > thread. You might as well delete the object there.


    > That won't work all the time. A clean shutdown requires that the main
    > thread be kept alive until after all other threads have reliably
    > terminated.


    That's a separate issue. (In some cases, there's no need for a
    clean shutdown anyway. A shutdown is, by definition, unclean.)

    > This means (possibly other meanings too) that every thread
    > object must be deleted by a thread other than the thread itself before
    > exit from main.


    I think you're confusing the "thread" object with what the OS
    considers a thread. In fact, the "thread" object, here, is just
    a functional object which is called by the function whose
    address is passed to pthread_create. The OS knows nothing about
    it, and there's not the slightest problem with deleting it in
    any thread you chose, as long as you are sure that no other
    thread will use it. (In every case where I've used this idiom,
    no other thread has even had a pointer to this object, so no
    other thread could use it.)

    > Creating a system that does otherwise will have you searching
    > for the elusive bug - I've been there too often - random
    > segv's on exit or shutdown of a threaded object.


    Exit is a special case, and needs to be handled, but it has no
    relevance here.

    > > My real question concerning your initial design, I guess, was
    > > who was going to call the destructor. A thread is a function,
    > > an asynchronous function, but a function.


    > A thread is a thread. Multiple threads may execute the same
    > function at the same time.


    I was using the term a bit loosely, but for the user, a thread
    is the equivalent of a function called asynchronously, with its
    own stack. Obviously, many threads can execute the same
    function, just as, within a single threaded environment, the
    same function can be executing many times simultaneously.

    In at least one of the models being considered for
    standardization, a thread IS an asynchronous function (which
    possibly returns a type which can be used to get its return
    value later).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 12, 2007
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sebastian Kloeppel

    Posix Thread Wrapper [HELP!!!]

    Sebastian Kloeppel, Apr 2, 2004, in forum: C++
    Replies:
    3
    Views:
    1,212
    Arash Partow
    Apr 17, 2004
  2. Replies:
    2
    Views:
    1,125
    Howard Hinnant
    May 13, 2005
  3. darklupine

    Problem creating new posix thread

    darklupine, Nov 24, 2005, in forum: C++
    Replies:
    6
    Views:
    498
  4. jrpfinch
    Replies:
    2
    Views:
    516
    Fredrik Lundh
    Mar 23, 2007
  5. Replies:
    5
    Views:
    277
Loading...

Share This Page