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