Implementing barrier in Pthreads

According from my previous post, the most useful thing in parallel programming is barrier. Usually, barriers are available in Pthreads at least below 3 functions.

int pthread_barrier_destroy(pthread_barrier_t *barrier);
int pthread_barrier_init(pthread_barrier_t *barrier,
      const pthread_barrierattr_t *attr, unsigned count);

int pthread_barrier_wait(pthread_barrier_t *barrier);

Unfortunately, above functions may be not available on some architecture, e.g., Cygwin. That's why I need to implement one.

The first part is to define some macros for compatibility with Pthreads.

#if defined(WITH_BARRIER) || defined(CYGWIN)
#define pthread_barrier_t barrier_t
#define pthread_barrier_attr_t barrier_attr_t
#define pthread_barrier_init(b,a,n) barrier_init(b,n)
#define pthread_barrier_destroy(b) barrier_destroy(b)
#define pthread_barrier_wait(b) barrier_wait(b)
#endif

Then follows by declarations of a structure named barrier_t. We need to have at least a mutex, a cond and 2 integers to store the needed number and the number of called.

typedef struct {
    int needed;
    int called;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} barrier_t;

I follows the style of Pthreads (init, destroy and wait) to make it very easy to adopt.

int barrier_init(barrier_t *barrier,int needed);
int barrier_destroy(barrier_t *barrier);
int barrier_wait(barrier_t *barrier);

In barrier_init(), we don't have any special attributes so there are only 2 arguments here. All members of barrier_t are initialized to make it usable.

int barrier_init(barrier_t *barrier,int needed)
{
    barrier->needed = needed;
    barrier->called = 0;
    pthread_mutex_init(&barrier->mutex,NULL);
    pthread_cond_init(&barrier->cond,NULL);
    return 0;
}

Don't forget to run barrier_destroy() to propagate to mutex and cond.

int barrier_destroy(barrier_t *barrier)
{
    pthread_mutex_destroy(&barrier->mutex);
    pthread_cond_destroy(&barrier->cond);
    return 0;
}

Now we reach barrier_wait(). The code is very simple. Whenever current thread obtains the lock, called will be increased. If called is not equal to needed, this thread will wait. Otherwise, called is equal to needed. That means we have all threads reaching the barrier so just simply send broadcast to notify other waiting threads. That's all.

int barrier_wait(barrier_t *barrier)
{
    pthread_mutex_lock(&barrier->mutex);
    barrier->called++;
    if (barrier->called == barrier->needed) {
        barrier->called = 0;
        pthread_cond_broadcast(&barrier->cond);
    } else {
        pthread_cond_wait(&barrier->cond,&barrier->mutex);
    }
    pthread_mutex_unlock(&barrier->mutex);
    return 0;
}

Tags: , , ,

Post new comment