(root)/
grep-3.11/
gnulib-tests/
glthread/
thread.c
       1  /* Creating and controlling threads.
       2     Copyright (C) 2005-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>, 2005.
      18     Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include "glthread/thread.h"
      24  
      25  #include <stdlib.h>
      26  #include "glthread/lock.h"
      27  
      28  /* ========================================================================= */
      29  
      30  #if USE_ISOC_THREADS
      31  
      32  struct thrd_with_exitvalue
      33  {
      34    thrd_t volatile tid;
      35    void * volatile exitvalue;
      36  };
      37  
      38  /* The Thread-Specific Storage (TSS) key that allows to access each thread's
      39     'struct thrd_with_exitvalue *' pointer.  */
      40  static tss_t thrd_with_exitvalue_key;
      41  
      42  /* Initializes thrd_with_exitvalue_key.
      43     This function must only be called once.  */
      44  static void
      45  do_init_thrd_with_exitvalue_key (void)
      46  {
      47    if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
      48      abort ();
      49  }
      50  
      51  /* Initializes thrd_with_exitvalue_key.  */
      52  static void
      53  init_thrd_with_exitvalue_key (void)
      54  {
      55    static once_flag once = ONCE_FLAG_INIT;
      56    call_once (&once, do_init_thrd_with_exitvalue_key);
      57  }
      58  
      59  typedef union
      60          {
      61            struct thrd_with_exitvalue t;
      62            struct
      63            {
      64              thrd_t tid; /* reserve memory for t.tid */
      65              void *(*mainfunc) (void *);
      66              void *arg;
      67            } a;
      68          }
      69          main_arg_t;
      70  
      71  static int
      72  thrd_main_func (void *pmarg)
      73  {
      74    /* Unpack the object that combines mainfunc and arg.  */
      75    main_arg_t *main_arg = (main_arg_t *) pmarg;
      76    void *(*mainfunc) (void *) = main_arg->a.mainfunc;
      77    void *arg = main_arg->a.arg;
      78  
      79    if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
      80      abort ();
      81  
      82    /* Execute mainfunc, with arg as argument.  */
      83    {
      84      void *exitvalue = mainfunc (arg);
      85      /* Store the exitvalue, for use by glthread_join().  */
      86      main_arg->t.exitvalue = exitvalue;
      87      return 0;
      88    }
      89  }
      90  
      91  int
      92  glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
      93  {
      94    init_thrd_with_exitvalue_key ();
      95    {
      96      /* Combine mainfunc and arg in a single object.
      97         A stack-allocated object does not work, because it would be out of
      98         existence when thrd_create returns before thrd_main_func is
      99         entered.  So, allocate it in the heap.  */
     100      main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
     101      if (main_arg == NULL)
     102        return ENOMEM;
     103      main_arg->a.mainfunc = mainfunc;
     104      main_arg->a.arg = arg;
     105      switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
     106        {
     107        case thrd_success:
     108          break;
     109        case thrd_nomem:
     110          free (main_arg);
     111          return ENOMEM;
     112        default:
     113          free (main_arg);
     114          return EAGAIN;
     115        }
     116      *threadp = &main_arg->t;
     117      return 0;
     118    }
     119  }
     120  
     121  gl_thread_t
     122  gl_thread_self (void)
     123  {
     124    init_thrd_with_exitvalue_key ();
     125    {
     126      gl_thread_t thread =
     127        (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_key);
     128      if (thread == NULL)
     129        {
     130          /* This happens only in threads that have not been created through
     131             glthread_create(), such as the main thread.  */
     132          for (;;)
     133            {
     134              thread =
     135                (struct thrd_with_exitvalue *)
     136                malloc (sizeof (struct thrd_with_exitvalue));
     137              if (thread != NULL)
     138                break;
     139              /* Memory allocation failed.  There is not much we can do.  Have to
     140                 busy-loop, waiting for the availability of memory.  */
     141              {
     142                struct timespec ts;
     143                ts.tv_sec = 1;
     144                ts.tv_nsec = 0;
     145                thrd_sleep (&ts, NULL);
     146              }
     147            }
     148          thread->tid = thrd_current ();
     149          thread->exitvalue = NULL; /* just to be deterministic */
     150          if (tss_set (thrd_with_exitvalue_key, thread) != thrd_success)
     151            abort ();
     152        }
     153      return thread;
     154    }
     155  }
     156  
     157  int
     158  glthread_join (gl_thread_t thread, void **return_value_ptr)
     159  {
     160    /* On Solaris 11.4, thrd_join crashes when the second argument we pass is
     161       NULL.  */
     162    int dummy;
     163  
     164    if (thread == gl_thread_self ())
     165      return EINVAL;
     166    if (thrd_join (thread->tid, &dummy) != thrd_success)
     167      return EINVAL;
     168    if (return_value_ptr != NULL)
     169      *return_value_ptr = thread->exitvalue;
     170    free (thread);
     171    return 0;
     172  }
     173  
     174  _Noreturn void
     175  gl_thread_exit (void *return_value)
     176  {
     177    gl_thread_t thread = gl_thread_self ();
     178    thread->exitvalue = return_value;
     179    thrd_exit (0);
     180  }
     181  
     182  #endif
     183  
     184  /* ========================================================================= */
     185  
     186  #if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
     187  
     188  #include <pthread.h>
     189  
     190  #if defined PTW32_VERSION || defined __MVS__
     191  
     192  const gl_thread_t gl_null_thread /* = { .p = NULL } */;
     193  
     194  #endif
     195  
     196  #endif
     197  
     198  /* ========================================================================= */
     199  
     200  #if USE_WINDOWS_THREADS
     201  
     202  #endif
     203  
     204  /* ========================================================================= */
     205  
     206  gl_thread_t
     207  gl_thread_create (void *(*func) (void *arg), void *arg)
     208  {
     209    gl_thread_t thread;
     210    int ret;
     211  
     212    ret = glthread_create (&thread, func, arg);
     213    if (ret != 0)
     214      abort ();
     215    return thread;
     216  }