I am trying to understand semaphores, but I cannot figure it out. I think I still have race conditions in my code
The concept is quite simple start this program 4 times using a command line argument -a, -b,-c or -d. Starting order should not matter, but with the following code (see below) it does, and I am not quite sure why.
The printed output should 1 2 3 4 5 6 7 8
in the end.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
sem_t *semdes = SEM_FAILED;
char *sem_name = "test";
int main(int argc, char *const *argv)
{
int opt, res;
semdes = sem_open(sem_name, O_CREAT | O_EXCL, 0600, 4);
printf("sem_open() returned %p\n", semdes);
if (semdes == SEM_FAILED)
{
semdes = sem_open(sem_name, 0);
}
if ((opt = getopt(argc, argv, "abcd:")) != -1)
{
switch (opt)
{
case 'a':
//printNumbers(1, 5);
sem_wait(semdes);
res = sem_wait(semdes);
if (res != 0)
{
perror("ERROR: sem_wait() failed");
}
printf("sem_wait() returned %d\n", res);
printf("%d\n", 1);
sleep(1);
sem_post(semdes);
sem_wait(semdes);
res = sem_wait(semdes);
if (res != 0)
{
perror("ERROR: sem_wait() failed");
}
printf("sem_wait() returned %d\n", res);
printf("%d\n", 5);
sleep(1);
sem_post(semdes);
break;
case 'b':
sem_wait(semdes);
res = sem_wait(semdes);
if (res != 0)
{
perror("ERROR: sem_wait() failed");
}
printf("sem_wait() returned %d\n", res);
printf("%d\n", 2);
sleep(1);
sem_post(semdes);
//printNumbers(2, 6);
break;
case 'c':
sem_wait(semdes);
res = sem_wait(semdes);
if (res != 0)
{
perror("ERROR: sem_wait() failed");
}
printf("sem_wait() returned %d\n", res);
printf("%d\n", 3);
sleep(1);
sem_post(semdes);
//printNumbers(3, 7);
break;
case 'd':
sem_wait(semdes);
res = sem_wait(semdes);
if (res != 0)
{
perror("ERROR: sem_wait() failed");
}
printf("sem_wait() returned %d\n", res);
printf("%d\n", 4);
sleep(1);
sem_post(semdes);
//printNumbers(4, 8);
break;
default:
fprintf(stderr, "ERROR: unknown option '%c'\n", opt);
exit(1);
break;
}
}
else
{
exit(1);
}
return 0;
}
I think I fail to understand how semaphores work or I am using the sem_open
incorrectly.
Using Ubuntu 18.04
EDIT This the current output generated
sem_open() returned 0x7f5fdbcbf000
sem_wait() returned 0
2
sem_open() returned (nil)
sem_wait() returned 0
3
sem_open() returned (nil)
sem_open() returned (nil)
./n: option requires an argument -- 'd'
ERROR: unknown option '?'
sem_wait() returned 0
1
^C
I solved the problem thanks to Christian idea of using multiple semaphores!
Final code !
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
sem_t *semA = SEM_FAILED;
sem_t *semB = SEM_FAILED;
sem_t *semC = SEM_FAILED;
sem_t *semD = SEM_FAILED;
sem_t *sem1 = SEM_FAILED;
sem_t *sem2 = SEM_FAILED;
char *nameA = "testa";
char *nameB = "testb";
char *nameC = "testc";
char *nameD = "testd";
int printNumbers(char *name1, char *name2, int no)
{
//open own semaphore first
sem1 = sem_open(name1, O_CREAT, 0600, 0);
int res = sem_wait(sem1);
if (res != 0)
{
perror("PROG: ERROR: sem_wait() failed");
}
//printf("sem_wait() of %s returned %d\n", name1, res);
printf("%d\n", no);
sem_close(sem1);
sleep(5);
//open next semaphore
sem2 = sem_open(name2, O_CREAT, 0600, 1);
sem_post(sem2);
sem_close(sem2);
return res;
}
void cleanUp()
{
char name[6] = "testa";
int res = 0;
for (size_t i = 0; i < 4; i++)
{
res = sem_unlink(name);
if (res != 0)
{
perror("ERROR: sem_unlink() failed");
}
printf("sem_unlink() returned %d\n", res);
name[4] = name[4]+1;
}
}
int main(int argc, char *const *argv)
{
int opt, res;
if ((opt = getopt(argc, argv, "abcd")) != -1)
{
switch (opt)
{
case 'a':
// Guarantees 1 is always first since no semaphore
printf("\n%d\n", 1);
sleep(5);
semB = sem_open(nameB, O_CREAT, 0600, 1);
sem_post(semB);
sleep(5);
sem_close(semB);
printNumbers(nameA, nameB, 5);
break;
case 'b':
printNumbers(nameB, nameC, 2);
printNumbers(nameB, nameC, 6);
break;
case 'c':
printNumbers(nameC, nameD, 3);
printNumbers(nameC, nameD, 7);
break;
case 'd':
printNumbers(nameD, nameA, 4);
printNumbers(nameD, nameA, 8);
cleanUp();
break;
default:
fprintf(stderr, "ERROR: unknown option '%c'\n", opt);
exit(1);
break;
}
}
else
{
exit(1);
}
return 0;
}
With output as :
1
2
3
4
5
6
7
8
sem_unlink() returned 0
sem_unlink() returned 0
sem_unlink() returned 0
sem_unlink() returned 0