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;
+}

Reply via email to