Comparison of PTHREAD_PROCESS_SHARED in Soaris and FreeBSD
Let us begin with the sample code below (headers omitted for brevity):
The above code makes use of mutexes to synchronize between two processes. It first acquires an anonymous memory mapped region, which is marked for sharing between processes. Then it initializes a mutex in that region with the attribute PHREAD_PROCESS_SHARED. Before forking, the mutex is locked by the parent. After fork, the child process too attempts to lock the mutex.
The expected result is that the child should block until the parent unlocks the mutex.
I tried this piece of code in two Unix flavours: Solaris 9 and FreeBSD 6.2. Only the Solaris implementation seemed to give the expected result.
In FreeBSD implementation, the types pthread_mutex_t, pthread_cond_t and pthread_rwlock_t are all typedefed as pointers. Hence whenever a variable of one of these types is declared, only a pointer is declared. When the respective *_init function is called, the actual memory is allocated. The thread/process calling init functions does not have any control over from where the memory is acquired (a memory mapped region, shared memory or heap). Also, it might lead to memory leaks (but how many times in a program a thread, attribute, mutex, etc are initialized :-).
Contrarily, Soaris has these types as structures. Hence it is easy to share a mutex between two processes by having the mutex in a mmaped region or shared memory.
To compile the above program in Solaris, give "CC -mt". In FreeBSD, give "CC -pthread -lpthread".
int main(int argc, const char* argv[]) {
void* mmap_ptr = mmap (NULL, sizeof (pthread_mutex_t),
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
if (mmap_ptr == MAP_FAILED) {
perror ("mmap failed");
return -1;
}
fprintf (stderr, "mmaped at: %x\n", mmap_ptr);
pthread_mutex_t* mutp = (pthread_mutex_t*)mmap_ptr;
// initialize the attribute
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED); // this is what we're testing
// initialize the mutex
pthread_mutex_init (mutp, &attr);
pthread_mutexattr_destroy (&attr);
// acquire the lock before fork
pthread_mutex_lock (mutp);
pid_t chld = fork ();
if (chld != 0) { // parent
fprintf (stderr, "parent: going to sleep...\n");
sleep (30);
fprintf (stderr, "parent: unlocking.\n");
pthread_mutex_unlock (mutp);
} else { // child
fprintf (stderr, "child: going to acquire the lock ...\n");
pthread_mutex_lock (mutp);
fprintf (stderr, "child: acquired the lock.\n");
sleep (30);
pthread_mutex_unlock (mutp);
}
return 0;
}
The above code makes use of mutexes to synchronize between two processes. It first acquires an anonymous memory mapped region, which is marked for sharing between processes. Then it initializes a mutex in that region with the attribute PHREAD_PROCESS_SHARED. Before forking, the mutex is locked by the parent. After fork, the child process too attempts to lock the mutex.
The expected result is that the child should block until the parent unlocks the mutex.
I tried this piece of code in two Unix flavours: Solaris 9 and FreeBSD 6.2. Only the Solaris implementation seemed to give the expected result.
In FreeBSD implementation, the types pthread_mutex_t, pthread_cond_t and pthread_rwlock_t are all typedefed as pointers. Hence whenever a variable of one of these types is declared, only a pointer is declared. When the respective *_init function is called, the actual memory is allocated. The thread/process calling init functions does not have any control over from where the memory is acquired (a memory mapped region, shared memory or heap). Also, it might lead to memory leaks (but how many times in a program a thread, attribute, mutex, etc are initialized :-).
Contrarily, Soaris has these types as structures. Hence it is easy to share a mutex between two processes by having the mutex in a mmaped region or shared memory.
To compile the above program in Solaris, give "CC -mt
Comments
Note that PTHREAD_PROCESS_SHARED will fail on older Linux distributions (which use the LinuxThreads pthreads implementation); newer distributions use NPTL (Native POSIX Thread Library) instead, which works fine.
One can discover which one has by running the following at the command line:
getconf GNU_LIBPTHREAD_VERSION
If that returns NPTL x.y then this example will work fine.