Karachi   ->   Sweden   ->   Karachi, again   ->   Dubai   ->   Bahrain   ->   Karachi, once more   ->   London and Leeds

Friday, December 24, 2010

System V Semaphores: How not to design an API

I recently read the chapter on Inter Process Communication from Advanced Linux Programming book. To my horror, the semaphore way of interprocess communication was unnecessarily complicated. Though the ALP book doesn't mention anything about alternates, after some search on the Internet, I found that one can have POSIX named semaphores instead for synchronization between processes on a Linux system.

Below is a gist of what's wrong with System V Semaphore design.

Allocation and Deallocation Symmetry

semget()Allocate a semaphore
semctl()Deallocate a semaphore

Can you see the lack of symmetry? If semget() allocates a semaphore, shouldn't the deallocation function be called semremove() or semunget()? Please note that semremove() and semunget() are my suggestions---they don't exist!

Undefined Union!

Adding fuel to the fire, here is a typical call to semctl() to remove a semaphore:
semctl (semdid, 1, IPC_RMID, ignoredArgument);
Yes, you read it right: ignoredArgument. And what's its type? It's union semun ignoredArgument. And in which header file is semun is defined? Nowhere!

One has to define this union himself!! That is, if you are going to use System V semaphores, somewhere in your code you must define this union:
union semun {
int val;
struct semid_ds *buff;
unsigned short int *array;
struct seminfo *_buff;

Why on earth this union definition is not included in the standard sys/sem.h is beyond me.

What's the Use of an Un-initialized Semaphore

If you think semget() gives you a ready to use semaphore, then you are mistaken. It only gives you an uninitialized one. You must invoke semctl() on this semaphore to initialize it as follows:
union semun argument;
unsigned short int values[1];
Values[0] = 1;
argument.array = values;
semctl (semid, 0, SETALL, argument);

Now you must have come to know where semun is used.

Waiting and Posting on the Semaphore

Is it too much to ask to create separate sem_wait() and sem_post() methods? Please note that sem_wait() and sem_post() do exist in POSIX but not in System V. Someone designing the System V api thought that one can have a single function to do both, and the difference can be indicated with the help of a passed argument. Hence came the semop() function. Below is how to wait on an initialized semaphore:
struct sembuf operations[1];
operations[0].sem_num = 0;
operations[0].sem_op = -1;
operations[0].sem_flg = SEM_UNDO;
semop (semid, operations, 1);

Why sem_flg and not sem_flag is also beyond me! How much space is saved by declaring the flag member variable as sem_flg instead of sem_flag?

Below is how to post on a semaphore:
struct sembuf operations[1];
operations[0].sem_num = 0;
operations[0].sem_op = 1;
operations[0].sem_flg = SEM_UNDO;
semop (semid, operations, 1);

I wonder why the ALP book chose to teach System V semaphores in chapter 5 when it had already introduced POSIX semaphores as part of chapter 4 on Threads. Someone learning from the book can wrongly deduce that POSIX semaphores are for inter-thread communication while System V semaphores are for inter-process communication. This is plain wrong! Named POSIX semaphores are kernel level objects and can effectively be used for IPC.