NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
lib/59126: pthread_once(3): missing memory ordering
>Number: 59126
>Category: lib
>Synopsis: pthread_once(3): missing memory ordering
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Mar 04 00:20:00 +0000 2025
>Originator: Taylor R Campbell
>Release: current, 10, 9, ...
>Organization:
The NetBSD Oncelerbarrier
>Environment:
>Description:
The critical rule of pthread_once(O, I) is that any memory operations during I() in any thread happen-before all memory operations after pthread_once returns.
But the optimistic unlocked test in pthread_once does not guarantee this. It needs membar_release/acquire at least.
>How-To-Repeat:
The following test program probably exhibits the issue on multicore machines with relaxed memory ordering (link with -pthread, run with the number of parallel threads to try, hit ^T for progress or ^C if you get tired of waiting):
#include <err.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
unsigned long long nloop;
static void
onsig(int signo)
{
char buf[128];
snprintf_ss(buf, sizeof(buf), "signal %d after %llu trials\n", signo,
nloop);
(void)write(STDOUT_FILENO, buf, strlen(buf));
if (signo == SIGINFO)
return;
(void)signal(signo, SIG_DFL);
(void)raise(signo);
}
pthread_once_t once, once0 = PTHREAD_ONCE_INIT;
int done = 0;
static void
init(void)
{
done = 1;
}
static void *
thread(void *cookie)
{
pthread_barrier_t *bar = cookie;
(void)pthread_barrier_wait(bar);
pthread_once(&once, &init);
if (!done)
errx(1, "fail after %llu trials", nloop);
}
int
main(int argc, char **argv)
{
enum { N = 256 };
unsigned n = argc == 2 ? atoi(argv[1]) : 16;
if (n < 1)
errx(1, "not enough");
if (n > N)
errx(1, "too many");
if (signal(SIGINT, &onsig) == SIG_ERR)
err(1, "signal(SIGINT)");
if (signal(SIGINFO, &onsig) == SIG_ERR)
err(1, "signal(SIGINFO)");
for (;; nloop++) {
pthread_barrier_t bar;
pthread_t t[N];
unsigned i;
int error;
error = pthread_barrier_init(&bar, NULL, n);
if (error)
errc(1, error, "pthread_barrier_init");
for (i = 0; i < n - 1; i++) {
error = pthread_create(&t[i], NULL, &thread, &bar);
if (error)
errc(1, error, "pthread_create");
}
once = once0;
done = 0;
(void)pthread_barrier_wait(&bar);
pthread_once(&once, &init);
if (!done)
errx(1, "fail");
for (i = 0; i < n - 1; i++) {
error = pthread_join(t[i], NULL);
if (error)
errc(1, error, "pthread_join");
}
error = pthread_barrier_destroy(&bar);
if (error)
errc(1, error, "pthread_barrier_destroy");
}
}
>Fix:
membar_release/acquire
Home |
Main Index |
Thread Index |
Old Index