Hi,
Per this bit from pthread_once(3):
> The function pthread_once() is not a cancellation point.
> However, if init_routine() is a cancellation point and is
> cancelled, the effect on once_control is as if pthread_once()
> was never called.
it should be possible to cancel a thread from init_routine() and
send in another thread to retry. But the attached regression
test fails, so my guess is that there needs to be something like a
pthread_cleanup wrapper around init_routine() in pthread_once() in
order for it to be POSIX-compliant.
Before pthread_once(3) was moved from librthread into libc the
solution could have been akin to:
Index: lib/libc/thread/rthread_once.c
===================================================================
RCS file: /cvs/src/lib/libc/thread/rthread_once.c,v
retrieving revision 1.1
diff -u -p -r1.1 rthread_once.c
--- lib/libc/thread/rthread_once.c 15 Aug 2017 06:13:24 -0000 1.1
+++ lib/libc/thread/rthread_once.c 2 Oct 2017 01:25:49 -0000
@@ -23,7 +23,10 @@ pthread_once(pthread_once_t *once_contro
{
pthread_mutex_lock(&once_control->mutex);
if (once_control->state == PTHREAD_NEEDS_INIT) {
+ pthread_cleanup_push(pthread_mutex_unlock,
+ &once_control->mutex);
init_routine();
+ pthread_cleanup_pop(0);
once_control->state = PTHREAD_DONE_INIT;
}
pthread_mutex_unlock(&once_control->mutex);
But pthread_once(3) is in libc now while pthread_cleanup_push and
pthread_cleanup_pop remain in librthread, so you have a choice:
1. Move the pthread_cleanup interfaces into libc, or
2. Copy their contents and do the cleanup push/pop on the
stack in pthread_once().
But I'm not a committer and I'm out of my depth. And given the
recent interface migrations I don't know which is most appropriate.
Thoughts?
--
Scott Cheloha
P.S. I've never cvs-add'd directories or files before. Or written
a bsd.regress.mk regression test. Did my best to make it look like
the others in regress/lib/libpthread. Sorry if I botched it.
Index: regress/lib/libpthread/Makefile
===================================================================
RCS file: /cvs/src/regress/lib/libpthread/Makefile,v
retrieving revision 1.48
diff -u -p -r1.48 Makefile
--- regress/lib/libpthread/Makefile 7 Jul 2017 23:55:21 -0000 1.48
+++ regress/lib/libpthread/Makefile 2 Oct 2017 02:21:31 -0000
@@ -18,7 +18,7 @@ SUBDIR+= barrier blocked_shutdown \
group netdb pcap poll preemption preemption_float \
pthread_atfork pthread_cond_timedwait pthread_create \
pthread_join pthread_kill pthread_mutex \
- pthread_rwlock pthread_specific \
+ pthread_once pthread_rwlock pthread_specific \
readdir restart \
select semaphore setjmp sigdeliver siginfo \
siginterrupt signal signals signodefer sigsuspend sigwait sleep \
Index: regress/lib/libpthread/pthread_once/Makefile
===================================================================
RCS file: regress/lib/libpthread/pthread_once/Makefile
diff -N regress/lib/libpthread/pthread_once/Makefile
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ regress/lib/libpthread/pthread_once/Makefile 2 Oct 2017 02:21:31
-0000
@@ -0,0 +1,5 @@
+# $OpenBSD$
+
+PROG= pthread_once
+
+.include <bsd.regress.mk>
Index: regress/lib/libpthread/pthread_once/pthread_once.c
===================================================================
RCS file: regress/lib/libpthread/pthread_once/pthread_once.c
diff -N regress/lib/libpthread/pthread_once/pthread_once.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ regress/lib/libpthread/pthread_once/pthread_once.c 2 Oct 2017 02:21:31
-0000
@@ -0,0 +1,48 @@
+/* $OpenBSD$ */
+
+/*
+ * Scott Cheloha <[email protected]>, 2017. Public Domain.
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include "test.h"
+
+pthread_once_t once_control = PTHREAD_ONCE_INIT;
+int done = 0;
+
+void
+init_routine(void)
+{
+ static int first = 1;
+
+ if (first) {
+ first = 0;
+ CHECKr(pthread_cancel(pthread_self()));
+ pthread_testcancel();
+ }
+ done = 1;
+}
+
+void *
+thread_main(void *arg)
+{
+ pthread_once(&once_control, init_routine);
+ return NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+ pthread_t tid;
+
+ CHECKr(pthread_create(&tid, NULL, thread_main, NULL));
+ CHECKr(pthread_join(tid, NULL));
+ ASSERT(!done);
+ alarm(5); /* if this rings we are probably deadlocked */
+ CHECKr(pthread_once(&once_control, init_routine));
+ alarm(0);
+ ASSERT(done);
+ SUCCEED;
+}