root/maint/gnulib/lib/glthread/thread.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. do_init_thrd_with_exitvalue_key
  2. init_thrd_with_exitvalue_key
  3. thrd_main_func
  4. glthread_create
  5. gl_thread_self
  6. glthread_join
  7. gl_thread_exit
  8. gl_thread_create

   1 /* Creating and controlling threads.
   2    Copyright (C) 2005-2021 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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */