Loading include/semaphore.h +9 −1 Changes for include/semaphore.h: 9 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Real POSIX unnamed semaphores -- were missing entirely (real gap: * CMake's bundled libuv builds uv_sem_t on sem_t). Futex-based, see * src/linux/semaphore.cpp. Named semaphores (sem_open) aren't provided. */ * src/linux/semaphore.cpp. Named semaphores (sem_open, real gap: x265's * threading.h) are such a sem_t in a /dev/shm/sem.<name> file mapped * MAP_SHARED, the same layout glibc and musl use. */ #include <time.h> Loading @@ -38,6 +40,7 @@ extern "C" { #endif #define SEM_VALUE_MAX 0x7fffffff #define SEM_FAILED ((sem_t*)0) typedef struct { volatile int __value; /* the count */ Loading @@ -54,6 +57,11 @@ int sem_timedwait(sem_t* sem, const struct timespec* abstime); int sem_post(sem_t* sem); int sem_getvalue(sem_t* sem, int* value); /* oflag O_CREAT takes two more arguments: mode_t mode, unsigned value */ sem_t* sem_open(const char* name, int oflag, ...); int sem_close(sem_t* sem); int sem_unlink(const char* name); #ifdef __cplusplus } #endif src/linux/semaphore.cpp +99 −0 Changes for src/linux/semaphore.cpp: 99 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -33,6 +33,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <semaphore.h> #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> #include <bits/syscall.h> #include <bits/sysbits.h> Loading Loading @@ -78,6 +83,37 @@ int wait_impl(sem_t* s, const struct timespec* abstime) { } } const char kSemPrefix[] = "/dev/shm/sem."; /* "/name" -> "/dev/shm/sem.name" (one leading slash, no others) */ int sem_path(const char* name, char* buf, size_t bufsize) { if (*name == '/') name++; if (!*name || strchr(name, '/')) { errno = EINVAL; return -1; } size_t plen = sizeof(kSemPrefix) - 1; size_t nlen = strlen(name); if (plen + nlen + 1 > bufsize) { errno = ENAMETOOLONG; return -1; } memcpy(buf, kSemPrefix, plen); memcpy(buf + plen, name, nlen + 1); return 0; } char* append_uint(char* p, unsigned long v) { char t[24]; int n = 0; do t[n++] = (char)('0' + v % 10); while (v /= 10); while (n) *p++ = t[--n]; *p = 0; return p; } sem_t* map_sem(int fd) { void* p = mmap(nullptr, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); int e = errno; close(fd); if (p == MAP_FAILED) { errno = e; return SEM_FAILED; } return (sem_t*)p; } } // namespace extern "C" { Loading Loading @@ -117,4 +153,67 @@ int sem_getvalue(sem_t* sem, int* value) { return 0; } /* A new semaphore is written to a private temp file first and only then * link()ed under its name, so no other sem_open() can map it half * initialised; EEXIST from link() means another process won the race and * (without O_EXCL) we open theirs. */ sem_t* sem_open(const char* name, int oflag, ...) { char path[256]; if (sem_path(name, path, sizeof(path)) < 0) return SEM_FAILED; mode_t mode = 0; unsigned int value = 0; if (oflag & O_CREAT) { va_list ap; va_start(ap, oflag); mode = (mode_t)va_arg(ap, unsigned int); value = va_arg(ap, unsigned int); va_end(ap); if (value > SEM_VALUE_MAX) { errno = EINVAL; return SEM_FAILED; } } static unsigned long counter; for (;;) { if (!(oflag & O_CREAT) || !(oflag & O_EXCL)) { int fd = open(path, O_RDWR | O_NOFOLLOW | O_CLOEXEC); if (fd >= 0) return map_sem(fd); if (errno != ENOENT || !(oflag & O_CREAT)) return SEM_FAILED; } char tmp[64]; char* t = tmp; memcpy(t, "/dev/shm/sem.tmp-", 17); t = append_uint(t + 17, (unsigned long)getpid()); *t++ = '-'; append_uint(t, __atomic_add_fetch(&counter, 1, __ATOMIC_RELAXED)); int fd = open(tmp, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, mode); if (fd < 0) return SEM_FAILED; sem_t init; memset(&init, 0, sizeof(init)); sem_init(&init, 1, value); if (write(fd, &init, sizeof(init)) != (long)sizeof(init)) { int e = errno; close(fd); unlink(tmp); errno = e; return SEM_FAILED; } int r = link(tmp, path); int e = errno; unlink(tmp); if (r == 0) return map_sem(fd); close(fd); if (e != EEXIST || (oflag & O_EXCL)) { errno = e; return SEM_FAILED; } } } int sem_close(sem_t* sem) { return munmap(sem, sizeof(sem_t)); } int sem_unlink(const char* name) { char path[256]; if (sem_path(name, path, sizeof(path)) < 0) return -1; return unlink(path); } } Loading
include/semaphore.h +9 −1 Changes for include/semaphore.h: 9 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Real POSIX unnamed semaphores -- were missing entirely (real gap: * CMake's bundled libuv builds uv_sem_t on sem_t). Futex-based, see * src/linux/semaphore.cpp. Named semaphores (sem_open) aren't provided. */ * src/linux/semaphore.cpp. Named semaphores (sem_open, real gap: x265's * threading.h) are such a sem_t in a /dev/shm/sem.<name> file mapped * MAP_SHARED, the same layout glibc and musl use. */ #include <time.h> Loading @@ -38,6 +40,7 @@ extern "C" { #endif #define SEM_VALUE_MAX 0x7fffffff #define SEM_FAILED ((sem_t*)0) typedef struct { volatile int __value; /* the count */ Loading @@ -54,6 +57,11 @@ int sem_timedwait(sem_t* sem, const struct timespec* abstime); int sem_post(sem_t* sem); int sem_getvalue(sem_t* sem, int* value); /* oflag O_CREAT takes two more arguments: mode_t mode, unsigned value */ sem_t* sem_open(const char* name, int oflag, ...); int sem_close(sem_t* sem); int sem_unlink(const char* name); #ifdef __cplusplus } #endif
src/linux/semaphore.cpp +99 −0 Changes for src/linux/semaphore.cpp: 99 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -33,6 +33,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <semaphore.h> #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> #include <bits/syscall.h> #include <bits/sysbits.h> Loading Loading @@ -78,6 +83,37 @@ int wait_impl(sem_t* s, const struct timespec* abstime) { } } const char kSemPrefix[] = "/dev/shm/sem."; /* "/name" -> "/dev/shm/sem.name" (one leading slash, no others) */ int sem_path(const char* name, char* buf, size_t bufsize) { if (*name == '/') name++; if (!*name || strchr(name, '/')) { errno = EINVAL; return -1; } size_t plen = sizeof(kSemPrefix) - 1; size_t nlen = strlen(name); if (plen + nlen + 1 > bufsize) { errno = ENAMETOOLONG; return -1; } memcpy(buf, kSemPrefix, plen); memcpy(buf + plen, name, nlen + 1); return 0; } char* append_uint(char* p, unsigned long v) { char t[24]; int n = 0; do t[n++] = (char)('0' + v % 10); while (v /= 10); while (n) *p++ = t[--n]; *p = 0; return p; } sem_t* map_sem(int fd) { void* p = mmap(nullptr, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); int e = errno; close(fd); if (p == MAP_FAILED) { errno = e; return SEM_FAILED; } return (sem_t*)p; } } // namespace extern "C" { Loading Loading @@ -117,4 +153,67 @@ int sem_getvalue(sem_t* sem, int* value) { return 0; } /* A new semaphore is written to a private temp file first and only then * link()ed under its name, so no other sem_open() can map it half * initialised; EEXIST from link() means another process won the race and * (without O_EXCL) we open theirs. */ sem_t* sem_open(const char* name, int oflag, ...) { char path[256]; if (sem_path(name, path, sizeof(path)) < 0) return SEM_FAILED; mode_t mode = 0; unsigned int value = 0; if (oflag & O_CREAT) { va_list ap; va_start(ap, oflag); mode = (mode_t)va_arg(ap, unsigned int); value = va_arg(ap, unsigned int); va_end(ap); if (value > SEM_VALUE_MAX) { errno = EINVAL; return SEM_FAILED; } } static unsigned long counter; for (;;) { if (!(oflag & O_CREAT) || !(oflag & O_EXCL)) { int fd = open(path, O_RDWR | O_NOFOLLOW | O_CLOEXEC); if (fd >= 0) return map_sem(fd); if (errno != ENOENT || !(oflag & O_CREAT)) return SEM_FAILED; } char tmp[64]; char* t = tmp; memcpy(t, "/dev/shm/sem.tmp-", 17); t = append_uint(t + 17, (unsigned long)getpid()); *t++ = '-'; append_uint(t, __atomic_add_fetch(&counter, 1, __ATOMIC_RELAXED)); int fd = open(tmp, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, mode); if (fd < 0) return SEM_FAILED; sem_t init; memset(&init, 0, sizeof(init)); sem_init(&init, 1, value); if (write(fd, &init, sizeof(init)) != (long)sizeof(init)) { int e = errno; close(fd); unlink(tmp); errno = e; return SEM_FAILED; } int r = link(tmp, path); int e = errno; unlink(tmp); if (r == 0) return map_sem(fd); close(fd); if (e != EEXIST || (oflag & O_EXCL)) { errno = e; return SEM_FAILED; } } } int sem_close(sem_t* sem) { return munmap(sem, sizeof(sem_t)); } int sem_unlink(const char* name) { char path[256]; if (sem_path(name, path, sizeof(path)) < 0) return -1; return unlink(path); } }