mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-24 02:03:35 +08:00
f0419e6a10
This is another attempt at making pthread_once handle throwing exceptions from the init routine callback. As the new testcases show, just switching to the cleanup attribute based cleanup does fix the tst-once5 test, but breaks the new tst-oncey3 test. That is because when throwing exceptions, only the unwind info registered cleanups (i.e. C++ destructors or cleanup attribute), when cancelling threads and there has been unwind info from the cancellation point up to whatever needs cleanup both unwind info registered cleanups and THREAD_SETMEM (self, cleanup, ...) registered cleanups are invoked, but once we hit some frame with no unwind info, only the THREAD_SETMEM (self, cleanup, ...) registered cleanups are invoked. So, to stay fully backwards compatible (allow init routines without unwind info which encounter cancellation points) and handle exception throwing we actually need to register the pthread_once cleanups in both unwind info and in the THREAD_SETMEM (self, cleanup, ...) way. If an exception is thrown, only the former will happen and we in that case need to also unregister the THREAD_SETMEM (self, cleanup, ...) registered handler, because otherwise after catching the exception the user code could call deeper into the stack some cancellation point, get cancelled and then a stale cleanup handler would clobber stack and probably crash. If a thread calling init routine is cancelled and unwind info ends before the pthread_once frame, it will be cleaned up through self->cleanup as before. And if unwind info is present, unwind_stop first calls the self->cleanup registered handler for the frame, then it will call the unwind info registered handler but that will already see __do_it == 0 and do nothing.
80 lines
2.2 KiB
C++
80 lines
2.2 KiB
C++
/* Copyright (C) 2015-2021 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
|
|
|
// Exception type thrown from the pthread_once init routine.
|
|
struct OnceException { };
|
|
|
|
// Test iteration counter.
|
|
static int niter;
|
|
|
|
static void
|
|
init_routine (void)
|
|
{
|
|
if (niter < 2)
|
|
throw OnceException ();
|
|
}
|
|
|
|
// Verify that an exception thrown from the pthread_once init routine
|
|
// is propagated to the pthread_once caller and that the function can
|
|
// be subsequently invoked to attempt the initialization again.
|
|
static int
|
|
do_test (void)
|
|
{
|
|
int result = 1;
|
|
|
|
// Repeat three times, having the init routine throw the first two
|
|
// times and succeed on the final attempt.
|
|
for (niter = 0; niter != 3; ++niter) {
|
|
|
|
try {
|
|
int rc = pthread_once (&once, init_routine);
|
|
if (rc)
|
|
fprintf (stderr, "pthread_once failed: %i (%s)\n",
|
|
rc, strerror (rc));
|
|
|
|
if (niter < 2)
|
|
fputs ("pthread_once unexpectedly returned without"
|
|
" throwing an exception", stderr);
|
|
}
|
|
catch (OnceException) {
|
|
if (niter > 1)
|
|
fputs ("pthread_once unexpectedly threw", stderr);
|
|
result = 0;
|
|
}
|
|
catch (...) {
|
|
fputs ("pthread_once threw an unknown exception", stderr);
|
|
}
|
|
|
|
// Abort the test on the first failure.
|
|
if (result)
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#define TEST_FUNCTION do_test ()
|
|
#include "../test-skeleton.c"
|