Best way to share data between threads

Giorgos Keramidas keramida at ceid.upatras.gr
Wed Feb 23 06:15:40 GMT 2005


On 2005-02-23 03:58, Jonathon McKitrick <jcm at FreeBSD-uk.eu.org> wrote:
>On Tue, Feb 22, 2005 at 02:42:44PM +0200, Giorgos Keramidas wrote:
>: 	1) Explicit notification using a condition variable.
>:
>: The first can be accomplished by associating a pthread_cond_t with the
>
> I think this is the approach I will use.  As a matter of fact, I added it
> today for a different, more limited use, and it worked well.  We have an
> instrument that needs firmware ftp'ed to it after a cold start, and I placed
> that into a pthread.  I'll try the data acq thread soon.
>
> The only problem is that pthread_join doesn't seem to have a timeout
> feature.  So if the thread hangs, I have no way of knowing.

True.  I usually get around this by polling on a thread flag until its
state changes as expected or a timeout occurs.

	struct thread_data {
		pthread_mutex_t	td_lock;
		pthread_t	td_id;
		int		td_flags;
	};

	#define	TDF_SPAWNING	(1<<0)		/* Thread is now spawning   */
	#define	TDF_ACTIVE	(1<<1)		/* If set, thread is active */
	#define	TDF_CLOSE	(1<<2)		/* If set, thread closes    */
	#define	TDF_CLOSING	(1<<3)		/* Thread in close process  */

The main thread that needs to know if a thread is active, keeps a list
of thread_data structures around and passes pointers to these structures
to pthread_create() as the thread 'arg'.

Before calling pthread_create(), the main thread initializes a new
thread_data structure and sets its TDF_SPAWNING flag.  This way, even if
the spawned thread takes a long time to start, the thread_data won't be
ripped off from under its feet while it is still spawning.

Then pthread_create() starts the new thread:

	struct thread_data *td;

	if (pthread_create(&(td->td_id), NULL, thread_main, td) != 0)
		die();

The pthread_main() function takes care of changing the state of the
thread_data from TDF_SPAWNING to TDF_ACTIVE:

	void *
	thread_main(void *arg)
	{
		struct thread_data *td = (struct thread_data *)arg;

		if (td == NULL)
			return (NULL);
		if (pthread_mutex_lock(&(td->td_lock)) != 0)
			return (NULL);

		td->td_flags |= TDF_ACTIVE;
		td->td_flags &= ~(TDF_SPAWNING);

		if (pthread_mutex_unlock(&(td->td_lock)) != 0)
			panic();

Then the thread_main() function can go on doing other things, waiting
for its TDF_CLOSE flag to be set.  That is an indication that the thread
should start cleaning up and stop running.  As soon as the thread
notices TDF_CLOSE is set, it locks thread_data, sets TDF_CLOSING and
clears TDF_CLOSE.  Then thread_data can be unlocked again, the thread
can clean up, eventually ending up at the final part:

	if (pthread_mutex_lock(&(td->td_lock)) != 0)
		error();
	td->td_flags &= ~(TDF_ACTIVE | TDF_CLOSING);
	if (pthread_mutex_unlock(&(td->td_lock)) != 0)
		error();
	return (arg);

Done.  The fact that TDF_ACTIVE is no longer set, means that the thread
has already exited, so its safe to call pthread_join() on its td->td_id
and it won't block indefinitely.

- Giorgos



More information about the freebsd-questions mailing list