Re: Posix thread exiting and destructors
Boltar wrote:
On Apr 10, 9:38 am, "Erik Wikstr?m" <eri...@student.chalmers.se>
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=en&q=+at_posix_thread_cpp.i+show:QsaACJno998:8XyWPxo9vo4:GjBrbFv6xF4&sa=N&cd=1&ct=rc&cs_p=http://www.sourcefiles.org/Programming/Libraries/Utilities/austria-0.8.tgz&cs_f=austria-0.8/src/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 908764&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