mq_notify: use semaphore instead of barrier to sync args consumption
[musl] / src / mq / mq_notify.c
1 #include <mqueue.h>
2 #include <pthread.h>
3 #include <errno.h>
4 #include <sys/socket.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #include <semaphore.h>
8 #include "syscall.h"
9
10 struct args {
11         sem_t sem;
12         int sock;
13         const struct sigevent *sev;
14 };
15
16 static void *start(void *p)
17 {
18         struct args *args = p;
19         char buf[32];
20         ssize_t n;
21         int s = args->sock;
22         void (*func)(union sigval) = args->sev->sigev_notify_function;
23         union sigval val = args->sev->sigev_value;
24
25         sem_post(&args->sem);
26         n = recv(s, buf, sizeof(buf), MSG_NOSIGNAL|MSG_WAITALL);
27         close(s);
28         if (n==sizeof buf && buf[sizeof buf - 1] == 1)
29                 func(val);
30         return 0;
31 }
32
33 int mq_notify(mqd_t mqd, const struct sigevent *sev)
34 {
35         struct args args = { .sev = sev };
36         pthread_attr_t attr;
37         pthread_t td;
38         int s;
39         struct sigevent sev2;
40         static const char zeros[32];
41         int cs;
42
43         if (!sev || sev->sigev_notify != SIGEV_THREAD)
44                 return syscall(SYS_mq_notify, mqd, sev);
45
46         s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, 0);
47         if (s < 0) return -1;
48         args.sock = s;
49
50         if (sev->sigev_notify_attributes) attr = *sev->sigev_notify_attributes;
51         else pthread_attr_init(&attr);
52         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
53         sem_init(&args.sem, 0, 0);
54
55         if (pthread_create(&td, &attr, start, &args)) {
56                 __syscall(SYS_close, s);
57                 errno = EAGAIN;
58                 return -1;
59         }
60
61         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
62         sem_wait(&args.sem);
63         pthread_setcancelstate(cs, 0);
64         sem_destroy(&args.sem);
65
66         sev2.sigev_notify = SIGEV_THREAD;
67         sev2.sigev_signo = s;
68         sev2.sigev_value.sival_ptr = (void *)&zeros;
69
70         if (syscall(SYS_mq_notify, mqd, &sev2) < 0) {
71                 pthread_cancel(td);
72                 __syscall(SYS_close, s);
73                 return -1;
74         }
75
76         return 0;
77 }