Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Immediately destroying pthread barriers

193 views
Skip to first unread message

Christoph Bartoschek

unread,
Feb 3, 2009, 11:56:00 AM2/3/09
to
Hi,

is it allowed to immediately destroy pthread barriers after it has been
used?

Here is a small example.

The first thread does the following:

pthread_barrier_t * barrier = malloc(sizeof(*barrier));
pthread_barrier_init(barrier, NULL, 2);
// Start second thread
pthread_barrier_wait(barrier);
pthread_barrier_destroy(barrier);
free(barrier);

The second thread does the following:

// Do some work
pthread_barrier_wait(barrier);


The problem I see is that both threads can reach their pthread_barrier_wait
calls and the first thread is woken up first. Then it destroys the barrier.
I can imagine that the second thread has to check some things when it is
woken up that still require a valid barrier object. Is this the case?

For example I have seen a broken home-made C++ barrier implementation that
used condition variables and showed the problem:

void Barrier::wait()
{
ScopedLock lock(_mutex);
if (_num_threads == 0) {
return;
}

--_num_threads;

if (_num_threads == 0) {
_all_reached_cv.signal_all();
return;
}

while (_num_threads > 0) {
_all_reached.wait(_mutex);
}
}

Greetings
Christoph

David Schwartz

unread,
Feb 4, 2009, 5:40:29 PM2/4/09
to
On Feb 3, 8:56 am, Christoph Bartoschek <bartosc...@gmx.de> wrote:

> is it allowed to immediately destroy pthread barriers after it has been
> used?

You cannot destroy *any* shared resource if another thread is or might
be using it.

> Here is a small example.
>
> The first thread does the following:
>
> pthread_barrier_t * barrier = malloc(sizeof(*barrier));
> pthread_barrier_init(barrier, NULL, 2);
> // Start second thread
> pthread_barrier_wait(barrier);
> pthread_barrier_destroy(barrier);
> free(barrier);

The pthread_barrier_destroy call is legal if and only if you know for
sure that no other thread is, or might be, using the barrier.

> The second thread does the following:
>
> // Do some work
> pthread_barrier_wait(barrier);
>
> The problem I see is that both threads can reach their pthread_barrier_wait
> calls and the first thread is woken up first. Then it destroys the barrier.
> I can imagine that the second thread has to check some things when it is
> woken up that still require a valid barrier object. Is this the case?

Even before it wakes up, if it's blocked on a barrier, that barrier
must exist.

> For example I have seen a broken home-made C++ barrier implementation that
> used condition variables and showed the problem:
>
> void Barrier::wait()
> {
>    ScopedLock lock(_mutex);
>    if (_num_threads == 0) {
>       return;
>    }
>
>    --_num_threads;
>
>    if (_num_threads == 0) {
>       _all_reached_cv.signal_all();
>       return;
>    }
>
>    while (_num_threads > 0) {
>       _all_reached.wait(_mutex);
>    }
>
> }

What is broken is destroying a synchronization object in one thread
while another thread might be blocked on it!

DS

Alexander Terekhov

unread,
Feb 4, 2009, 7:08:59 PM2/4/09
to

David Schwartz wrote:
>
> On Feb 3, 8:56 am, Christoph Bartoschek <bartosc...@gmx.de> wrote:
>
> > is it allowed to immediately destroy pthread barriers after it has been
> > used?
>
> You cannot destroy *any* shared resource if another thread is or might
> be using it.

He is talking about the same level of safety for barriers with respect
to destruction and exiting waiters (unblocked threads) as for mutexes
and condvars.

See

http://www.opengroup.org/austin/mailarchives/ag-review/msg01705.html
http://www.opengroup.org/austin/mailarchives/ag-review/msg01706.html

Curiously enough, both DRs were rejected...

regards,
alexander.

Christoph Bartoschek

unread,
Feb 4, 2009, 7:12:03 PM2/4/09
to
David Schwartz wrote:

> On Feb 3, 8:56 am, Christoph Bartoschek <bartosc...@gmx.de> wrote:
>
>> is it allowed to immediately destroy pthread barriers after it has been
>> used?
>
> You cannot destroy *any* shared resource if another thread is or might
> be using it.

This is clear to me.

>> Here is a small example.
>>
>> The first thread does the following:
>>
>> pthread_barrier_t * barrier = malloc(sizeof(*barrier));
>> pthread_barrier_init(barrier, NULL, 2);
>> // Start second thread
>> pthread_barrier_wait(barrier);
>> pthread_barrier_destroy(barrier);
>> free(barrier);
>
> The pthread_barrier_destroy call is legal if and only if you know for
> sure that no other thread is, or might be, using the barrier.
>
>> The second thread does the following:
>>
>> // Do some work
>> pthread_barrier_wait(barrier);

The barrier is only used by the two example threads and only for the calls
shown. The second thread calls pthread_barrier_wait only once and never
touches barrier again.

My question is, whether destroying the barrier in such an example is legal?
Can I be sure that the barrier is no longer used in this case?

If the call to pthread_barrier_destroy is illegal in this case, then
barriers are of limited use for me, because then I need an additional
synchronisation primitive to make sure that the barrier is no longer used.

Here is a complete example. Is the program correct?

#include <pthread.h>
#include <stdlib.h>

pthread_barrier_t * barrier;

void * thread(void * arg) {
pthread_barrier_wait(barrier);
return NULL;
}

int main() {
pthread_t tid;

barrier = malloc(sizeof(*barrier));
pthread_barrier_init(barrier, NULL, 2);

pthread_create(&tid, NULL, thread, NULL);

pthread_barrier_wait(barrier);
pthread_barrier_destroy(barrier);
free(barrier);

pthread_join(tid, NULL);
return 0;
}

Christoph

David Schwartz

unread,
Feb 4, 2009, 8:02:34 PM2/4/09
to
On Feb 4, 4:12 pm, Christoph Bartoschek <bartosc...@gmx.de> wrote:

> Here is a complete example. Is the program correct?
>
> #include <pthread.h>
> #include <stdlib.h>
>
> pthread_barrier_t * barrier;
>
> void * thread(void * arg) {
>    pthread_barrier_wait(barrier);
>    return NULL;
>
> }
>
> int main() {
>    pthread_t tid;
>
>    barrier = malloc(sizeof(*barrier));
>    pthread_barrier_init(barrier, NULL, 2);
>
>    pthread_create(&tid, NULL, thread, NULL);
>
>    pthread_barrier_wait(barrier);
>    pthread_barrier_destroy(barrier);
>    free(barrier);
>
>    pthread_join(tid, NULL);
>    return 0;
>
> }

Not only is it not correct, it's still incorrect even with the
proposed strengthening of the thread-safety of barriers. Even the
strengthening proposal requires that only the thread that leaves last
(the one that gets PTHREAD_BARRIER_SERIAL_THREAD) can destroy the
barrier.

If you replaced each pthread_barrier_wait call with:
if(pthread_barrier_wait(barrier)==PTHREAD_BARRIER_SERIAL_THREAD)
pthread_barrier_destroy(barrier);
You would be safe if the requirements were strengthened, as only the
'last' thread would be destroying the barrier.

But your code is unsafe under any proposal.

DS

Christoph Bartoschek

unread,
Feb 5, 2009, 10:21:53 AM2/5/09
to
David Schwartz wrote:

> Not only is it not correct, it's still incorrect even with the
> proposed strengthening of the thread-safety of barriers. Even the
> strengthening proposal requires that only the thread that leaves last
> (the one that gets PTHREAD_BARRIER_SERIAL_THREAD) can destroy the
> barrier.
>
> If you replaced each pthread_barrier_wait call with:
> if(pthread_barrier_wait(barrier)==PTHREAD_BARRIER_SERIAL_THREAD)
> pthread_barrier_destroy(barrier);
> You would be safe if the requirements were strengthened, as only the
> 'last' thread would be destroying the barrier.
>
> But your code is unsafe under any proposal.

Ok. I see that my code is not correct.

Now I wonder why the definition of a barrier cannot be strengthened such
that the code would be valid? Is it impossible to implement a barrier that
can be destroyed as soon as all threads are released?

What about the other synchronisation primitives? I would think that the
following is correct:

#include <pthread.h>
#include <stdlib.h>

pthread_mutex_t * mutex;

void * thread(void * arg) {

pthread_mutex_lock(mutex);
pthread_mutex_unlock(mutex);
pthread_mutex_destroy(mutex);
free(mutex);
return NULL;
}

int main() {
pthread_t tid;

mutex = malloc(sizeof(*mutex));
pthread_mutex_init(mutex, NULL);
pthread_mutex_lock(mutex);
pthread_create(&tid, NULL, thread, NULL);

pthread_mutex_unlock(mutex);

pthread_join(tid, NULL);
return 0;
}

Here the mutex is destroyed, although the main thread can still be inside of
the pthread_mutex_unlock function just after performing the unlock.

Greetings
Christoph

Alexander Terekhov

unread,
Feb 5, 2009, 11:34:58 AM2/5/09
to

Christoph Bartoschek wrote:
>
> David Schwartz wrote:
>
> > Not only is it not correct, it's still incorrect even with the
> > proposed strengthening of the thread-safety of barriers. Even the
> > strengthening proposal requires that only the thread that leaves last
> > (the one that gets PTHREAD_BARRIER_SERIAL_THREAD) can destroy the
> > barrier.

Uhmm,

http://www.opengroup.org/austin/mailarchives/ag-review/msg01705.html

doesn't require that.

> >
> > If you replaced each pthread_barrier_wait call with:
> > if(pthread_barrier_wait(barrier)==PTHREAD_BARRIER_SERIAL_THREAD)
> > pthread_barrier_destroy(barrier);
> > You would be safe if the requirements were strengthened, as only the
> > 'last' thread would be destroying the barrier.
> >
> > But your code is unsafe under any proposal.
>
> Ok. I see that my code is not correct.

Actually, under

http://www.opengroup.org/austin/mailarchives/ag-review/msg01705.html

clarification, your code is perfectly correct since there is no
difference whether pthread_barrier_wait() in your initial/main thread
returns zero or PTHREAD_BARRIER_SERIAL_THREAD -- in both cases no thread
can be considered "blocked" on the barrier. Note that

http://www.opengroup.org/onlinepubs/000095399/functions/pthread_barrier_destroy.html

says "results are undefined if pthread_barrier_destroy() is called when
any thread is blocked on the barrier, or if this function is called with
an uninitialized barrier".

It follows that pthread_barrier_destroy() is safe to call when no thread
is BLOCKED on the barrier (call pthread_barrier_destroy() after
pthread_barrier_wait() in your initial/main thread returns either zero
or PTHREAD_BARRIER_SERIAL_THREAD).

>
> Now I wonder why the definition of a barrier cannot be strengthened such
> that the code would be valid?

To not make incorrect implementations officially broken? ;-)

> Is it impossible to implement a barrier that
> can be destroyed as soon as all threads are released?

Of course it is possible but apparently not all implementations do that.
Check Linux/NPTL, for example.

regards,
alexander.

David Schwartz

unread,
Feb 5, 2009, 1:51:30 PM2/5/09
to
On Feb 5, 8:34 am, Alexander Terekhov <terek...@web.de> wrote:

> It follows that pthread_barrier_destroy() is safe to call when no thread
> is BLOCKED on the barrier (call pthread_barrier_destroy() after
> pthread_barrier_wait() in your initial/main thread returns either zero
> or PTHREAD_BARRIER_SERIAL_THREAD).

The other thread may still be blocked on the barrier. Nothing
guarantees that the unblocking of all threads blocked on a barrier is
an atomic operation.

DS

Alexander Terekhov

unread,
Feb 6, 2009, 12:56:34 PM2/6/09
to

By the same line of reasoning, nothing guarantees that "reset to the
state it had as a result of the most recent pthread_barrier_init()
function that referenced it" is an atomic operation.

http://www.opengroup.org/onlinepubs/000095399/functions/pthread_barrier_wait.html

Why don't you file a DR, DS. ;-)

regards,
alexander.

David Schwartz

unread,
Feb 6, 2009, 4:36:06 PM2/6/09
to
On Feb 6, 9:56 am, Alexander Terekhov <terek...@web.de> wrote:

> By the same line of reasoning, nothing guarantees that "reset to the
> state it had as a result of the most recent pthread_barrier_init()
> function that referenced it" is an atomic operation.

I guess you're technically right. But I think it's reasonable to
argue that when a section of a standard is clearly intended to give
you a particular guarantee, and real-world implementations do actually
honor that guarantee, then the standard provides that guarantee.

But I guess I'm starting to feel like a constitutional lawyer here.
When the precise text of the standard supports my argument, I argue
from the text. When the text doesn't, I argue the intent of the
standard. When neither does, I argue the most.

DS

Chris M. Thomasson

unread,
Feb 6, 2009, 11:35:15 PM2/6/09
to
"Christoph Bartoschek" <barto...@gmx.de> wrote in message
news:1i9m56-...@burns.bruehl.pontohonk.de...

> Hi,
>
> is it allowed to immediately destroy pthread barriers after it has been
> used?
>
> Here is a small example.
>
> The first thread does the following:
> [...]

You can check here for some "further" context:

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/38d68d1289352a53

If you take the docs literally, well, it seems as if an implementation would
need to implement/use a robust lifetime management strategy for their
synchronization objects. However, if programmer has common sense, then this
strict conformance is not "needed" because the, dare I say GC of objects, is
provided at a higher level by an library/application programmer.

0 new messages