Commit 951d814d authored by jan.koester's avatar jan.koester
Browse files

test

parent 10222550
Loading
Loading
Loading
Loading
+9 −1
Changes for include/semaphore.h: 9 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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>

@@ -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 */
@@ -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
+99 −0
Changes for src/linux/semaphore.cpp: 99 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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>

@@ -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" {
@@ -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);
}

}