2

I'm sharing a counter variable between a multi-process program, where processes are created using the fork() call. I'm using the following code to create a shared memory where the counter is stored, and can be accessed and incremented by each process.

This is the thread creation mechanism in the main parent thread

void createSHM()
{
int key = SHMKEY;
int *shm;

if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
    perror("shmget");
    exit(1);
}

if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
    perror("shmat");
    exit(1);
}
*shm=0
}

and this is the function called in each of the forked processes to get the value of the counter, and increment it.

int attachSHMandIncrement()
{
int key = SHMKEY;
int *shm;

if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
    perror("shmget");
    exit(1);
}

if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
    perror("shmat");
    exit(1);
}
*shm++;
return *shm;
}
  1. I'm not sure if this implementation is race free? Can anyone comment on if it is, and if not, an example of how this can be made race free?

  2. Given the fact that these are forked processes and will inherit the memory at the time of fork. Is there anything I should keep in mind? I've done this before in totally separate processes but here I might have to do it in forked processes.

tsar2512
  • 2,826
  • 3
  • 33
  • 61
  • @SSC That question does not have any example code, and only suggests several possible synchronization examples. – tsar2512 Dec 01 '14 at 02:14
  • @SSC - since the code does not offer any examples, can you remove possible duplicate flag? Unfortunately, I cannot do so. I simply want a race free implementation. I'm not sure if my current implementation is race free – tsar2512 Dec 01 '14 at 03:32
  • Typically, mutexes are used in a threaded program to serialize thread access to a local resource (like a global variable or a file descriptor), and semaphores are used to serialize access by different processes to a resource (like shared memory). Are you questioning HOW to use a mutex (and by extension how to use a semaphore)...? – TonyB Dec 01 '14 at 06:04
  • @TonyB - Yes, I am asking how to use semaphores in the above example? Or alternatively as Chris below suggested an alternate way of using atomic operations for synchronization. – tsar2512 Dec 01 '14 at 21:40

2 Answers2

2

Since you don't have any mutexes, there's nothing to stop two processes from trying to increment the counter simultaneously, and since increment of an int is generally not atomic, this is an immediate race condition. Over and above the increment race, the routine increments and then reads the value, which is also not atomic, so subject to races.

In order to avoid races, you need some sort of mutex. You could use a semaphore, or you might be able to use a pthread mutex in shared memory (if you have pthreads available). Alternately you may be able use file locks, or you could write your own atomic assembly routines and call them. If you're using C11, you could even use the stuff in stdatomic.h. Lots of possible choices.

edit

You can wrap a mutex around the racing operations to avoid the race condition at the end of your increment function:

    struct sembuf sem_ops = { 0, -1, SEM_UNDO };
    semop(semid, &sem_ops, 1);
    *shm++;
    rv = *shm;
    sem_ops.sem_op = 1;
    semop(semid, &sem_ops, 1);
    return rv;
}

Generally, you'll just attach the shared memory once in each process (and create it once in one process), and then just use it, without repeatedly attaching and detaching it. Similarly, you'll create and initialize your mutexes once, and then use them.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • @Chris_Dodd : mutexes is what I am using now. Since I am using multi-process, can one make the shared variable increment atomic? Also I read several places that atomic operations ensure that the update is indeed atomic, but in a multi-process, multiple processor environment... you can still show up with a race, depending on how the caching works in the hardware. Don't know if that's true however. Could you give an example with my code as a reference on how to use atomic updates? – tsar2512 Dec 01 '14 at 14:40
  • @tsar2512: Your code as posted does not contain any mutexes. I added an example of how you might add a mutex to your Increment function. – Chris Dodd Dec 01 '14 at 17:01
  • @Chris_Dodd - Sorry I misread your update initially.. I've added an answer with code of how the mutex will work for the increment. That should hopefully work in my application. still haven't integrated. I was wondering if you can give a "code" example of how stdatomic.h can be used to achieve the same result. – tsar2512 Dec 01 '14 at 17:06
  • @chris_dodd - Thanks for your answer, one final clarification? If I am forking processes. Do I need to create and initialize mutexes in each child process? Or is it sufficient to do so only in the parent. – tsar2512 Dec 01 '14 at 23:00
  • @tsar2512: You only create the mutexes once, in one process. Then every process need to attach the mutexes, which you can do just in the parent before forking (so each forked child inherits the descriptor) – Chris Dodd Dec 03 '14 at 02:44
0

I found a solution online, that works for me. I am posting it here since I wanted an example code as an answer anyways. I hope this helps others.

This is using an mmapped area shared between the processes, and using a mutex to ensure no race condition.

#include "unpipc.h"
#define SEM_NAME "mysem"

int main(int argc, char **argv)
{
    int fd, i, nloop, zero = 0; 7 int *ptr;
    sem_t  *mutex;
    if (argc != 3)
       err_quit("usage: incr2  &lt;#loops&gt;");
     nloop=atoi(argv[2]); 
      /* open file, initialize to 0, map into memory */
      fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
     write(fd,&amp;zero,sizeof(int)); 
     ptr = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     close(fd);
     /* create, initialize, and unlink semaphore */
     mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1);
     sem_unlink(Px_ipc_name(SEM_NAME));
     setbuf(stdout, NULL);       /* stdout is unbuffered */
     if (Fork() == 0) {          /* child */
     for (i = 0; i &lt; nloop; i++) {
         sem_wait(mutex);
         printf("child:%d\n",(*ptr)++); 
         sem_post(mutex);
     }
     exit(0);
     }
     /*parent*/ 
     for (i = 0; i &lt; nloop; i++) {
        sem_wait(mutex);
        printf("parent:%d\n",(*ptr)++); 
        sem_post(mutex);
     }
      exit(0);
     }
tsar2512
  • 2,826
  • 3
  • 33
  • 61