mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-04-28 06:33:36 +00:00
sigprocmask: On mingw, avoid dependency on libwinpthread.
* lib/sigprocmask.c: Include windows-tls.h, windows-once.h. (thread_local): Remove macro. (struct per_thread): New type. (tls_key): New variable. (keys_init): New function. (keys_init_once): New variable. (get_per_thread): New function. (per_thread_singleton): New variable. (blocked_set, pending_array): Remove variables. (sigpending, override_handler, pthread_sigmask, _gl_raise_SIGPIPE): Invoke get_per_thread, to access blocked_set or pending_array. * modules/sigprocmask (Depends-on): Add windows-once, windows-tls.
This commit is contained in:
16
ChangeLog
16
ChangeLog
@@ -1,3 +1,19 @@
|
||||
2026-04-14 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
sigprocmask: On mingw, avoid dependency on libwinpthread.
|
||||
* lib/sigprocmask.c: Include windows-tls.h, windows-once.h.
|
||||
(thread_local): Remove macro.
|
||||
(struct per_thread): New type.
|
||||
(tls_key): New variable.
|
||||
(keys_init): New function.
|
||||
(keys_init_once): New variable.
|
||||
(get_per_thread): New function.
|
||||
(per_thread_singleton): New variable.
|
||||
(blocked_set, pending_array): Remove variables.
|
||||
(sigpending, override_handler, pthread_sigmask, _gl_raise_SIGPIPE):
|
||||
Invoke get_per_thread, to access blocked_set or pending_array.
|
||||
* modules/sigprocmask (Depends-on): Add windows-once, windows-tls.
|
||||
|
||||
2026-04-14 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
sigaction: Fix test failure on native Windows.
|
||||
|
||||
@@ -28,11 +28,16 @@
|
||||
# include "msvc-inval.h"
|
||||
#endif
|
||||
|
||||
#if GNULIB_SIGPROCMASK_SINGLE_THREAD
|
||||
#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
|
||||
# include "windows-spin.h"
|
||||
#else
|
||||
# define glwthread_spin_lock(lock)
|
||||
# define glwthread_spin_unlock(lock)
|
||||
#else
|
||||
# include "windows-spin.h"
|
||||
#endif
|
||||
|
||||
#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
|
||||
# include "windows-tls.h"
|
||||
# include "windows-once.h"
|
||||
#endif
|
||||
|
||||
/* We assume that a platform without POSIX signal blocking functions
|
||||
@@ -181,7 +186,6 @@ sigdelset (sigset_t *set, int sig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sigfillset (sigset_t *set)
|
||||
{
|
||||
@@ -189,28 +193,67 @@ sigfillset (sigset_t *set)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Storage class specifier for thread-local storage. */
|
||||
#undef thread_local
|
||||
#if defined _MSC_VER
|
||||
/* <https://learn.microsoft.com/en-us/cpp/parallel/thread-local-storage-tls> */
|
||||
# define thread_local __declspec(thread)
|
||||
/* Thread-local storage.
|
||||
We don't use 'thread_local' here, since on mingw it (or '__thread') pulls in
|
||||
libwinpthread, which we want to avoid. See gl_AVOID_WINPTHREAD. */
|
||||
|
||||
struct per_thread
|
||||
{
|
||||
/* Set of currently blocked signals. */
|
||||
sigset_t blocked_set /* = 0 */;
|
||||
/* Set of currently blocked and pending signals. */
|
||||
volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
|
||||
};
|
||||
|
||||
#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
|
||||
|
||||
static glwthread_tls_key_t tls_key; /* TLS key for a 'struct per_thread *' */
|
||||
|
||||
static void
|
||||
keys_init (void)
|
||||
{
|
||||
if (glwthread_tls_key_create (&tls_key, free) != 0)
|
||||
/* Should not happen. */
|
||||
abort ();
|
||||
/* The per-thread initial value is NULL. */
|
||||
}
|
||||
|
||||
/* Ensure that keys_init is called once only. */
|
||||
static glwthread_once_t keys_init_once = GLWTHREAD_ONCE_INIT;
|
||||
|
||||
/* Returns the per-thread data belonging to the current thread. */
|
||||
static struct per_thread *
|
||||
get_per_thread (void)
|
||||
{
|
||||
glwthread_once (&keys_init_once, keys_init);
|
||||
struct per_thread *data = glwthread_tls_get (tls_key);
|
||||
if (data == NULL)
|
||||
{
|
||||
data = (struct per_thread *) calloc (1, sizeof (struct per_thread));
|
||||
if (data == NULL)
|
||||
/* Should not happen. */
|
||||
abort ();
|
||||
glwthread_tls_set (tls_key, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
#else
|
||||
/* <https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html> */
|
||||
# define thread_local __thread
|
||||
|
||||
/* Assume a single thread. */
|
||||
static struct per_thread per_thread_singleton;
|
||||
|
||||
# define get_per_thread() (&per_thread_singleton)
|
||||
|
||||
#endif
|
||||
|
||||
/* Set of currently blocked signals. */
|
||||
static thread_local sigset_t blocked_set /* = 0 */;
|
||||
|
||||
/* Set of currently blocked and pending signals. */
|
||||
static thread_local volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
|
||||
|
||||
int
|
||||
sigpending (sigset_t *set)
|
||||
{
|
||||
const struct per_thread *pt = get_per_thread ();
|
||||
sigset_t pending = 0;
|
||||
for (int sig = 0; sig < NSIG; sig++)
|
||||
if (pending_array[sig])
|
||||
if (pt->pending_array[sig])
|
||||
pending |= 1U << sig;
|
||||
|
||||
*set = pending;
|
||||
@@ -246,10 +289,11 @@ override_handler (int sig)
|
||||
when we reinstall it will trigger the default handler; oh well. */
|
||||
signal (sig, override_handler);
|
||||
|
||||
if ((blocked_set >> sig) & 1)
|
||||
struct per_thread *pt = get_per_thread ();
|
||||
if ((pt->blocked_set >> sig) & 1)
|
||||
{
|
||||
/* The signal is blocked in the current thread. */
|
||||
pending_array[sig] = 1;
|
||||
pt->pending_array[sig] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -278,8 +322,10 @@ override_handler (int sig)
|
||||
int
|
||||
pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
|
||||
{
|
||||
struct per_thread *pt = get_per_thread ();
|
||||
|
||||
if (old_set != NULL)
|
||||
*old_set = blocked_set;
|
||||
*old_set = pt->blocked_set;
|
||||
|
||||
if (set != NULL)
|
||||
{
|
||||
@@ -287,27 +333,27 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
|
||||
switch (operation)
|
||||
{
|
||||
case SIG_BLOCK:
|
||||
new_blocked_set = blocked_set | *set;
|
||||
new_blocked_set = pt->blocked_set | *set;
|
||||
break;
|
||||
case SIG_SETMASK:
|
||||
new_blocked_set = *set;
|
||||
break;
|
||||
case SIG_UNBLOCK:
|
||||
new_blocked_set = blocked_set & ~*set;
|
||||
new_blocked_set = pt->blocked_set & ~*set;
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
sigset_t to_unblock = blocked_set & ~new_blocked_set;
|
||||
sigset_t to_block = new_blocked_set & ~blocked_set;
|
||||
sigset_t to_unblock = pt->blocked_set & ~ new_blocked_set;
|
||||
sigset_t to_block = new_blocked_set & ~ pt->blocked_set;
|
||||
|
||||
if (to_block != 0)
|
||||
for (int sig = 0; sig < NSIG; sig++)
|
||||
if ((to_block >> sig) & 1)
|
||||
{
|
||||
/* All pending_array[sig] remain zero. */
|
||||
blocked_set |= 1U << sig;
|
||||
/* All pt->pending_array[sig] remain zero. */
|
||||
pt->blocked_set |= 1U << sig;
|
||||
if (!overrides[sig].overridden)
|
||||
{
|
||||
glwthread_spin_lock (&overrides_mt_lock);
|
||||
@@ -316,8 +362,8 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
|
||||
{
|
||||
/* Now it's OK to install the override_handler:
|
||||
- If it gets invoked in this thread, there is no race
|
||||
condition since blocked_set already has the sig bit
|
||||
set.
|
||||
condition since pt->blocked_set already has the sig
|
||||
bit set.
|
||||
- If it gets invoked in another thread, the
|
||||
overrides_handler_lock protects it from proceeding
|
||||
until we have stored the old_handler. For this case,
|
||||
@@ -348,9 +394,9 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
|
||||
for (int sig = 0; sig < NSIG; sig++)
|
||||
if ((to_unblock >> sig) & 1)
|
||||
{
|
||||
received[sig] = pending_array[sig];
|
||||
blocked_set &= ~(1U << sig);
|
||||
pending_array[sig] = 0;
|
||||
received[sig] = pt->pending_array[sig];
|
||||
pt->blocked_set &= ~(1U << sig);
|
||||
pt->pending_array[sig] = 0;
|
||||
}
|
||||
else
|
||||
received[sig] = 0;
|
||||
@@ -437,9 +483,10 @@ _gl_raise_SIGPIPE (void)
|
||||
any thread of the current process. In the SIGPIPE emulation here, we do
|
||||
it slightly differently: we deliver it to the current thread always,
|
||||
like raise (SIGPIPE) would do. */
|
||||
if (blocked_set & (1U << SIGPIPE))
|
||||
struct per_thread *pt = get_per_thread ();
|
||||
if (pt->blocked_set & (1U << SIGPIPE))
|
||||
/* The signal is blocked in the current thread. */
|
||||
pending_array[SIGPIPE] = 1;
|
||||
pt->pending_array[SIGPIPE] = 1;
|
||||
else
|
||||
{
|
||||
handler_t handler = SIGPIPE_handler;
|
||||
|
||||
@@ -10,7 +10,9 @@ signal-h
|
||||
stdint-h [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
raise [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
msvc-inval [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
windows-once [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
windows-spin [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
windows-tls [test $HAVE_POSIX_SIGNALBLOCKING = 0]
|
||||
|
||||
configure.ac:
|
||||
gl_SIGNALBLOCKING
|
||||
|
||||
Reference in New Issue
Block a user