about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-06-07 02:42:55 -0400
committerRich Felker <dalias@aerifal.cx>2011-06-07 02:42:55 -0400
commitede353d8e5c9198cf467aad95e49a496020898ed (patch)
tree446c42bc09c75f1de7e0db696701920f813b4c56
parentab11386aaaffc93755ff86199bf753a6d5ef4b45 (diff)
downloadmusl-ede353d8e5c9198cf467aad95e49a496020898ed.tar.gz
musl-ede353d8e5c9198cf467aad95e49a496020898ed.tar.xz
musl-ede353d8e5c9198cf467aad95e49a496020898ed.zip
implement mq_notify
-rw-r--r--src/mq/mq_notify.c67
1 files changed, 65 insertions, 2 deletions
diff --git a/src/mq/mq_notify.c b/src/mq/mq_notify.c
index d85db1da..60bb2db2 100644
--- a/src/mq/mq_notify.c
+++ b/src/mq/mq_notify.c
@@ -1,10 +1,73 @@
 #include <mqueue.h>
 #include <pthread.h>
 #include <errno.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <unistd.h>
 #include "syscall.h"
 
+struct args {
+	pthread_barrier_t barrier;
+	int sock;
+	const struct sigevent *sev;
+};
+
+void *start(void *p)
+{
+	struct args *args = p;
+	char buf[32];
+	ssize_t n;
+	int s = args->sock;
+	void (*func)(union sigval) = args->sev->sigev_notify_function;
+	union sigval val = args->sev->sigev_value;
+
+	pthread_barrier_wait(&args->barrier);
+	n = recv(s, buf, sizeof(buf), MSG_NOSIGNAL|MSG_WAITALL);
+	close(s);
+	if (n==sizeof buf && buf[sizeof buf - 1] == 1)
+		func(val);
+	return 0;
+}
+
 int mq_notify(mqd_t mqd, const struct sigevent *sev)
 {
-	errno = ENOSYS;
-	return -1;
+	struct args args = { .sev = sev };
+	pthread_attr_t attr;
+	pthread_t td;
+	int s;
+	struct sigevent sev2;
+	static const char zeros[32];
+
+	if (!sev || sev->sigev_notify != SIGEV_THREAD)
+		return syscall(SYS_mq_notify, mqd, sev);
+
+	s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, 0);
+	if (s < 0) return -1;
+	args.sock = s;
+
+	if (sev->sigev_notify_attributes) attr = *sev->sigev_notify_attributes;
+	else pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	pthread_barrier_init(&args.barrier, 0, 2);
+
+	if (pthread_create(&td, &attr, start, &args)) {
+		__syscall(SYS_close, s);
+		errno = EAGAIN;
+		return -1;
+	}
+
+	pthread_barrier_wait(&args.barrier);
+	pthread_barrier_destroy(&args.barrier);
+
+	sev2.sigev_notify = SIGEV_THREAD;
+	sev2.sigev_signo = s;
+	sev2.sigev_value.sival_ptr = &zeros;
+
+	if (syscall(SYS_mq_notify, mqd, &sev2) < 0) {
+		pthread_cancel(td);
+		__syscall(SYS_close, s);
+		return -1;
+	}
+
+	return 0;
 }