| #include <process.h> | |
| #include <pthread.h> | |
| #include <stdio.h> | |
| #define WIN32_LEAN_AND_MEAN | |
| #include <windows.h> | |
| /* mingw-w64 headers */ | |
| #include "libtest.h" | |
| #define STATUS_USER_DEFINED (1U << 29) | |
| #define GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C') | |
| #define GCC_EXCEPTION(TYPE) (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC) | |
| #define STATUS_GCC_THROW GCC_EXCEPTION(0) | |
| #define STATUS_GCC_UNWIND GCC_EXCEPTION(1) | |
| #define STATUS_GCC_FORCED GCC_EXCEPTION(2) | |
| static int test(void) | |
| { | |
| /* Test SEH GCC C++ ABI that raising GCC C++ SEH exceptions do not crash whole process */ | |
| RaiseException(STATUS_GCC_THROW, 0, 0, NULL); | |
| /* should return back without crashing application */ | |
| RaiseException(STATUS_GCC_FORCED, 0, 0, NULL); | |
| /* should return back without crashing application */ | |
| UnhandledExceptionFilter(&(EXCEPTION_POINTERS){ &(EXCEPTION_RECORD){ .ExceptionCode = STATUS_GCC_THROW }, NULL }); | |
| /* should return back without crashing application */ | |
| UnhandledExceptionFilter(&(EXCEPTION_POINTERS){ &(EXCEPTION_RECORD){ .ExceptionCode = STATUS_GCC_FORCED }, NULL }); | |
| /* should return back without crashing application */ | |
| return 0; | |
| } | |
| static void *test_from_pthread_create(void *arg __attribute__((unused))) | |
| { | |
| return (void *)(intptr_t)test(); | |
| } | |
| #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 | |
| static DWORD WINAPI test_from_createthread(LPVOID arg __attribute__((unused))) | |
| { | |
| return test(); | |
| } | |
| #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 | |
| static unsigned __stdcall test_from_beginthreadex(void *arg __attribute__((unused))) | |
| { | |
| return test(); | |
| } | |
| #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 | |
| static void __attribute__((noreturn)) __cdecl test_from_beginthread(void *arg __attribute__((unused))) | |
| { | |
| /* Do not return from _beginthread's startaddress and do not call _endthread() | |
| * as they invalidate the thread handle returned by _beginthread(). | |
| * The thread handle is used by _beginthread()'s caller which will close it. | |
| * So the only safe option is to exit this tread via WinAPI ExitThread() function. | |
| */ | |
| ExitThread(test()); | |
| } | |
| int main() | |
| { | |
| int ret = 0; | |
| pthread_t pthread; | |
| HANDLE wthread; | |
| mingw_test_init (); | |
| { | |
| int tret = test(); | |
| if (tret) | |
| { | |
| puts("test in main thread failed"); | |
| ret = 1; | |
| } | |
| else | |
| puts("test in main thread passed"); | |
| } | |
| /* FIXME: Disable pthread test because mingw-w64-crt/testcases build system does not provide pthread support yet */ | |
| #if 0 | |
| if (pthread_create(&pthread, NULL, test_from_pthread_create, NULL) != 0) | |
| { | |
| puts("\nfailed to spawn thread via pthread_create()"); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| void *tret; | |
| if (pthread_join(pthread, &tret) != 0 || tret) | |
| { | |
| puts("test in thread spawned by pthread_create() failed"); | |
| ret = 1; | |
| } | |
| else | |
| puts("test in thread spawned by pthread_create() passed"); | |
| } | |
| #else | |
| (void)pthread; | |
| #endif | |
| wthread = CreateThread(NULL, 0, test_from_createthread, NULL, 0, &(DWORD){0} /*out: ThreadId*/); | |
| if (wthread == NULL) | |
| { | |
| puts("failed to spawn thread via CreateThread()"); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| DWORD tret; | |
| WaitForSingleObject(wthread, INFINITE); | |
| GetExitCodeThread(wthread, &tret); | |
| CloseHandle(wthread); | |
| if (tret) | |
| { | |
| puts("test in thread spawned by CreateThread() failed"); | |
| ret = 1; | |
| } | |
| else | |
| puts("test in thread spawned by CreateThread() passed"); | |
| } | |
| wthread = (HANDLE)_beginthreadex(NULL, 0, test_from_beginthreadex, NULL, 0, &(unsigned){0} /*out: ThreadId*/); | |
| if (wthread == NULL) | |
| { | |
| puts("failed to spawn thread via _beginthreadex()"); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| DWORD tret; | |
| WaitForSingleObject(wthread, INFINITE); | |
| GetExitCodeThread(wthread, &tret); | |
| CloseHandle(wthread); | |
| if (tret) | |
| { | |
| puts("test in thread spawned by _beginthreadex() failed"); | |
| ret = 1; | |
| } | |
| else | |
| puts("test in thread spawned by _beginthreadex() passed"); | |
| } | |
| wthread = (HANDLE)_beginthread(test_from_beginthread, 0, NULL); | |
| if (wthread == INVALID_HANDLE_VALUE) | |
| { | |
| puts("failed to spawn thread via _beginthread()"); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| DWORD tret; | |
| WaitForSingleObject(wthread, INFINITE); | |
| GetExitCodeThread(wthread, &tret); | |
| CloseHandle(wthread); | |
| if (tret) | |
| { | |
| puts("test in thread spawned by _beginthread() failed"); | |
| ret = 1; | |
| } | |
| else | |
| puts("test in thread spawned by _beginthread() passed"); | |
| } | |
| return ret; | |
| } |