sharing mutex, rwlock, barrier and condition variable among processes

POSIX defined a special attribute called "PTHREAD_PROCESS_SHARED" to allow spinlock ( treat it as a highly simplified mutex without priority inheritance or ownership), mutex, rwlock, barrier and condition variables to be shared among processes. The following code give an example of such.

Note that using mmap with an ordinary file between processes has many implications on boundary conditions. Product quality code must pay special attention to initialization during process startup, mutex ownership revival during process restart, security, etc.

Some implementation of POSIX requires that the file needs to be a shared memory file created via shm_open(), some implementation may require the sharing processes having parent/child relationship. Those are all non-conforming implementation. POSIX has no such requirements.

Also note that PTHREAD_PROCESS_SHARED is an extension, it is perfectly OK for some POSIX implementation to not implement this extension. You can test the implementation via: #ifdef _POSIX_THREAD_PROCESS_SHARED.

// file name "demo.h"

#include <pthread.h>

typedef struct _demo {

pthread_mutex_t mutex;

pthread_cond_t cond;

pthread_rwlock_t rwlock;

pthread_barrier_t barrier;

unsigned int count;

} demo_t;

demo_t *d_create(char *);

demo_t *d_open(char *);

// file name "demo.c"

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <pthread.h>

#include <fcntl.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include "demo.h"

demo_t *d_create(char *d_name)

{

int fd;

pthread_mutexattr_t mutex_attr;

pthread_condattr_t cond_attr;

pthread_rwlockattr_t rwlock_attr;

pthread_barrierattr_t barrier_attr;

demo_t *d;

fd = open(d_name, O_RDWR | O_CREAT, 0660);

if (fd < 0) {

printf("failed to open %s, error: %s\n", d_name, strerror(errno));

return NULL;

}

(void) ftruncate(fd, sizeof(demo_t));

(void) pthread_mutexattr_init(&mutex_attr);

(void) pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);

(void) pthread_condattr_init(&cond_attr);

(void) pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);

(void) pthread_rwlockattr_init(&rwlock_attr);

(void) pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED);

(void) pthread_barrierattr_init(&barrier_attr);

(void) pthread_barrierattr_setpshared(&barrier_attr, PTHREAD_PROCESS_SHARED);

d = (demo_t *)mmap(NULL, sizeof(demo_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

close(fd);

(void) pthread_mutex_init(&d->mutex, &mutex_attr);

(void) pthread_cond_init(&d->cond, &cond_attr);

(void) pthread_rwlock_init(&d->rwlock, &rwlock_attr);

(void) pthread_barrier_init(&d->barrier, &barrier_attr, 2);

d->count = 0;

return d;

}

demo_t *d_open( char *s_name)

{

int fd;

demo_t *d;

fd = open(s_name, O_RDWR, 0660);

if (fd < 0) printf("failed to open %s, error: %s\n", s_name, strerror(errno));

d = (demo_t *) mmap(NULL, sizeof(demo_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

close(fd);

return d;

}

// file name "create.c"

#include <pthread.h>

#include "demo.h"

void main(void)

{

(void) d_create("/tmp/my_sem");

}

// file name "create.c"

#include <pthread.h>

#include "demo.h"

void main(void)

{

(void) d_create("/tmp/my_sem");

}

// file name "producer.c"

#include <pthread.h>

#include <stdio.h>

#include "demo.h"

void main(void)

{

demo_t *d = d_open("/tmp/my_sem");

if ( d == NULL ) {

return;

}

while (1) {

pthread_mutex_lock(&d->mutex);

if ( d->count == 0 ) {

d->count++;

pthread_cond_signal(&d->cond);

printf("producer is to signal, time is %ld, count is %d\n", time(NULL), d->count);

fflush(stdout);

}

pthread_mutex_unlock(&d->mutex);

printf("producer call barrier wait, time is %ld\n", time(NULL));

pthread_barrier_wait(&d->barrier);

printf("producer out barrier wait, time is %ld\n", time(NULL));

printf("producer call writer lock, time is %ld\n", time(NULL));

pthread_rwlock_wrlock(&d->rwlock);

sleep(1);

printf("producer out writer lock, time is %ld\n", time(NULL));

pthread_rwlock_unlock(&d->rwlock);

sleep(3);

printf("\n\n\n");

}

}

// file name "consumer.c"

#include <pthread.h>

#include <stdio.h>

#include "demo.h"

void main(void)

{

demo_t *d = d_open("/tmp/my_sem");

if ( !d ) return;

while (1) {

pthread_mutex_lock(&d->mutex);

while (d->count == 0) {

pthread_cond_wait(&d->cond, &d->mutex);

}

d->count--;

pthread_mutex_unlock(&d->mutex);

printf("consumer out of cond wait, time is %ld, count is %d\n", time(NULL), d->count);

sleep(1);

printf("consumer call barrier wait, time is %ld\n", time(NULL));

pthread_barrier_wait(&d->barrier);

printf("consumer out barrier wait, time is %ld\n", time(NULL));

printf("consumer call reader lock, time is %ld\n", time(NULL));

pthread_rwlock_rdlock(&d->rwlock);

sleep(1);

printf("consumer out reader lock, time is %ld\n", time(NULL));

pthread_rwlock_unlock(&d->rwlock);

}

}

following are results:

gcc -o producer producer.c demo.c -pthread; gcc -o consumer consumer.c demo.c -pthread

gcc create.c demo.c -pthread

./a.out

./producer&

./consumer&

producer is to signal, time is 1409702945, count is 1

producer call barrier wait, time is 1409702945

consumer out of cond wait, time is 1409702945, count is 0

consumer call barrier wait, time is 1409702946

consumer out barrier wait, time is 1409702946

consumer call reader lock, time is 1409702946

producer out barrier wait, time is 1409702946

producer call writer lock, time is 1409702946

consumer out reader lock, time is 1409702947

producer out writer lock, time is 1409702948

producer is to signal, time is 1409702951, count is 1

producer call barrier wait, time is 1409702951

consumer out of cond wait, time is 1409702951, count is 0

consumer call barrier wait, time is 1409702952

consumer out barrier wait, time is 1409702952

consumer call reader lock, time is 1409702952

producer out barrier wait, time is 1409702952

producer call writer lock, time is 1409702952

consumer out reader lock, time is 1409702953

producer out writer lock, time is 1409702954