Branch
Hash :
9ea3eece
Author :
Date :
2025-05-16T16:37:03
asyncsafe-spin: Fix race condition on native Windows. * lib/asyncsafe-spin.h (asyncsafe_spin_lock, asyncsafe_spin_unlock): Add from_signal_handler parameter. * lib/asyncsafe-spin.c (asyncsafe_spin_lock, asyncsafe_spin_unlock): Likewise. * modules/asyncsafe-spin (Depends-on): Add bool. * tests/test-asyncsafe-spin1.c (main): Update. * tests/test-asyncsafe-spin2.c (lock_mutator_thread, lock_checker_thread): Update. * lib/clean-temp-simple.c (clean_temp_asyncsafe_close): Update. * lib/clean-temp.c (asyncsafe_fclose_variant): Update.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/* Spin locks for communication between threads and signal handlers.
Copyright (C) 2020-2025 Free Software Foundation, Inc.
This file 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.
This file 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 this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Bruno Haible <bruno@clisp.org>, 2020. */
#include <config.h>
/* Specification. */
#include "asyncsafe-spin.h"
#include <stdlib.h>
void
asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
{
glthread_spinlock_init (lock);
}
void
asyncsafe_spin_lock (asyncsafe_spinlock_t *lock,
bool from_signal_handler,
const sigset_t *mask, sigset_t *saved_mask)
{
/* On all platforms, when not running in a signal handler, we need to block
the signals until the corresponding asyncsafe_spin_unlock() invocation.
This is needed because if, during that period, a signal occurred and it
happened to run in the current thread and it were to wait on this spin
lock, it would hang.
On platforms other than native Windows, it is useful to do the same
thing also within a signal handler, since signals may remain enabled
while a signal handler runs. It is possible to do this because
sigprocmask() is safe to call from within a signal handler, see
POSIX section "Signal Actions"
<https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_04_03>.
(In other words, sigprocmask() is atomic, because it is implemented as a
system call.)
Whereas on native Windows, sigprocmask() is not atomic, because it
manipulates global variables. Therefore in this case, we are *not*
allowed to call it from within a signal handler. */
#if defined _WIN32 && !defined __CYGWIN__
if (!from_signal_handler)
#endif
sigprocmask (SIG_BLOCK, mask, saved_mask); /* equivalent to pthread_sigmask */
glthread_spinlock_lock (lock);
}
void
asyncsafe_spin_unlock (asyncsafe_spinlock_t *lock,
bool from_signal_handler,
const sigset_t *saved_mask)
{
if (glthread_spinlock_unlock (lock))
abort ();
#if defined _WIN32 && !defined __CYGWIN__
if (!from_signal_handler)
#endif
sigprocmask (SIG_SETMASK, saved_mask, NULL); /* equivalent to pthread_sigmask */
}
void
asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
{
glthread_spinlock_destroy (lock);
}