Barrier Synchronization in Threads

Jay Desai
4 min readJun 11, 2020

--

Think of it like being out for a hike with some friends. You agree to wait for each other at the top of each hill (and you make a mental note how many are in your group). Say you’re the first one to reach the top of the first hill. You’ll wait there at the top for your friends. One by one, they’ll arrive at the top, but nobody will continue until the last person in your group arrives. Once they do, you’ll all proceed. Barrier Synchronization works in same way.

In parallel computing, a barrier is a type of synchronization method where it enables multiple threads to wait until all threads have reached a particular point of execution(barrier) before any thread continues.
Synchronization barriers can be shared across different threads and processes.

As you can see in above image, When a thread/process(1,2 & 4) reaches a barrier, it will wait at the barrier until all the thread/process(3) reach the barrier, and then they’ll all proceed together.

To do barrier synchronization, POSIX thread library provides barrier functions which is listed below.

Syntax :
int pthread_barrier_init(pthread_barrier_t * barrier,
const pthread_barrierattr_t * attr, unsigned count);
int pthread_barrier_destroy(pthread_barrier_t * barrier);
int pthread_barrier_wait(pthread_barrier_t * barrier);
Return Value : On success _init() & _destroy() return 0 otherwise error number.(EAGAIN-system lacks the necessary resources to initialize another barrier, EINVAL-value specified by count is equal to zero, ENOMEM-insufficient memory exists to initialize the barrier)Description : The pthread_barrier_init() allocate resources required to use the barrier referenced by barrier and initialize the barrier with attribute referenced by attr. If attr is NULL, the default barrier attributes applied to barrier. The count specifies the number of threads that must call pthread_barrier_wait(). The count must be greater than zero.The pthread_barrier_destroy() destroy the barrier referenced by barrier and release any resources used by the barrier.The pthread_barrier_wait() synchronize participating threads at the barrier referenced by barrier. The calling thread block untill the required number of threads(mentioned in count while initilizing barrier) have called pthread_barrier_wait() specifying the barrier.
When the required number of threads have called pthread_barrier_wait() specifying the barrier, the constant PTHREAD_BARRIER_SERIAL_THREAD shall be returned to one unspecified thread and zero shall be returned to each remaining threads.

Let’s understand this by example. Below is the code having four threads. Each thread run the loop twice it’s thread number and wait at barrier untill all thread finish it’s loop. Once all thread reach to barrier, it execute further. Note, which thread resume first vary upon CPU scheduling.

Consider different scenario with above example.
* Pass barrier counter as 4: You’ll see, thread-1, thread-2 & thread-3 waits untill thread-4 finish it’s work and come to barrier_wait. Then all thread resume. Also return value of _barrier_wait() in thread-4 is -1 and in other thread it returns 0 b’cz call of _barrier_wait() from thread-4 is the number of barrier count we’ve passed while initializing.
* Pass barrier counter as 2: You’ll see, thread-1 & thread-2 resume together after loop execution has done. And same way thread-3 & thread-4.
* Pass barrier counter as 3 : You’ll see, thread-1, thread-2 & thread-3 resume together while thread-4 was blocked. This is because barrier in thread-4 expecting another two thread to call _barrier_wait() as we passed barrier counter 3.
* Pass barrier counter as 1: Passing 1 as barrier counter does not affect any execution as one call is enough to resume thread and it is done by own thread.
* Pass barrier counter as 0: This gives error while initializing barrier as we see in function return value.

Barrier example we’ve seen above it having default attribute as we passed NULL while initializing barrier using pthread_barrier_init(). Let’s discuss barrier attribute and it’s example.

Barrier Attributes

Barrier synchronization can be used between threads as well as processes too. To use barrier sync between different process, POSIX thread provide barrier attribute pshared which can be either PTHREAD_PROCESS_SHARED or PTHREAD_PROCESS_PRIVATE(by default). To set attribute POSIX thread library provide following function.

Syntax :
int pthread_barrierattr_init(pthread_barrierattr_t *attr);
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);
Return Value : On success _init() & _destroy() return 0 otherwise error number.(ENOMEM-insufficient memory exists to initialize the barrier)Description : The pthread_barrierattr_init() initialize barrier attribute referenced by attr. The same attribute destroyed by pthread_barrierattr_destroy() once attribute assign to any barrier.Syntax :
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr,
int pshared);
int pthread_barrierattr_getpshared(const pthread_barrierattr_t
*restrict attr, int *restrict pshared);
Return Value : On success _setpshared() & _getpshared() return 0 otherwise error number.(EINVAL-Value specified by pshared is not valid)Description : The pthread_barrierattr_getpshared() function get the value of the pshared(process-shared) attribute from the attributes object referenced by attr.The pthread_barrierattr_setpshared() function shall set the pshared attribute in an initialized attributes object referenced by attr. pshared can be any one out of following.

PTHREAD_PROCESS_PRIVATE

Permit a barrier to be operated upon by any thread that has access to the memory where the barrier is allocated

As you can observe the output, for parent and child process barrier is private. Child-Thread-1 has finished it’s work and wait for barrier call from another thread. At same time Parent-Thread-1 has finished it’s job and call barrier. But due to the different processes, both thread waiting till it’s own thread call barrier. Once Child-Thread-2 and Parent-Thread-2 finished its work, Both process has finished.

PTHREAD_PROCESS_SHARED

Barrier only be operated upon by any thread created within the same process as the thread that initialized the barrier

From output of above program, you can see, parent process finish its work and waiting for another process/thread to call barrier. After few sec, child finish its work and call barrier and both parent and child resume.

--

--

Jay Desai

"Jay Desai is a skilled Real-Time Embedded Engineer with a passion for innovation and technology. Currently employed at Airspan Network.