root/maint/gnulib/lib/thrd.c

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

DEFINITIONS

This source file includes following definitions.
  1. do_init_thrd_with_exitcode_key
  2. init_thrd_with_exitcode_key
  3. thrd_main_func
  4. rpl_thrd_create
  5. rpl_thrd_current
  6. rpl_thrd_equal
  7. rpl_thrd_detach
  8. rpl_thrd_join
  9. rpl_thrd_join
  10. pthread_main_func
  11. thrd_create
  12. thrd_current
  13. thrd_equal
  14. thrd_yield
  15. thrd_detach
  16. thrd_join
  17. thrd_exit
  18. thrd_create
  19. thrd_current
  20. thrd_equal
  21. thrd_yield
  22. thrd_detach
  23. thrd_join
  24. thrd_exit
  25. thrd_sleep

   1 /* Creating and controlling ISO C 11 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, 2019.
  18    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */
  19 
  20 #include <config.h>
  21 
  22 #include <threads.h>
  23 
  24 #include <stdlib.h>
  25 
  26 #if HAVE_THREADS_H
  27 /* Provide workarounds.  */
  28 
  29 # if BROKEN_THRD_START_T
  30 
  31 #  undef thrd_t
  32 
  33 /* AIX 7.1..7.2 defines thrd_start_t incorrectly, namely as
  34    'void * (*) (void *)' instead of 'int (*) (void *)'.
  35    As a consequence, its thrd_join function never stores an exit code.  */
  36 
  37 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
  38    'struct thrd_with_exitcode *' pointer.  */
  39 static tss_t thrd_with_exitcode_key;
  40 
  41 /* Initializes thrd_with_exitcode_key.
  42    This function must only be called once.  */
  43 static void
  44 do_init_thrd_with_exitcode_key (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46   if (tss_create (&thrd_with_exitcode_key, NULL) != thrd_success)
  47     abort ();
  48 }
  49 
  50 /* Initializes thrd_with_exitcode_key.  */
  51 static void
  52 init_thrd_with_exitcode_key (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  53 {
  54   static once_flag once = ONCE_FLAG_INIT;
  55   call_once (&once, do_init_thrd_with_exitcode_key);
  56 }
  57 
  58 typedef union
  59         {
  60           struct thrd_with_exitcode t;
  61           struct
  62           {
  63             thrd_t tid; /* reserve memory for t.tid */
  64             int detached; /* reserve memory for t.detached */
  65             thrd_start_t mainfunc;
  66             void *arg;
  67           } a;
  68         }
  69         main_arg_t;
  70 
  71 static void *
  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   thrd_start_t mainfunc = main_arg->a.mainfunc;
  77   void *arg = main_arg->a.arg;
  78 
  79   if (tss_set (thrd_with_exitcode_key, &main_arg->t) != thrd_success)
  80     abort ();
  81 
  82   /* Execute mainfunc, with arg as argument.  */
  83   {
  84     int exitcode = mainfunc (arg);
  85     /* Store the exitcode, for use by thrd_join().  */
  86     main_arg->t.exitcode = exitcode;
  87     if (main_arg->t.detached)
  88       {
  89         /* Clean up the thread, like thrd_join would do.  */
  90         free (&main_arg->t);
  91       }
  92     return NULL;
  93   }
  94 }
  95 
  96 int
  97 rpl_thrd_create (rpl_thrd_t *threadp, thrd_start_t mainfunc, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  98 #  undef thrd_create
  99 {
 100   init_thrd_with_exitcode_key ();
 101   {
 102     /* Combine mainfunc and arg in a single object.
 103        A stack-allocated object does not work, because it would be out of
 104        existence when thrd_create returns before pthread_main_func is
 105        entered.  So, allocate it in the heap.  */
 106     main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
 107     if (main_arg == NULL)
 108       return thrd_nomem;
 109     main_arg->a.mainfunc = mainfunc;
 110     main_arg->a.arg = arg;
 111     main_arg->t.detached = 0;
 112     {
 113       int err =
 114         thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg);
 115       if (err == thrd_success)
 116         *threadp = &main_arg->t;
 117       else
 118         free (main_arg);
 119       return err;
 120     }
 121   }
 122 }
 123 
 124 rpl_thrd_t
 125 rpl_thrd_current (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 #  undef thrd_current
 127 {
 128   init_thrd_with_exitcode_key ();
 129   {
 130     rpl_thrd_t thread =
 131       (struct thrd_with_exitcode *) tss_get (thrd_with_exitcode_key);
 132     if (thread == NULL)
 133       {
 134         /* This happens only in threads that have not been created through
 135            thrd_create(), such as the main thread.  */
 136         for (;;)
 137           {
 138             thread =
 139               (struct thrd_with_exitcode *)
 140               malloc (sizeof (struct thrd_with_exitcode));
 141             if (thread != NULL)
 142               break;
 143             /* Memory allocation failed.  There is not much we can do.  Have to
 144                busy-loop, waiting for the availability of memory.  */
 145             {
 146               struct timespec ts;
 147               ts.tv_sec = 1;
 148               ts.tv_nsec = 0;
 149               thrd_sleep (&ts, NULL);
 150             }
 151           }
 152         thread->tid = thrd_current ();
 153         thread->detached = 0; /* This can lead to a memory leak.  */
 154         thread->exitcode = 0; /* just to be deterministic */
 155         if (tss_set (thrd_with_exitcode_key, thread) != thrd_success)
 156           abort ();
 157       }
 158     return thread;
 159   }
 160 }
 161 
 162 int
 163 rpl_thrd_equal (rpl_thrd_t thread1, rpl_thrd_t thread2)
     /* [previous][next][first][last][top][bottom][index][help] */
 164 {
 165   return thread1 == thread2;
 166 }
 167 
 168 int
 169 rpl_thrd_detach (rpl_thrd_t thread)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 #  undef thrd_detach
 171 {
 172   if (thread->detached)
 173     return thrd_error;
 174   {
 175     int err =
 176       thrd_detach (thread == rpl_thrd_current ()
 177                    ? /* thread->tid may not be initialized at this point.  */
 178                      thrd_current ()
 179                    : thread->tid);
 180     if (err == thrd_success)
 181       thread->detached = 1;
 182     return err;
 183   }
 184 }
 185 
 186 int
 187 rpl_thrd_join (rpl_thrd_t thread, int *exitcodep)
     /* [previous][next][first][last][top][bottom][index][help] */
 188 #  undef thrd_join
 189 {
 190   if (thread == rpl_thrd_current () || thread->detached)
 191     return thrd_error;
 192   {
 193     int err = thrd_join (thread->tid, NULL);
 194     if (err == thrd_success)
 195       {
 196         if (exitcodep != NULL)
 197           *exitcodep = thread->exitcode;
 198         free (thread);
 199       }
 200     return err;
 201   }
 202 }
 203 
 204 # endif
 205 
 206 # if BROKEN_THRD_JOIN
 207 
 208 /* On Solaris 11.4, thrd_join crashes when the second argument is NULL.  */
 209 int
 210 rpl_thrd_join (thrd_t thread, int *exitcodep)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 #  undef thrd_join
 212 {
 213   int exitcode;
 214   int err = thrd_join (thread, &exitcode);
 215   if (err == 0 && exitcodep != NULL)
 216     *exitcodep = exitcode;
 217   return err;
 218 }
 219 
 220 # endif
 221 
 222 #else
 223 
 224 # include <errno.h>
 225 # include <stdint.h>
 226 
 227 # if defined _WIN32 && ! defined __CYGWIN__
 228 /* Use Windows threads.  */
 229 
 230 #  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 231 #  include <windows.h>
 232 
 233 # else
 234 /* Use POSIX threads.  */
 235 
 236 #  include <pthread.h>
 237 #  include <sched.h>
 238 
 239 # endif
 240 
 241 /* The main functions passed to thrd_create and
 242    pthread_create/glwthread_thread_create have different return types:
 243    'int' vs. 'void *'.  */
 244 
 245 struct pthread_main_arg_t
 246 {
 247   thrd_start_t mainfunc;
 248   void *arg;
 249 };
 250 
 251 static void *
 252 pthread_main_func (void *pmarg)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254   /* Unpack the object that combines mainfunc and arg.  */
 255   struct pthread_main_arg_t *pthread_main_arg =
 256     (struct pthread_main_arg_t *) pmarg;
 257   thrd_start_t mainfunc = pthread_main_arg->mainfunc;
 258   void *arg = pthread_main_arg->arg;
 259 
 260   /* Free it.  */
 261   free (pmarg);
 262 
 263   /* Execute mainfunc, with arg as argument.  */
 264   {
 265     int exitcode = mainfunc (arg);
 266     /* Note: When using Windows threads, this exit code is different from the
 267        argument passed to ExitThread(), because the latter should never be 259,
 268        see <https://docs.microsoft.com/de-de/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodethread>,
 269        whereas the exit code passed to thrd_exit() is not constrained.  */
 270     return (void *) (intptr_t) exitcode;
 271   }
 272 }
 273 
 274 # if defined _WIN32 && ! defined __CYGWIN__
 275 /* Use Windows threads.  */
 276 
 277 int
 278 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 279 {
 280   /* Combine mainfunc and arg in a single object.
 281      A stack-allocated object does not work, because it would be out of
 282      existence when thrd_create returns before pthread_main_func is
 283      entered.  So, allocate it in the heap.  */
 284   struct pthread_main_arg_t *pthread_main_arg =
 285     (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
 286   if (pthread_main_arg == NULL)
 287     return thrd_nomem;
 288   pthread_main_arg->mainfunc = mainfunc;
 289   pthread_main_arg->arg = arg;
 290 
 291   {
 292     int err = glwthread_thread_create (threadp, 0,
 293                                        pthread_main_func, pthread_main_arg);
 294     if (err != 0)
 295       free (pthread_main_arg);
 296     return (err == 0 ? thrd_success :
 297             err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
 298             thrd_error);
 299   }
 300 }
 301 
 302 thrd_t
 303 thrd_current (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305   return glwthread_thread_self ();
 306 }
 307 
 308 int
 309 thrd_equal (thrd_t thread1, thrd_t thread2)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 {
 311   return thread1 == thread2;
 312 }
 313 
 314 void
 315 thrd_yield (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 316 {
 317   Sleep (0);
 318 }
 319 
 320 int
 321 thrd_detach (thrd_t thread)
     /* [previous][next][first][last][top][bottom][index][help] */
 322 {
 323   int err = glwthread_thread_detach (thread);
 324   return (err == 0 ? thrd_success : thrd_error);
 325 }
 326 
 327 int
 328 thrd_join (thrd_t thread, int *exitcodep)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330   void *exitptr;
 331   int err = glwthread_thread_join (thread, &exitptr);
 332   if (err == 0)
 333     {
 334       if (exitcodep != NULL)
 335         *exitcodep = (int) (intptr_t) exitptr;
 336       return thrd_success;
 337     }
 338   else
 339     return thrd_error;
 340 }
 341 
 342 _Noreturn void
 343 thrd_exit (int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345   glwthread_thread_exit ((void *) (intptr_t) exitcode);
 346 }
 347 
 348 # else
 349 /* Use POSIX threads.  */
 350 
 351 int
 352 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354   /* Combine mainfunc and arg in a single object.
 355      A stack-allocated object does not work, because it would be out of
 356      existence when thrd_create returns before pthread_main_func is
 357      entered.  So, allocate it in the heap.  */
 358   struct pthread_main_arg_t *pthread_main_arg =
 359     (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
 360   if (pthread_main_arg == NULL)
 361     return thrd_nomem;
 362   pthread_main_arg->mainfunc = mainfunc;
 363   pthread_main_arg->arg = arg;
 364 
 365   {
 366     int err = pthread_create (threadp, NULL,
 367                               pthread_main_func, pthread_main_arg);
 368     if (err != 0)
 369       free (pthread_main_arg);
 370     return (err == 0 ? thrd_success :
 371             err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
 372             thrd_error);
 373   }
 374 }
 375 
 376 thrd_t
 377 thrd_current (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 378 {
 379   return pthread_self ();
 380 }
 381 
 382 int
 383 thrd_equal (thrd_t thread1, thrd_t thread2)
     /* [previous][next][first][last][top][bottom][index][help] */
 384 {
 385   return pthread_equal (thread1, thread2);
 386 }
 387 
 388 void
 389 thrd_yield (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391   sched_yield ();
 392 }
 393 
 394 int
 395 thrd_detach (thrd_t thread)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397   int err = pthread_detach (thread);
 398   return (err == 0 ? thrd_success : thrd_error);
 399 }
 400 
 401 int
 402 thrd_join (thrd_t thread, int *exitcodep)
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404   void *exitptr;
 405   int err = pthread_join (thread, &exitptr);
 406   if (err == 0)
 407     {
 408       if (exitcodep != NULL)
 409         *exitcodep = (int) (intptr_t) exitptr;
 410       return thrd_success;
 411     }
 412   else
 413     return thrd_error;
 414 }
 415 
 416 _Noreturn void
 417 thrd_exit (int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419   pthread_exit ((void *) (intptr_t) exitcode);
 420 }
 421 
 422 # endif
 423 
 424 int
 425 thrd_sleep (const struct timespec *duration, struct timespec *remaining)
     /* [previous][next][first][last][top][bottom][index][help] */
 426 {
 427   int ret = nanosleep (duration, remaining);
 428   return (ret == 0 ? 0 : errno == EINTR ? -1 : -2);
 429 }
 430 
 431 #endif

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