Blame gnulib/tests/glthread/thread.c

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