Blame gl/tests/windows-thread.c

Packit Service 4684c1
/* Creating and controlling threads (native Windows implementation).
Packit Service 4684c1
   Copyright (C) 2005-2020 Free Software Foundation, Inc.
Packit Service 4684c1
Packit Service 4684c1
   This program is free software; you can redistribute it and/or modify
Packit Service 4684c1
   it under the terms of the GNU General Public License as published by
Packit Service 4684c1
   the Free Software Foundation; either version 3, or (at your option)
Packit Service 4684c1
   any later version.
Packit Service 4684c1
Packit Service 4684c1
   This program is distributed in the hope that it will be useful,
Packit Service 4684c1
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 4684c1
   GNU General Public License for more details.
Packit Service 4684c1
Packit Service 4684c1
   You should have received a copy of the GNU General Public License
Packit Service 4684c1
   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit Service 4684c1
Packit Service 4684c1
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
Packit Service 4684c1
   Based on GCC's gthr-win32.h.  */
Packit Service 4684c1
Packit Service 4684c1
#include <config.h>
Packit Service 4684c1
Packit Service 4684c1
/* Specification.  */
Packit Service 4684c1
#include "windows-thread.h"
Packit Service 4684c1
Packit Service 4684c1
#include <errno.h>
Packit Service 4684c1
#include <process.h>
Packit Service 4684c1
#include <stdlib.h>
Packit Service 4684c1
Packit Service 4684c1
#include "windows-once.h"
Packit Service 4684c1
#include "windows-tls.h"
Packit Service 4684c1
Packit Service 4684c1
/* The Thread-Local Storage (TLS) key that allows to access each thread's
Packit Service 4684c1
   'struct glwthread_thread_struct *' pointer.  */
Packit Service 4684c1
static DWORD self_key = (DWORD)-1;
Packit Service 4684c1
Packit Service 4684c1
/* Initializes self_key.  This function must only be called once.  */
Packit Service 4684c1
static void
Packit Service 4684c1
do_init_self_key (void)
Packit Service 4684c1
{
Packit Service 4684c1
  self_key = TlsAlloc ();
Packit Service 4684c1
  /* If this fails, we're hosed.  */
Packit Service 4684c1
  if (self_key == (DWORD)-1)
Packit Service 4684c1
    abort ();
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Initializes self_key.  */
Packit Service 4684c1
static void
Packit Service 4684c1
init_self_key (void)
Packit Service 4684c1
{
Packit Service 4684c1
  static glwthread_once_t once = GLWTHREAD_ONCE_INIT;
Packit Service 4684c1
  glwthread_once (&once, do_init_self_key);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* This structure contains information about a thread.
Packit Service 4684c1
   It is stored in TLS under key self_key.  */
Packit Service 4684c1
struct glwthread_thread_struct
Packit Service 4684c1
{
Packit Service 4684c1
  /* Fields for managing the handle.  */
Packit Service 4684c1
  HANDLE volatile handle;
Packit Service 4684c1
  CRITICAL_SECTION handle_lock;
Packit Service 4684c1
  /* Fields for managing the exit value.  */
Packit Service 4684c1
  BOOL volatile detached;
Packit Service 4684c1
  void * volatile result;
Packit Service 4684c1
  /* Fields for managing the thread start.  */
Packit Service 4684c1
  void * (*func) (void *);
Packit Service 4684c1
  void *arg;
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
/* Return a real HANDLE object for the current thread.  */
Packit Service 4684c1
static HANDLE
Packit Service 4684c1
get_current_thread_handle (void)
Packit Service 4684c1
{
Packit Service 4684c1
  HANDLE this_handle;
Packit Service 4684c1
Packit Service 4684c1
  /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
Packit Service 4684c1
     identifier, not a real handle.  */
Packit Service 4684c1
  if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
Packit Service 4684c1
                        GetCurrentProcess (), &this_handle,
Packit Service 4684c1
                        0, FALSE, DUPLICATE_SAME_ACCESS))
Packit Service 4684c1
    abort ();
Packit Service 4684c1
  return this_handle;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
glwthread_thread_t
Packit Service 4684c1
glwthread_thread_self (void)
Packit Service 4684c1
{
Packit Service 4684c1
  glwthread_thread_t thread;
Packit Service 4684c1
Packit Service 4684c1
  if (self_key == (DWORD)-1)
Packit Service 4684c1
    init_self_key ();
Packit Service 4684c1
  thread = TlsGetValue (self_key);
Packit Service 4684c1
  if (thread == NULL)
Packit Service 4684c1
    {
Packit Service 4684c1
      /* This happens only in threads that have not been created through
Packit Service 4684c1
         glthread_create(), such as the main thread.  */
Packit Service 4684c1
      for (;;)
Packit Service 4684c1
        {
Packit Service 4684c1
          thread =
Packit Service 4684c1
            (struct glwthread_thread_struct *)
Packit Service 4684c1
            malloc (sizeof (struct glwthread_thread_struct));
Packit Service 4684c1
          if (thread != NULL)
Packit Service 4684c1
            break;
Packit Service 4684c1
          /* Memory allocation failed.  There is not much we can do.  Have to
Packit Service 4684c1
             busy-loop, waiting for the availability of memory.  */
Packit Service 4684c1
          Sleep (1);
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      thread->handle = get_current_thread_handle ();
Packit Service 4684c1
      InitializeCriticalSection (&thread->handle_lock);
Packit Service 4684c1
      thread->detached = FALSE; /* This can lead to a memory leak.  */
Packit Service 4684c1
      thread->result = NULL; /* just to be deterministic */
Packit Service 4684c1
      TlsSetValue (self_key, thread);
Packit Service 4684c1
    }
Packit Service 4684c1
  return thread;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* The main function of a freshly creating thread.  It's a wrapper around
Packit Service 4684c1
   the FUNC and ARG arguments passed to glthread_create_func.  */
Packit Service 4684c1
static unsigned int WINAPI
Packit Service 4684c1
wrapper_func (void *varg)
Packit Service 4684c1
{
Packit Service 4684c1
  struct glwthread_thread_struct *thread =
Packit Service 4684c1
    (struct glwthread_thread_struct *) varg;
Packit Service 4684c1
Packit Service 4684c1
  EnterCriticalSection (&thread->handle_lock);
Packit Service 4684c1
  /* Create a new handle for the thread only if the parent thread did not yet
Packit Service 4684c1
     fill in the handle.  */
Packit Service 4684c1
  if (thread->handle == NULL)
Packit Service 4684c1
    thread->handle = get_current_thread_handle ();
Packit Service 4684c1
  LeaveCriticalSection (&thread->handle_lock);
Packit Service 4684c1
Packit Service 4684c1
  if (self_key == (DWORD)-1)
Packit Service 4684c1
    init_self_key ();
Packit Service 4684c1
  TlsSetValue (self_key, thread);
Packit Service 4684c1
Packit Service 4684c1
  /* Run the thread.  Store the exit value if the thread was not terminated
Packit Service 4684c1
     otherwise.  */
Packit Service 4684c1
  thread->result = thread->func (thread->arg);
Packit Service 4684c1
Packit Service 4684c1
  /* Process the TLS destructors.  */
Packit Service 4684c1
  glwthread_tls_process_destructors ();
Packit Service 4684c1
Packit Service 4684c1
  if (thread->detached)
Packit Service 4684c1
    {
Packit Service 4684c1
      /* Clean up the thread, like thrd_join would do.  */
Packit Service 4684c1
      DeleteCriticalSection (&thread->handle_lock);
Packit Service 4684c1
      CloseHandle (thread->handle);
Packit Service 4684c1
      free (thread);
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
glwthread_thread_create (glwthread_thread_t *threadp, unsigned int attr,
Packit Service 4684c1
                         void * (*func) (void *), void *arg)
Packit Service 4684c1
{
Packit Service 4684c1
  struct glwthread_thread_struct *thread =
Packit Service 4684c1
    (struct glwthread_thread_struct *)
Packit Service 4684c1
    malloc (sizeof (struct glwthread_thread_struct));
Packit Service 4684c1
  if (thread == NULL)
Packit Service 4684c1
    return ENOMEM;
Packit Service 4684c1
  thread->handle = NULL;
Packit Service 4684c1
  InitializeCriticalSection (&thread->handle_lock);
Packit Service 4684c1
  thread->detached = (attr & GLWTHREAD_ATTR_DETACHED ? TRUE : FALSE);
Packit Service 4684c1
  thread->result = NULL; /* just to be deterministic */
Packit Service 4684c1
  thread->func = func;
Packit Service 4684c1
  thread->arg = arg;
Packit Service 4684c1
Packit Service 4684c1
  {
Packit Service 4684c1
    unsigned int thread_id;
Packit Service 4684c1
    HANDLE thread_handle;
Packit Service 4684c1
Packit Service 4684c1
    thread_handle = (HANDLE)
Packit Service 4684c1
      _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
Packit Service 4684c1
      /* calls CreateThread with the same arguments */
Packit Service 4684c1
    if (thread_handle == NULL)
Packit Service 4684c1
      {
Packit Service 4684c1
        DeleteCriticalSection (&thread->handle_lock);
Packit Service 4684c1
        free (thread);
Packit Service 4684c1
        return EAGAIN;
Packit Service 4684c1
      }
Packit Service 4684c1
Packit Service 4684c1
    EnterCriticalSection (&thread->handle_lock);
Packit Service 4684c1
    if (thread->handle == NULL)
Packit Service 4684c1
      thread->handle = thread_handle;
Packit Service 4684c1
    else
Packit Service 4684c1
      /* thread->handle was already set by the thread itself.  */
Packit Service 4684c1
      CloseHandle (thread_handle);
Packit Service 4684c1
    LeaveCriticalSection (&thread->handle_lock);
Packit Service 4684c1
Packit Service 4684c1
    *threadp = thread;
Packit Service 4684c1
    return 0;
Packit Service 4684c1
  }
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
glwthread_thread_join (glwthread_thread_t thread, void **retvalp)
Packit Service 4684c1
{
Packit Service 4684c1
  if (thread == NULL)
Packit Service 4684c1
    return EINVAL;
Packit Service 4684c1
Packit Service 4684c1
  if (thread == glwthread_thread_self ())
Packit Service 4684c1
    return EDEADLK;
Packit Service 4684c1
Packit Service 4684c1
  if (thread->detached)
Packit Service 4684c1
    return EINVAL;
Packit Service 4684c1
Packit Service 4684c1
  if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
Packit Service 4684c1
    return EINVAL;
Packit Service 4684c1
Packit Service 4684c1
  if (retvalp != NULL)
Packit Service 4684c1
    *retvalp = thread->result;
Packit Service 4684c1
Packit Service 4684c1
  DeleteCriticalSection (&thread->handle_lock);
Packit Service 4684c1
  CloseHandle (thread->handle);
Packit Service 4684c1
  free (thread);
Packit Service 4684c1
Packit Service 4684c1
  return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
glwthread_thread_detach (glwthread_thread_t thread)
Packit Service 4684c1
{
Packit Service 4684c1
  if (thread == NULL)
Packit Service 4684c1
    return EINVAL;
Packit Service 4684c1
Packit Service 4684c1
  if (thread->detached)
Packit Service 4684c1
    return EINVAL;
Packit Service 4684c1
Packit Service 4684c1
  thread->detached = TRUE;
Packit Service 4684c1
  return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
glwthread_thread_exit (void *retval)
Packit Service 4684c1
{
Packit Service 4684c1
  glwthread_thread_t thread = glwthread_thread_self ();
Packit Service 4684c1
  thread->result = retval;
Packit Service 4684c1
  glwthread_tls_process_destructors ();
Packit Service 4684c1
  _endthreadex (0); /* calls ExitThread (0) */
Packit Service 4684c1
  abort ();
Packit Service 4684c1
}