1  /* Implementation of threads compatibility routines for libgcc2.  */
       2  
       3  /* Copyright (C) 1999-2023 Free Software Foundation, Inc.
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it under
       8  the terms of the GNU General Public License as published by the Free
       9  Software Foundation; either version 3, or (at your option) any later
      10  version.
      11  
      12  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15  for more details.
      16  
      17  Under Section 7 of GPL version 3, you are granted additional
      18  permissions described in the GCC Runtime Library Exception, version
      19  3.1, as published by the Free Software Foundation.
      20  
      21  You should have received a copy of the GNU General Public License and
      22  a copy of the GCC Runtime Library Exception along with this program;
      23  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24  <http://www.gnu.org/licenses/>.  */
      25  
      26  /* This module is separate from the rest of the implementation because only
      27     one copy of it ought to be linked.  */
      28  
      29  /* The implementation strategy for the c++0x thread support is as follows.
      30  
      31     A GNU thread is represented by a Win32 HANDLE that is obtained when the
      32     Win32 thread is created, except of course for the initial thread.  This
      33     Win32 HANDLE is stored in a descriptor keyed from TLS memory for every
      34     thread, so the self routine can return it instead of having to duplicate
      35     the pseudo-handle returned by GetCurrentThread each time it is invoked.
      36     For the initial thread, this Win32 HANDLE is created during the first
      37     call to the self routine using the aforementioned technique.
      38  
      39     Note that the equal routine compares the identifier of threads instead
      40     of their Win32 HANDLE, which will give the correct positive answer even
      41     in the case where distinct Win32 HANDLEs have been created for the same
      42     thread by multiple instances of libgcc included in the link.  */
      43  
      44  #include "gthr-win32.h"
      45  
      46  /* The thread descriptor keyed from TLS memory.  */
      47  struct __gthr_win32_thr_desc
      48  {
      49    void *(*func) (void*);
      50    void *args;
      51    HANDLE h;
      52  };
      53  
      54  /* The TLS key used by one instance of the library.  */
      55  static __gthread_key_t __gthr_win32_tls = TLS_OUT_OF_INDEXES;
      56  
      57  /* The initialization device for the TLS key.  */
      58  static __gthread_once_t __gthr_win32_tls_once = __GTHREAD_ONCE_INIT;
      59  
      60  /* Initialize the TLS key.  */
      61  
      62  static void
      63  __gthr_win32_tls_init (void)
      64  {
      65    if (__gthread_key_create (&__gthr_win32_tls, free))
      66      abort ();
      67  }
      68  
      69  /* Wrapper routine around thread functions.  */
      70  
      71  static DWORD
      72  __gthr_win32_thread_wrapper (void *args)
      73  {
      74    struct __gthr_win32_thr_desc *td = (struct __gthr_win32_thr_desc *) args;
      75  
      76    __gthread_setspecific (__gthr_win32_tls, td);
      77  
      78    DWORD exit_code = (DWORD) (ULONG_PTR) (*td->func) (td->args);
      79  
      80    ExitThread (exit_code);
      81    return exit_code;
      82  }
      83  
      84  /* Implement the __gthread_create routine.  */
      85  
      86  int
      87  __gthr_win32_create (__gthread_t *thr, void *(*func) (void*), void *args)
      88  {
      89    struct __gthr_win32_thr_desc *td;
      90  
      91    __gthread_once (&__gthr_win32_tls_once, __gthr_win32_tls_init);
      92  
      93    td = malloc (sizeof (struct __gthr_win32_thr_desc));
      94    td->func = func;
      95    td->args = args;
      96    td->h = CreateThread (NULL, 0,
      97  			(LPTHREAD_START_ROUTINE) __gthr_win32_thread_wrapper,
      98  			(LPVOID) td, CREATE_SUSPENDED, NULL);
      99    if (td->h)
     100      {
     101        ResumeThread (td->h);
     102        *thr = (__gthread_t) td->h;
     103        return 0;
     104      }
     105    else
     106      {
     107        free (td);
     108        return (int) GetLastError ();
     109      }
     110  }
     111  
     112  /* Implement the __gthread_join routine.  */
     113  
     114  int
     115  __gthr_win32_join (__gthread_t thr, void **value_ptr)
     116  {
     117    int status = 0;
     118  
     119    if (GetThreadId ((HANDLE) thr) == GetCurrentThreadId ())
     120      return 1;
     121  
     122    if (WaitForSingleObject ((HANDLE) thr, INFINITE) == WAIT_OBJECT_0)
     123      {
     124        if (value_ptr)
     125  	{
     126  	  DWORD exit_code;
     127  	  if (GetExitCodeThread ((HANDLE) thr, &exit_code))
     128  	    *value_ptr = (void *) (ULONG_PTR) exit_code;
     129  	  else
     130  	    status = (int) GetLastError ();
     131  	}
     132      }
     133    else
     134      status = (int) GetLastError ();
     135  
     136    CloseHandle ((HANDLE) thr);
     137    return status;
     138  }
     139  
     140  /* Implement the __gthread_self routine.  */
     141  
     142  __gthread_t
     143  __gthr_win32_self (void)
     144  {
     145    struct __gthr_win32_thr_desc *td;
     146  
     147    __gthread_once (&__gthr_win32_tls_once, __gthr_win32_tls_init);
     148  
     149    if (!(td = __gthread_getspecific (__gthr_win32_tls)))
     150      {
     151        HANDLE proc = GetCurrentProcess ();
     152        td = malloc (sizeof (struct __gthr_win32_thr_desc));
     153        td->func = NULL;
     154        td->args = NULL;
     155        if (!DuplicateHandle (proc, GetCurrentThread(), proc, &td->h, 0, FALSE,
     156  			    DUPLICATE_SAME_ACCESS))
     157  	abort ();
     158        __gthread_setspecific (__gthr_win32_tls, td);
     159      }
     160  
     161    return td->h;
     162  }