Reputation: 13
I'm trying to create semaphores in an initialization function in C like that:
void sem_init(int size, sem_t** sem1, sem_t** sem2) {
char* semname1 = "/somename";
char* semname2 = "/someothername";
errno = 0;
*sem1 = sem_open(semname1, O_CREAT, S_IRUSR|S_IWUSR, 0);
printf(strerror(errno));
errno = 0;
*sem2 = sem_open(semname2, O_CREAT, S_IRUSR|S_IWUSR, size);
printf(strerror(errno));
}
But even though I set to O_CREAT flag and the names are well-formed, I always get "No such file or directory" as output. The semaphores are created at /dev/shm/...
I don't see any obvious reason for the error to occur. Please help me on what I'm doing wrong.
Upvotes: 0
Views: 4281
Reputation: 5211
As reported in the comments and previous answers, errno is undefined after a service call if the latter did not return in error (e.g. -1 or SEM_FAILED or MAP_FAILED depending on the service). The reason comes from the fact that the service can call several sub-services which may fail temporarily and trigger the setting of errno to some values but this does not make fail the service itself.
As an example, internally sem_open()
creates a temporary file in /dev/shm. To make it, it generates a random name thanks to mktemp()
service. But the generated name may already exist. So, the creation attempt of the file will make open()
return EEXIST. Hence, sem_open()
source code makes several attempts generating random file names until the creation succeeds. So, once the creation succeeded, the errno may still contain EEXIST but the returned code from sem_open()
is OK (i.e. different than SEM_FAILED). Here is the code snippet which makes all those tries (glibc-2.34, file sysdeps/pthread/sem_open.c):
#define NRETRIES 50
while (1)
{
/* We really want to use mktemp here. We cannot use mkstemp
since the file must be opened with a specific mode. The
mode cannot later be set since then we cannot apply the
file create mask. */
if (__mktemp (tmpfname) == NULL)
{
result = SEM_FAILED;
goto out;
}
/* Open the file. Make sure we do not overwrite anything. */
fd = __open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode);
if (fd == -1)
{
if (errno == EEXIST)
{
if (++retries < NRETRIES)
{
/* Restore the six placeholder bytes before the
null terminator before the next attempt. */
memcpy (tmpfname + sizeof (tmpfname) - 7, "XXXXXX", 6);
continue;
}
__set_errno (EAGAIN);
}
result = SEM_FAILED;
goto out;
}
/* We got a file. */
break;
}
In the OP's use case, the errno is ENOENT while the service returns a value different than SEM_FAILED. In fact the service realizes that the file to create already exists. Since the O_EXCL flag is not passed, this is considered OK. So, the file is removed and an open attempt is done again to make sure to get ENOENT before re-creating the file. Here is the code snippet part of the same function for this use case:
[...]
try_again:
fd = __open (dirname.name,
(oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
if (fd == -1)
{
/* If we are supposed to create the file try this next. */
if ((oflag & O_CREAT) != 0 && errno == ENOENT)
goto try_create;
/* Return. errno is already set. */
}
else
/* Check whether we already have this semaphore mapped and
create one if necessary. */
result = __sem_check_add_mapping (name, fd, SEM_FAILED);
[...]
if ((oflag & O_EXCL) == 0 && errno == EEXIST)
{
/* Remove the file. */
__unlink (tmpfname);
/* Close the file. */
__close (fd);
goto try_again;
}
[...]
Upvotes: 0
Reputation: 16540
as the comments to the OPs question have indicated, the incorrect value in errno
is being used. Suggest:
void sem_init(int size, sem_t** sem1, sem_t** sem2) {
char* semname1 = "/somename";
char* semname2 = "/someothername";
sem_t local_sem1;
sem_t local_sem2;
if( (local_sem1 = sem_open(semname1, O_CREAT, S_IRUSR|S_IWUSR, 0) ) == SEM_FAILED )
{
perror( "sem_open for sem1 failed" );
exit( EXIT_FAILURE );
}
*sem1 = local_sem1;
if( (local_sem2 = sem_open(semname2, O_CREAT, S_IRUSR|S_IWUSR, 0) ) == SEM_FAILED )
{
perror( "sem_open doe awm2 failed" );
exit( EXIT_FAILURE );
}
*sem2 = local_sem2;
}
Upvotes: 4