(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
asyncsafe-spin.c
       1  /* Spin locks for communication between threads and signal handlers.
       2     Copyright (C) 2020-2023 Free Software Foundation, Inc.
       3  
       4     This file is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "asyncsafe-spin.h"
      23  
      24  #include <stdlib.h>
      25  #if defined _AIX
      26  # include <sys/atomic_op.h>
      27  #endif
      28  #if 0x590 <= __SUNPRO_C && __STDC__
      29  # define asm __asm
      30  #endif
      31  
      32  #if defined _WIN32 && ! defined __CYGWIN__
      33  /* Use Windows threads.  */
      34  
      35  void
      36  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
      37  {
      38    glwthread_spin_init (lock);
      39  }
      40  
      41  static inline void
      42  do_lock (asyncsafe_spinlock_t *lock)
      43  {
      44    glwthread_spin_lock (lock);
      45  }
      46  
      47  static inline void
      48  do_unlock (asyncsafe_spinlock_t *lock)
      49  {
      50    if (glwthread_spin_unlock (lock))
      51      abort ();
      52  }
      53  
      54  void
      55  asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
      56  {
      57    glwthread_spin_destroy (lock);
      58  }
      59  
      60  #else
      61  
      62  # if HAVE_PTHREAD_H
      63  /* Use POSIX threads.  */
      64  
      65  /* We don't use semaphores (although sem_post() is allowed in signal handlers),
      66     because it would require to link with -lrt on HP-UX 11, OSF/1, Solaris 10,
      67     and also because on macOS only named semaphores work.
      68  
      69     We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
      70     require to link with -latomic.  */
      71  
      72  #  if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) \
      73         || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 1)) \
      74        && !defined __ibmxl__
      75  /* Use GCC built-ins (available in GCC >= 4.7 and clang >= 3.1) that operate on
      76     the first byte of the lock.
      77     Documentation:
      78     <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html>
      79   */
      80  
      81  #   if 1
      82  /* An implementation that verifies the unlocks.  */
      83  
      84  void
      85  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
      86  {
      87    __atomic_store_n (lock, 0, __ATOMIC_SEQ_CST);
      88  }
      89  
      90  static inline void
      91  do_lock (asyncsafe_spinlock_t *lock)
      92  {
      93    /* Wait until *lock becomes 0, then replace it with 1.  */
      94    asyncsafe_spinlock_t zero;
      95    while (!(zero = 0,
      96             __atomic_compare_exchange_n (lock, &zero, 1, false,
      97                                          __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
      98      ;
      99  }
     100  
     101  static inline void
     102  do_unlock (asyncsafe_spinlock_t *lock)
     103  {
     104    /* If *lock is 1, then replace it with 0.  */
     105    asyncsafe_spinlock_t one = 1;
     106    if (!__atomic_compare_exchange_n (lock, &one, 0, false,
     107                                      __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
     108      abort ();
     109  }
     110  
     111  #   else
     112  /* An implementation that is a little bit more optimized, but does not verify
     113     the unlocks.  */
     114  
     115  void
     116  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     117  {
     118    __atomic_clear (lock, __ATOMIC_SEQ_CST);
     119  }
     120  
     121  static inline void
     122  do_lock (asyncsafe_spinlock_t *lock)
     123  {
     124    while (__atomic_test_and_set (lock, __ATOMIC_SEQ_CST))
     125      ;
     126  }
     127  
     128  static inline void
     129  do_unlock (asyncsafe_spinlock_t *lock)
     130  {
     131    __atomic_clear (lock, __ATOMIC_SEQ_CST);
     132  }
     133  
     134  #   endif
     135  
     136  #  elif (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \
     137            && !(defined __sun && defined __sparc__) && !defined __ANDROID__) \
     138           || __clang_major__ >= 3) \
     139          && !defined __ibmxl__
     140  /* Use GCC built-ins (available in GCC >= 4.1, except on Solaris/SPARC and
     141     Android, and clang >= 3.0).
     142     Documentation:
     143     <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html>  */
     144  
     145  void
     146  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     147  {
     148    volatile unsigned int *vp = lock;
     149    *vp = 0;
     150    __sync_synchronize ();
     151  }
     152  
     153  static inline void
     154  do_lock (asyncsafe_spinlock_t *lock)
     155  {
     156    /* Wait until *lock becomes 0, then replace it with 1.  */
     157    while (__sync_val_compare_and_swap (lock, 0, 1) != 0)
     158      ;
     159  }
     160  
     161  static inline void
     162  do_unlock (asyncsafe_spinlock_t *lock)
     163  {
     164    /* If *lock is 1, then replace it with 0.  */
     165    if (__sync_val_compare_and_swap (lock, 1, 0) != 1)
     166      abort ();
     167  }
     168  
     169  #  elif defined _AIX
     170  /* AIX */
     171  
     172  void
     173  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     174  {
     175    atomic_p vp = (int *) lock;
     176    _clear_lock (vp, 0);
     177  }
     178  
     179  static inline void
     180  do_lock (asyncsafe_spinlock_t *lock)
     181  {
     182    atomic_p vp = (int *) lock;
     183    while (_check_lock (vp, 0, 1))
     184      ;
     185  }
     186  
     187  static inline void
     188  do_unlock (asyncsafe_spinlock_t *lock)
     189  {
     190    atomic_p vp = (int *) lock;
     191    if (_check_lock (vp, 1, 0))
     192      abort ();
     193  }
     194  
     195  #  elif ((defined __GNUC__ || defined __clang__ || defined __SUNPRO_C) && (defined __sparc || defined __i386 || defined __x86_64__)) || (defined __TINYC__ && (defined __i386 || defined __x86_64__))
     196  /* For older versions of GCC or clang, use inline assembly.
     197     GCC, clang, and the Oracle Studio C 12 compiler understand GCC's extended
     198     asm syntax, but the plain Oracle Studio C 11 compiler understands only
     199     simple asm.  */
     200  /* An implementation that verifies the unlocks.  */
     201  
     202  static void
     203  memory_barrier (void)
     204  {
     205  #   if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
     206  #    if defined __i386 || defined __x86_64__
     207  #     if defined __TINYC__ && defined __i386
     208    /* Cannot use the SSE instruction "mfence" with this compiler.  */
     209    asm volatile ("lock orl $0,(%esp)");
     210  #     else
     211    asm volatile ("mfence");
     212  #     endif
     213  #    endif
     214  #    if defined __sparc
     215    asm volatile ("membar 2");
     216  #    endif
     217  #   else
     218  #    if defined __i386 || defined __x86_64__
     219    asm ("mfence");
     220  #    endif
     221  #    if defined __sparc
     222    asm ("membar 2");
     223  #    endif
     224  #   endif
     225  }
     226  
     227  /* Store NEWVAL in *VP if the old value *VP is == CMP.
     228     Return the old value.  */
     229  static unsigned int
     230  atomic_compare_and_swap (volatile unsigned int *vp, unsigned int cmp,
     231                           unsigned int newval)
     232  {
     233  #   if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
     234    unsigned int oldval;
     235  #    if defined __i386 || defined __x86_64__
     236    asm volatile (" lock\n cmpxchgl %3,(%1)"
     237                  : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
     238  #    endif
     239  #    if defined __sparc
     240    asm volatile (" cas [%1],%2,%3\n"
     241                  " mov %3,%0"
     242                  : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
     243  #    endif
     244    return oldval;
     245  #   else /* __SUNPRO_C */
     246  #    if defined __x86_64__
     247    asm (" movl %esi,%eax\n"
     248         " lock\n cmpxchgl %edx,(%rdi)");
     249  #    elif defined __i386
     250    asm (" movl 16(%ebp),%ecx\n"
     251         " movl 12(%ebp),%eax\n"
     252         " movl 8(%ebp),%edx\n"
     253         " lock\n cmpxchgl %ecx,(%edx)");
     254  #    endif
     255  #    if defined __sparc
     256    asm (" cas [%i0],%i1,%i2\n"
     257         " mov %i2,%i0");
     258  #    endif
     259  #   endif
     260  }
     261  
     262  void
     263  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     264  {
     265    volatile unsigned int *vp = lock;
     266    *vp = 0;
     267    memory_barrier ();
     268  }
     269  
     270  static inline void
     271  do_lock (asyncsafe_spinlock_t *lock)
     272  {
     273    volatile unsigned int *vp = lock;
     274    while (atomic_compare_and_swap (vp, 0, 1) != 0)
     275      ;
     276  }
     277  
     278  static inline void
     279  do_unlock (asyncsafe_spinlock_t *lock)
     280  {
     281    volatile unsigned int *vp = lock;
     282    if (atomic_compare_and_swap (vp, 1, 0) != 1)
     283      abort ();
     284  }
     285  
     286  #  else
     287  /* Fallback code.  It has some race conditions.  */
     288  
     289  void
     290  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     291  {
     292    volatile unsigned int *vp = lock;
     293    *vp = 0;
     294  }
     295  
     296  static inline void
     297  do_lock (asyncsafe_spinlock_t *lock)
     298  {
     299    volatile unsigned int *vp = lock;
     300    while (*vp)
     301      ;
     302    *vp = 1;
     303  }
     304  
     305  static inline void
     306  do_unlock (asyncsafe_spinlock_t *lock)
     307  {
     308    volatile unsigned int *vp = lock;
     309    *vp = 0;
     310  }
     311  
     312  #  endif
     313  
     314  # else
     315  /* Provide a dummy implementation for single-threaded applications.  */
     316  
     317  void
     318  asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
     319  {
     320  }
     321  
     322  static inline void
     323  do_lock (asyncsafe_spinlock_t *lock)
     324  {
     325  }
     326  
     327  static inline void
     328  do_unlock (asyncsafe_spinlock_t *lock)
     329  {
     330  }
     331  
     332  # endif
     333  
     334  void
     335  asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
     336  {
     337  }
     338  
     339  #endif
     340  
     341  void
     342  asyncsafe_spin_lock (asyncsafe_spinlock_t *lock,
     343                       const sigset_t *mask, sigset_t *saved_mask)
     344  {
     345    sigprocmask (SIG_BLOCK, mask, saved_mask); /* equivalent to pthread_sigmask */
     346    do_lock (lock);
     347  }
     348  
     349  void
     350  asyncsafe_spin_unlock (asyncsafe_spinlock_t *lock, const sigset_t *saved_mask)
     351  {
     352    do_unlock (lock);
     353    sigprocmask (SIG_SETMASK, saved_mask, NULL); /* equivalent to pthread_sigmask */
     354  }