POSIX has the notion of unnamed (memory-based) semaphores. These are initialized at some memory location with sem_init
, and then we can use them with sem_post
and sem_wait
. I am wondering whether it always acceptable to copy the "semaphore handle" to some other memory location and then use it as a normal, independent, well-initialized semaphore.
In other words, is it legal to do:
#include "semaphore.h"
int main()
{
sem_t s1;
sem_init(&s1, 0, 1); /* Initialize unnamed semaphore */
sem_t s2 = s1; /* Copy to some other memory location */
sem_wait(&s2); /* Lock on the semaphore */
sem_post(&s2); /* Release the lock */
return 0;
}
This seems to work on Linux completely fine, without problems. In glibc, the semaphore seems to just be 2 or 3 integers, with atomic instructions being used to make sure the that it is thread- (or even process-) safe: https://github.com/bminor/glibc/blob/6c2f050dbe11fb4ed0a401a5f25731f2aa53046b/htl/pt-internal.h#L333 So, if I copy the semaphore, it will just copy the current state of it (its value) and that is fine.
However, I am wondering whether this might be just accidental, and we cannot rely on this behaviour on other POSIX implementations.
For example, I found that in the FreeRTOS POSIX layer (https://freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_POSIX/index.html), this is not valid, because its semaphores are implemented on top of FreeRTOS semaphores, and the FreeRTOS kernel keeps track of the addresses of initialized semaphores. So, it is not possible to just blindly copy a semaphore handle to a different memory location and use it then. Of course, note that the FreeRTOS POSIX library does not claim to be POSIX-compliant, and it only implements a small subset of the POSIX API, so this is just an example.
I was not able to find any mention of this behaviour in the official POSIX docs.
CodePudding user response:
The POSIX specification mentions:
For barriers, condition variables, mutexes, and read-write locks, if the process-shared attribute is set to PTHREAD_PROCESS_PRIVATE, only the synchronization object at the address used to initialize it can be used for performing synchronization. The effect of referring to another mapping of the same object when locking, unlocking, or destroying the object is undefined. If the process-shared attribute is set to PTHREAD_PROCESS_SHARED, only the synchronization object itself can be used for performing synchronization; however, it need not be referenced at the address used to initialize it (that is, another mapping of the same object can be used). The effect of referring to a copy of the object when locking, unlocking, or destroying it is undefined.
So, to be POSIX compliant, the copy is not allowed.