Posix thread exiting and destructors

B

Boltar

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
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

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.
 
B

Boltar

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
 
G

Gianni Mariani

Boltar said:
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 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
 
J

James Kanze

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.
 
B

bjeremy

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.
 
B

Boltar

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
 
G

Gianni Mariani

Boltar said:
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.
 
B

Boltar

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).
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
 
I

Ian Collins

Boltar said:
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>
 
B

Boltar

<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
 
J

James Kanze

[...]
#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
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.
 
J

James Kanze

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.
 
J

James Kanze

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.
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.
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.
 
G

Gianni Mariani

Boltar said:
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.
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.
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.
 
G

Gianni Mariani

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.
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 ...
 
B

bjeremy

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
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.
 
B

Boltar

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
 
J

James Kanze

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.
 
J

James Kanze

James Kanze wrote:

...
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.
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).
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top