(root)/
findutils-4.9.0/
gnulib-tests/
windows-thread.c
       1  /* Creating and controlling threads (native Windows implementation).
       2     Copyright (C) 2005-2022 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-win32.h.  */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include "windows-thread.h"
      24  
      25  #include <errno.h>
      26  #include <process.h>
      27  #include <stdlib.h>
      28  
      29  #include "windows-once.h"
      30  #include "windows-tls.h"
      31  
      32  /* The Thread-Local Storage (TLS) key that allows to access each thread's
      33     'struct glwthread_thread_struct *' pointer.  */
      34  static DWORD self_key = (DWORD)-1;
      35  
      36  /* Initializes self_key.  This function must only be called once.  */
      37  static void
      38  do_init_self_key (void)
      39  {
      40    self_key = TlsAlloc ();
      41    /* If this fails, we're hosed.  */
      42    if (self_key == (DWORD)-1)
      43      abort ();
      44  }
      45  
      46  /* Initializes self_key.  */
      47  static void
      48  init_self_key (void)
      49  {
      50    static glwthread_once_t once = GLWTHREAD_ONCE_INIT;
      51    glwthread_once (&once, do_init_self_key);
      52  }
      53  
      54  /* This structure contains information about a thread.
      55     It is stored in TLS under key self_key.  */
      56  struct glwthread_thread_struct
      57  {
      58    /* Fields for managing the handle.  */
      59    HANDLE volatile handle;
      60    CRITICAL_SECTION handle_lock;
      61    /* Fields for managing the exit value.  */
      62    BOOL volatile detached;
      63    void * volatile result;
      64    /* Fields for managing the thread start.  */
      65    void * (*func) (void *);
      66    void *arg;
      67  };
      68  
      69  /* Return a real HANDLE object for the current thread.  */
      70  static HANDLE
      71  get_current_thread_handle (void)
      72  {
      73    HANDLE this_handle;
      74  
      75    /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
      76       identifier, not a real handle.  */
      77    if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
      78                          GetCurrentProcess (), &this_handle,
      79                          0, FALSE, DUPLICATE_SAME_ACCESS))
      80      abort ();
      81    return this_handle;
      82  }
      83  
      84  glwthread_thread_t
      85  glwthread_thread_self (void)
      86  {
      87    glwthread_thread_t thread;
      88  
      89    if (self_key == (DWORD)-1)
      90      init_self_key ();
      91    thread = TlsGetValue (self_key);
      92    if (thread == NULL)
      93      {
      94        /* This happens only in threads that have not been created through
      95           glthread_create(), such as the main thread.  */
      96        for (;;)
      97          {
      98            thread =
      99              (struct glwthread_thread_struct *)
     100              malloc (sizeof (struct glwthread_thread_struct));
     101            if (thread != NULL)
     102              break;
     103            /* Memory allocation failed.  There is not much we can do.  Have to
     104               busy-loop, waiting for the availability of memory.  */
     105            Sleep (1);
     106          }
     107  
     108        thread->handle = get_current_thread_handle ();
     109        InitializeCriticalSection (&thread->handle_lock);
     110        thread->detached = FALSE; /* This can lead to a memory leak.  */
     111        thread->result = NULL; /* just to be deterministic */
     112        TlsSetValue (self_key, thread);
     113      }
     114    return thread;
     115  }
     116  
     117  /* The main function of a freshly creating thread.  It's a wrapper around
     118     the FUNC and ARG arguments passed to glthread_create_func.  */
     119  static unsigned int WINAPI
     120  wrapper_func (void *varg)
     121  {
     122    struct glwthread_thread_struct *thread =
     123      (struct glwthread_thread_struct *) varg;
     124  
     125    EnterCriticalSection (&thread->handle_lock);
     126    /* Create a new handle for the thread only if the parent thread did not yet
     127       fill in the handle.  */
     128    if (thread->handle == NULL)
     129      thread->handle = get_current_thread_handle ();
     130    LeaveCriticalSection (&thread->handle_lock);
     131  
     132    if (self_key == (DWORD)-1)
     133      init_self_key ();
     134    TlsSetValue (self_key, thread);
     135  
     136    /* Run the thread.  Store the exit value if the thread was not terminated
     137       otherwise.  */
     138    thread->result = thread->func (thread->arg);
     139  
     140    /* Process the TLS destructors.  */
     141    glwthread_tls_process_destructors ();
     142  
     143    if (thread->detached)
     144      {
     145        /* Clean up the thread, like thrd_join would do.  */
     146        DeleteCriticalSection (&thread->handle_lock);
     147        CloseHandle (thread->handle);
     148        free (thread);
     149      }
     150  
     151    return 0;
     152  }
     153  
     154  int
     155  glwthread_thread_create (glwthread_thread_t *threadp, unsigned int attr,
     156                           void * (*func) (void *), void *arg)
     157  {
     158    struct glwthread_thread_struct *thread =
     159      (struct glwthread_thread_struct *)
     160      malloc (sizeof (struct glwthread_thread_struct));
     161    if (thread == NULL)
     162      return ENOMEM;
     163    thread->handle = NULL;
     164    InitializeCriticalSection (&thread->handle_lock);
     165    thread->detached = (attr & GLWTHREAD_ATTR_DETACHED ? TRUE : FALSE);
     166    thread->result = NULL; /* just to be deterministic */
     167    thread->func = func;
     168    thread->arg = arg;
     169  
     170    {
     171      unsigned int thread_id;
     172      HANDLE thread_handle;
     173  
     174      thread_handle = (HANDLE)
     175        _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
     176        /* calls CreateThread with the same arguments */
     177      if (thread_handle == NULL)
     178        {
     179          DeleteCriticalSection (&thread->handle_lock);
     180          free (thread);
     181          return EAGAIN;
     182        }
     183  
     184      EnterCriticalSection (&thread->handle_lock);
     185      if (thread->handle == NULL)
     186        thread->handle = thread_handle;
     187      else
     188        /* thread->handle was already set by the thread itself.  */
     189        CloseHandle (thread_handle);
     190      LeaveCriticalSection (&thread->handle_lock);
     191  
     192      *threadp = thread;
     193      return 0;
     194    }
     195  }
     196  
     197  int
     198  glwthread_thread_join (glwthread_thread_t thread, void **retvalp)
     199  {
     200    if (thread == NULL)
     201      return EINVAL;
     202  
     203    if (thread == glwthread_thread_self ())
     204      return EDEADLK;
     205  
     206    if (thread->detached)
     207      return EINVAL;
     208  
     209    if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
     210      return EINVAL;
     211  
     212    if (retvalp != NULL)
     213      *retvalp = thread->result;
     214  
     215    DeleteCriticalSection (&thread->handle_lock);
     216    CloseHandle (thread->handle);
     217    free (thread);
     218  
     219    return 0;
     220  }
     221  
     222  int
     223  glwthread_thread_detach (glwthread_thread_t thread)
     224  {
     225    if (thread == NULL)
     226      return EINVAL;
     227  
     228    if (thread->detached)
     229      return EINVAL;
     230  
     231    thread->detached = TRUE;
     232    return 0;
     233  }
     234  
     235  void
     236  glwthread_thread_exit (void *retval)
     237  {
     238    glwthread_thread_t thread = glwthread_thread_self ();
     239    thread->result = retval;
     240    glwthread_tls_process_destructors ();
     241    _endthreadex (0); /* calls ExitThread (0) */
     242    abort ();
     243  }