Function _beginthreadex() is implemented as wrapper around the CRT
_beginthread() function and _endthreadex() as wrapper around the WinAPI
ExitThread() function.
---
 mingw-w64-crt/Makefile.am           |  13 +-
 mingw-w64-crt/misc/_beginthreadex.c | 181 ++++++++++++++++++++++++++++
 mingw-w64-crt/misc/_endthreadex.c   |  20 +++
 3 files changed, 213 insertions(+), 1 deletion(-)
 create mode 100644 mingw-w64-crt/misc/_beginthreadex.c
 create mode 100644 mingw-w64-crt/misc/_endthreadex.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 33a5290409b4..e6a255d4995c 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -916,6 +916,8 @@ src_pre_msvcrt20=\
   misc/__p__winver.c \
   misc/__timezone.c \
   misc/__tzname.c \
+  misc/_beginthreadex.c \
+  misc/_endthreadex.c \
   stdio/_wfindfirst32.c \
   stdio/_wfindnext32.c \
   stdio/_wstat32.c \
@@ -1043,10 +1045,12 @@ src_pre_msvcr90=\
 src_pre_msvcr100=\
   misc/i386__XcptFilter.c \
   misc/i386__beginthread.c \
-  misc/i386__beginthreadex.c \
   misc/_invalid_parameter_noinfo_noreturn.c \
   misc/imaxdiv.c
 
+src_pre_msvcr100_post_msvcrt10=\
+  misc/i386__beginthreadex.c
+
 src_pre_msvcr110=\
   stdio/msvcr110pre_fstat32.c \
   stdio/msvcr110pre_fstat64i32.c \
@@ -1130,6 +1134,7 @@ src_msvcrt20=\
   $(src_pre_msvcr80) \
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   misc/msvcrt20__getmainargs.c \
@@ -1142,6 +1147,7 @@ src_msvcrt40=\
   $(src_pre_msvcr80) \
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   misc/msvcrt__getmainargs.c \
@@ -1153,6 +1159,7 @@ src_msvcrtd=\
   $(src_pre_msvcr80) \
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   locale/___lc_codepage_func.c \
@@ -1165,6 +1172,7 @@ src_msvcr70=\
   $(src_pre_msvcr80) \
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   misc/__p__osplatform.c
@@ -1173,6 +1181,7 @@ src_msvcr71=\
   $(src_pre_msvcr80) \
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   misc/__p__osplatform.c
@@ -1180,6 +1189,7 @@ src_msvcr71=\
 src_msvcr80=\
   $(src_pre_msvcr90) \
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   $(src_pre_msvcr120_post_msvcr71) \
@@ -1187,6 +1197,7 @@ src_msvcr80=\
 
 src_msvcr90=\
   $(src_pre_msvcr100) \
+  $(src_pre_msvcr100_post_msvcrt10) \
   $(src_pre_msvcr110) \
   $(src_pre_msvcr120) \
   $(src_pre_msvcr120_post_msvcr71) \
diff --git a/mingw-w64-crt/misc/_beginthreadex.c 
b/mingw-w64-crt/misc/_beginthreadex.c
new file mode 100644
index 000000000000..dad6eccf97ff
--- /dev/null
+++ b/mingw-w64-crt/misc/_beginthreadex.c
@@ -0,0 +1,181 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <errno.h>
+#include <process.h>
+#include <windows.h>
+
+#if defined(__i386__)
+#include "i386_sse_float_exception_handler.h"
+uintptr_t __cdecl __msvcrt_beginthread(_beginthread_proc_type start_address, 
unsigned stack_size, void *arglist);
+#define _beginthread __msvcrt_beginthread
+#endif
+
+#if defined(__i386__)
+/* We need to make sure that we align the stack to 16 bytes for the sake of 
SSE */
+__attribute__((force_align_arg_pointer))
+#endif
+__MINGW_ATTRIB_NORETURN
+static void __cdecl thread_func(void *data)
+{
+  unsigned ret;
+  void **thread_args = data;
+  _beginthreadex_proc_type start_address = thread_args[0];
+  void *arglist = thread_args[1];
+  unsigned *thread_id_ptr = thread_args[2];
+  HANDLE child_initialized = thread_args[3];
+  HANDLE child_can_run = thread_args[4];
+
+  /* Store our spawned thread id if the caller asked for it. */
+  if (thread_id_ptr)
+    *thread_id_ptr = GetCurrentThreadId();
+
+  /* Inform the parent thread that we (child) have stopped using the
+   * thread_args[] array (which is stored on the parent thread stack).
+   * After this step the parent thread can deallocate the thread_args[]
+   * array and destroy events which owns.
+   */
+  _ReadWriteBarrier();
+  SetEvent(child_initialized);
+
+  /* Wait until the _beginthreadex() parent finish its initialization.
+   * This prevents executing of the application's start_address which
+   * could close the thread handle and break our _beginthreadex().
+   * This also prevents executing of the application's start_address
+   * if our thread should be in the CREATE_SUSPENDED state.
+   */
+  WaitForSingleObject(child_can_run, INFINITE);
+  CloseHandle(child_can_run);
+
+  /* Call the application supplied thread function. */
+#if defined(__i386__)
+  EXCEPTION_REGISTRATION_RECORD exception_record = {
+    .Next = (EXCEPTION_REGISTRATION_RECORD *)__readfsdword(0),
+    .Handler = (PEXCEPTION_ROUTINE)(INT_PTR)sse_float_exception_handler,
+  };
+  __writefsdword(0, (DWORD)&exception_record);
+#endif
+  ret = start_address(arglist);
+#if defined(__i386__)
+  __writefsdword(0, (DWORD)exception_record.Next);
+#endif
+
+  /* Ensure that our thread function never returns back to the caller as
+   * our caller (which is _beginthread) automatically closes the thread handle.
+   * Thread spawned by the _beginthreadex() must not close the thread handle.
+   */
+  ExitThread(ret);
+}
+
+/* mingw-w64 _beginthreadex() implementation is wrapper around the CRT 
_beginthread() function. */
+uintptr_t __cdecl _beginthreadex(void *security, unsigned stack_size, 
_beginthreadex_proc_type start_address, void *arglist, unsigned initflag, 
unsigned *thread_id_ptr)
+{
+  void *thread_args[5];
+  uintptr_t thread_handle;
+  SECURITY_ATTRIBUTES *sec_attrs;
+  HANDLE child_initialized;
+  HANDLE child_can_run;
+  BOOL create_suspended = FALSE;
+  BOOL inherit_handle = FALSE;
+  SECURITY_DESCRIPTOR *dacl_descriptor = NULL;
+
+  if (initflag) {
+    if (initflag & ~(CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION)) {
+      /* Invalid initflag was passed. */
+      errno = EINVAL;
+      return 0;
+    }
+    if (initflag & CREATE_SUSPENDED)
+      create_suspended = TRUE;
+    /* STACK_SIZE_PARAM_IS_A_RESERVATION is ignored, new thread would have 
just larger preallocated stack. */
+  }
+
+  if (security) {
+    sec_attrs = security;
+    if (sec_attrs->nLength != sizeof(*sec_attrs)) {
+      /* Invalid SECURITY_ATTRIBUTES structure was passed. */
+      errno = EINVAL;
+      return 0;
+    }
+    inherit_handle = sec_attrs->bInheritHandle;
+    dacl_descriptor = sec_attrs->lpSecurityDescriptor;
+  }
+
+  child_initialized = CreateEventA(NULL, TRUE, FALSE, NULL);
+  if (!child_initialized) {
+    errno = ENOMEM;
+    return 0;
+  }
+
+  child_can_run = CreateEventA(NULL, TRUE, FALSE, NULL);
+  if (!child_can_run) {
+    CloseHandle(child_initialized);
+    errno = ENOMEM;
+    return 0;
+  }
+
+  thread_args[0] = (void *)start_address;
+  thread_args[1] = arglist;
+  thread_args[2] = thread_id_ptr;
+  thread_args[3] = child_initialized;
+  thread_args[4] = child_can_run;
+  thread_handle = _beginthread(thread_func, stack_size, thread_args);
+
+  if (thread_handle != (uintptr_t)-1) {
+    /* Set HANDLE_FLAG_INHERIT and DACL_SECURITY_INFORMATION as soon as 
possible. */
+    if (inherit_handle)
+      SetHandleInformation((HANDLE)thread_handle, HANDLE_FLAG_INHERIT, 
HANDLE_FLAG_INHERIT);
+    if (dacl_descriptor)
+      SetKernelObjectSecurity((HANDLE)thread_handle, 
DACL_SECURITY_INFORMATION, dacl_descriptor);
+
+    /* Wait until the child thread_func started, filled the thread_id_ptr,
+     * stopped using our stack variables, including the automatic array of
+     * arguments OR the newly spawned child thread crashed/finished.
+     */
+    if (WaitForMultipleObjects(2, (HANDLE[]){(HANDLE)thread_handle, 
child_initialized}, FALSE, INFINITE) == WAIT_OBJECT_0) {
+      /* If the newly spawned thread crashed/finished (returns WAIT_OBJECT_0)
+       * before signaling the successful startup (returns WAIT_OBJECT_1)
+       * then treat it as an _beginthread() error.
+       */
+      CloseHandle((HANDLE)thread_handle);
+      thread_handle = (uintptr_t)-1;
+      errno = EINVAL;
+    }
+    _ReadWriteBarrier();
+  }
+
+  CloseHandle(child_initialized);
+
+  /* _beginthread() returned -1 on error but _beginthreadex() has to return 0 
on error.
+   * _beginthread() already set errno if error happened and above code called
+   * only WinAPI functions which do not modify CRT errno at all.
+   */
+  if (thread_handle == (uintptr_t)-1) {
+    CloseHandle(child_can_run);
+    return 0;
+  }
+
+  /* If the CREATE_SUSPENDED flag was passed then now suspend the child thread.
+   * SuspendThread() is asynchronous function and it returns before the thread
+   * is suspended. To wait until the thread is suspended, it is needed to call
+   * some synchronous operation which forces the suspend request to be 
processed.
+   * GetThreadContext() is one of such function. See:
+   * https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
+   */
+  if (create_suspended) {
+    SuspendThread((HANDLE)thread_handle);
+    GetThreadContext((HANDLE)thread_handle, &(CONTEXT){.ContextFlags = 
CONTEXT_CONTROL});
+  }
+
+  /* Now tell the child thread that it can execute the application's 
start_address.
+   * If the child thread is suspended then it will start execution after is 
resumed.
+   * Child thread will also closes the child_can_run handle.
+   */
+  SetEvent(child_can_run);
+
+  return thread_handle;
+}
+uintptr_t (__cdecl *__MINGW_IMP_SYMBOL(_beginthreadex))(void *, unsigned, 
_beginthreadex_proc_type, void *, unsigned, unsigned *) = _beginthreadex;
diff --git a/mingw-w64-crt/misc/_endthreadex.c 
b/mingw-w64-crt/misc/_endthreadex.c
new file mode 100644
index 000000000000..31243abb7fdf
--- /dev/null
+++ b/mingw-w64-crt/misc/_endthreadex.c
@@ -0,0 +1,20 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <process.h>
+#include <windows.h>
+
+__MINGW_ATTRIB_NORETURN
+void __cdecl _endthreadex(unsigned retval)
+{
+  /* Do not call _endthread() as it automatically closes the thread handle.
+   * Function _endthreadex() must not close the thread handle.
+   * CRT DLL library always correctly release thread resources via its DLL 
entrypoint callback.
+   * So calling just the WinAPI ExitThread() is enough, no memory leak is 
possible.
+   */
+  ExitThread(retval);
+}
+void (__cdecl *__MINGW_IMP_SYMBOL(_endthreadex))(unsigned) = _endthreadex;
-- 
2.20.1



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to