Boost logo

Boost :

From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2001-06-30 11:12:00


> Not 100% true. It's undefined behavior to allow a program to
> terminate with a thread that's never been joined or detached.
> Theoretically such usage could lead to system leaks that can't be
> reclaimed (though in practice I'd be surprised to find an
> implementation that behaves that way).

it is defined and even explained:

10108 Threads terminated by a call to _Exit() or _exit( ) shall not invoke
their cancelation cleanup
10109 handlers or per-thread data destructors.

(exit() performs a superset of _exit() actions such as atexit handlers...)

7346 It was suggested that the cancelation cleanup handlers should also be
called when the
7347 process exits or calls the exec function. This was rejected partly due
to the performance
7348 problem caused by having to call the cancelation cleanup handlers of
every thread before the
7349 operation could continue. The other reason was that the only state
expected to be cleaned up
7350 by the cancelation cleanup handlers would be the intraprocess state.
Any handlers that are to
7351 clean up the interprocess state would be registered with atexit ( ).

> With a CV solution the thread is created detached and
> later the CV is waited on. That or the thread is immediately
> detached after waiting on the CV. Both result in the same behavior
> as join(). A join() concept is a convenience, and nothing more.

the behaviour is NOT the same. CV solution for detached thread
merely gives you an indication that thread is going to terminate
_sometime_ soon with NO indication of termination _completed_.
For stack reclamation you need exactly that - termination completed
indication and only join provides it! with CV solution you risk to
reclaim the terminating thread stack while the terminating thread
is still execution code inside its mutex_unlock (or cond_signal)
even _before_ actual termination. that is incorrect.

> Ahh... notice you're design uses a CV to implement the join. You've
> just proven that join() is a convenience ;).

well, the example i've posted is not written by me.. and you are partly
right -- it is not join()-conformant since it does not provide "join"
guaranties with respect to termination. however, it could be quite
easily fixed -- see below, changes marked with "AT".

> Other than proving my point, I don't see what else this example is
> supposed to prove.

it was supposed to prove nothing. the intent was to illustrate
join/detach concepts and show one possible way how C++
thread object could be implemented in posix. add virual
destructor, post-constructor (which would call pthread_create),
pre-destructor (which would join not detached thread [if not
already joined]; detached thread should destroy its thread
object via TSD destructor), container for second level thread
locals, support for thread cancellation which would throw
some "interrupted/cancelled" exception, etc.. and you would be
99% done ;) well, with at least some most basic features.

regards,
alexander.

6086 /*
6087 * Construct a thread variety entirely from existing functions
6088 * with which a join can be done, allowing the join to time out.
6089 */
6090 #include <pthread.h>
6091 #include <time.h>

6092 struct timed_thread {
6093 pthread_t t;
6094 pthread_mutex_t m;
6095 int exiting;
6096 pthread_cond_t exit_c;
6097 void *(*start_routine)(void *arg);
6098 void *arg;
6099 void *status;
6100 };
6101 typedef struct timed_thread *timed_thread_t;

6102 static pthread_key_t timed_thread_key;
6103 static pthread_once_t timed_thread_once = PTHREAD_ONCE_INIT;

6104 static void timed_thread_init()
6105 {
6106 pthread_key_create(&timed_thread_key, NULL);
6107 }

6108 static void *timed_thread_start_routine(void *args)
6109 /*
6110 * Routine to establish thread-specific data value and run the actual
6111 * thread start routine which was supplied to timed_thread_create().
6112 */
6113 {
6114 timed_thread_t tt = (timed_thread_t) args;
6115 pthread_once(&timed_thread_once, timed_thread_init);
6116 pthread_setspecific(timed_thread_key, (void *)tt);
6117 timed_thread_exit((tt->start_routine)(tt->arg));
6118 }

6119 int timed_thread_create(timed_thread_t ttp, const pthread_attr_t
*attr,
6120 void *(*start_routine)(void *), void *arg)
6121 /*
6122 * Allocate a thread which can be used with timed_thread_join().
6123 */
6124 {
6125 timed_thread_t tt;
6126 int result;
6127 tt = (timed_thread_t) malloc(sizeof(struct timed_thread));
6128 pthread_mutex_init(&tt->m,NULL);
6129 tt->exiting = FALSE;
6130 pthread_cond_init(&tt->exit_c,NULL);
6131 tt->start_routine = start_routine;
6132 tt->arg = arg;
6133 tt->status = NULL;
6134 if ((result = pthread_create(&tt->t, attr,
6135 timed_thread_start_routine, (void *)tt)) != 0) {
6136 free(tt);
6137 return result;
6138 }
6139 //AT pthread_detach(tt->t);
6140 ttp = tt;
6141 return 0;
6142 }

6143 int timed_thread_join(timed_thread_t tt,
6144 struct timespec *timeout,
6145 void **status)
6146 {
6147 int result;
6148 pthread_mutex_lock(&tt->m);
6149 result = 0;
6150 /*
6151 * Wait until the thread announces that it is exiting,
6152 * or until timeout.
6153 */
6154 while (result == 0 && ! tt->exiting) {
6155 result = pthread_cond_timedwait(&tt->exit_c, &tt->m, timeout);
6156 }
6157 pthread_mutex_unlock(&tt->m);
6158 if (result == 0 && tt->exiting) {
6159 *status = tt->status;
     /*AT*/ result = pthread_join(tt->t, NULL);
6160 free((void *)tt);
6161 //AT return result;
6162 }
6163 return result;
6164 }

6165 void timed_thread_exit(void *status)
6166 {
6167 timed_thread_t tt;
6168 void *specific;
6169 if ((specific=pthread_getspecific(timed_thread_key)) == NULL){
6170 /*
6171 * Handle cases which won't happen with correct usage.
6172 */
6173 pthread_exit( NULL);
6174 }
6175 tt = (timed_thread_t) specific;
6176 pthread_mutex_lock(&tt->m);
6177 /*
6178 * Tell a joiner that we're exiting.
6179 */
6180 tt->status = status;
6181 tt->exiting = TRUE;
6182 pthread_cond_signal(&tt->exit_c);
6183 pthread_mutex_unlock(&tt->m);
6184 /*
6185 * Call pthread exit() to call destructors and really
6186 * exit the thread.
6187 */
6188 pthread_exit(NULL);
6189 }


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk