/** * WinPR: Windows Portable Runtime * Process Thread Functions * * Copyright 2012 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include /** * api-ms-win-core-processthreads-l1-1-1.dll * * CreateRemoteThread * CreateRemoteThreadEx * CreateThread * DeleteProcThreadAttributeList * ExitThread * FlushInstructionCache * FlushProcessWriteBuffers * GetCurrentThread * GetCurrentThreadId * GetCurrentThreadStackLimits * GetExitCodeThread * GetPriorityClass * GetStartupInfoW * GetThreadContext * GetThreadId * GetThreadIdealProcessorEx * GetThreadPriority * GetThreadPriorityBoost * GetThreadTimes * InitializeProcThreadAttributeList * OpenThread * OpenThreadToken * QueryProcessAffinityUpdateMode * QueueUserAPC * ResumeThread * SetPriorityClass * SetThreadContext * SetThreadPriority * SetThreadPriorityBoost * SetThreadStackGuarantee * SetThreadToken * SuspendThread * SwitchToThread * TerminateThread * UpdateProcThreadAttribute */ #ifndef _WIN32 #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_EVENTFD_H #include #endif #include #include #include #include "thread.h" #include "../handle/handle.h" #include "../log.h" #define TAG WINPR_TAG("thread") static pthread_once_t thread_initialized = PTHREAD_ONCE_INIT; static HANDLE_CLOSE_CB _ThreadHandleCloseCb; static wListDictionary *thread_list = NULL; static BOOL ThreadCloseHandle(HANDLE handle); static void cleanup_handle(void *obj); static BOOL ThreadIsHandled(HANDLE handle) { WINPR_THREAD *pThread = (WINPR_THREAD *)handle; if (!pThread || pThread->Type != HANDLE_TYPE_THREAD) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } return TRUE; } static void ThreadInitialize(void) { _ThreadHandleCloseCb.IsHandled = ThreadIsHandled; _ThreadHandleCloseCb.CloseHandle = ThreadCloseHandle; RegisterHandleCloseCb(&_ThreadHandleCloseCb); } static void dump_thread(WINPR_THREAD* thread) { #if defined(WITH_DEBUG_THREADS) void* stack = winpr_backtrace(20); char** msg; size_t used, i; WLog_DBG(TAG, "Called from:"); msg = winpr_backtrace_symbols(stack, &used); for (i=0; icreate_stack, &used); for (i=0; istarted) WLog_DBG(TAG, "Thread still running!"); else if (!thread->exit_stack) WLog_DBG(TAG, "Thread suspended."); else { WLog_DBG(TAG, "Thread exited at:"); msg = winpr_backtrace_symbols(thread->exit_stack, &used); for (i=0; ipipe_fd[0], val); } while ((length < 0) && (errno == EINTR)); status = (length == 0) ? TRUE : FALSE; #else if (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0) { length = write(thread->pipe_fd[1], "-", 1); if (length == 1) status = TRUE; } else { status = TRUE; } #endif return status; } static BOOL reset_event(WINPR_THREAD *thread) { int length; BOOL status = FALSE; #ifdef HAVE_EVENTFD_H eventfd_t value; do { length = eventfd_read(thread->pipe_fd[0], &value); } while ((length < 0) && (errno == EINTR)); if ((length > 0) && (!status)) status = TRUE; #else length = read(thread->pipe_fd[0], &length, 1); if ((length == 1) && (!status)) status = TRUE; #endif return status; } static int thread_compare(void *a, void *b) { pthread_t *p1 = a; pthread_t *p2 = b; int rc = pthread_equal(*p1, *p2); return rc; } /* Thread launcher function responsible for registering * cleanup handlers and calling pthread_exit, if not done * in thread function. */ static void *thread_launcher(void *arg) { DWORD res = -1; void *rc = NULL; WINPR_THREAD *thread = (WINPR_THREAD *)arg; if (!thread) { WLog_ERR(TAG, "Called with invalid argument %p", arg); goto exit; } else { void *(*fkt)(void *) = (void *)thread->lpStartAddress; if (!fkt) { WLog_ERR(TAG, "Thread function argument is %p\n", fkt); goto exit; } rc = fkt(thread->lpParameter); } exit: if (thread) { if (!thread->exited) thread->dwExitCode = (DWORD)(size_t)rc; set_event(thread); res = thread->dwExitCode; if (thread->detached || !thread->started) cleanup_handle(thread); } pthread_exit((void*) (size_t) res); return rc; } static void winpr_StartThread(WINPR_THREAD *thread) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); if (thread->dwStackSize > 0) pthread_attr_setstacksize(&attr, (size_t) thread->dwStackSize); pthread_create(&thread->thread, &attr, thread_launcher, thread); pthread_attr_destroy(&attr); reset_event(thread); ListDictionary_Add(thread_list, &thread->thread, thread); dump_thread(thread); thread->started = TRUE; } HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { HANDLE handle; WINPR_THREAD *thread; thread = (WINPR_THREAD *) calloc(1, sizeof(WINPR_THREAD)); if (!thread) return NULL; pthread_once(&thread_initialized, ThreadInitialize); thread->dwStackSize = dwStackSize; thread->lpParameter = lpParameter; thread->lpStartAddress = lpStartAddress; thread->lpThreadAttributes = lpThreadAttributes; #if defined(WITH_DEBUG_THREADS) thread->create_stack = winpr_backtrace(20); dump_thread(thread); #endif #ifdef HAVE_EVENTFD_H thread->pipe_fd[0] = eventfd(0, EFD_NONBLOCK); if (thread->pipe_fd[0] < 0) { WLog_ERR(TAG, "failed to create thread"); free(thread); return NULL; } #else if (pipe(thread->pipe_fd) < 0) { WLog_ERR(TAG, "failed to create thread"); free(thread); return NULL; } #endif pthread_mutex_init(&thread->mutex, 0); WINPR_HANDLE_SET_TYPE(thread, HANDLE_TYPE_THREAD); handle = (HANDLE) thread; if (NULL == thread_list) { thread_list = ListDictionary_New(TRUE); thread_list->objectKey.fnObjectEquals = thread_compare; } if (!(dwCreationFlags & CREATE_SUSPENDED)) winpr_StartThread(thread); else set_event(thread); return handle; } void cleanup_handle(void *obj) { WINPR_THREAD *thread = (WINPR_THREAD *)obj; int rc = pthread_mutex_destroy(&thread->mutex); if (rc) WLog_ERR(TAG, "failed to destroy mutex [%d] %s (%d)", rc, strerror(errno), errno); if (thread->pipe_fd[0]) close(thread->pipe_fd[0]); if (thread->pipe_fd[1]) close(thread->pipe_fd[1]); if (thread_list && ListDictionary_Contains(thread_list, &thread->thread)) ListDictionary_Remove(thread_list, &thread->thread); #if defined(WITH_DEBUG_THREADS) if (thread->create_stack) winpr_backtrace_free(thread->create_stack); if (thread->exit_stack) winpr_backtrace_free(thread->exit_stack); #endif free(thread); } BOOL ThreadCloseHandle(HANDLE handle) { WINPR_THREAD *thread = (WINPR_THREAD *)handle; if (!thread_list) { WLog_ERR(TAG, "Thread list does not exist, check call!"); dump_thread(thread); } else if (!ListDictionary_Contains(thread_list, &thread->thread)) { WLog_ERR(TAG, "Thread list does not contain this thread! check call!"); dump_thread(thread); } else { ListDictionary_Lock(thread_list); dump_thread(thread); if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)) { WLog_ERR(TAG, "Thread running, setting to detached state!"); thread->detached = TRUE; pthread_detach(thread->thread); } else cleanup_handle(thread); ListDictionary_Unlock(thread_list); if (ListDictionary_Count(thread_list) < 1) { ListDictionary_Free(thread_list); thread_list = NULL; } } return TRUE; } HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { WLog_ERR(TAG, "not implemented"); return NULL; } VOID ExitThread(DWORD dwExitCode) { DWORD rc; pthread_t tid = pthread_self(); if (!thread_list) { WLog_ERR(TAG, "function called without existing thread list!"); #if defined(WITH_DEBUG_THREADS) DumpThreadHandles(); #endif pthread_exit(0); } else if (!ListDictionary_Contains(thread_list, &tid)) { WLog_ERR(TAG, "function called, but no matching entry in thread list!"); #if defined(WITH_DEBUG_THREADS) DumpThreadHandles(); #endif pthread_exit(0); } else { WINPR_THREAD* thread; ListDictionary_Lock(thread_list); thread = ListDictionary_GetItemValue(thread_list, &tid); assert(thread); thread->exited = TRUE; thread->dwExitCode = dwExitCode; #if defined(WITH_DEBUG_THREADS) thread->exit_stack = winpr_backtrace(20); #endif ListDictionary_Unlock(thread_list); set_event(thread); rc = thread->dwExitCode; if (thread->detached || !thread->started) cleanup_handle(thread); pthread_exit((void*) (size_t) rc); } } BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) { ULONG Type; PVOID Object; WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return FALSE; thread = (WINPR_THREAD *) Object; *lpExitCode = thread->dwExitCode; return TRUE; } HANDLE _GetCurrentThread(VOID) { HANDLE hdl = NULL; pthread_t tid = pthread_self(); if (NULL == thread_list) { WLog_ERR(TAG, "function called without existing thread list!"); #if defined(WITH_DEBUG_THREADS) DumpThreadHandles(); #endif } else if (!ListDictionary_Contains(thread_list, &tid)) { WLog_ERR(TAG, "function called, but no matching entry in thread list!"); #if defined(WITH_DEBUG_THREADS) DumpThreadHandles(); #endif } else { hdl = ListDictionary_GetItemValue(thread_list, &tid); } return hdl; } DWORD GetCurrentThreadId(VOID) { pthread_t tid; tid = pthread_self(); return (DWORD) tid; } DWORD ResumeThread(HANDLE hThread) { ULONG Type; PVOID Object; WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return 0; thread = (WINPR_THREAD *) Object; pthread_mutex_lock(&thread->mutex); if (!thread->started) winpr_StartThread(thread); else WLog_WARN(TAG, "Thread already started!"); pthread_mutex_unlock(&thread->mutex); return 0; } DWORD SuspendThread(HANDLE hThread) { WLog_ERR(TAG, "Function not implemented!"); return 0; } BOOL SwitchToThread(VOID) { return TRUE; } BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) { ULONG Type; PVOID Object; WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return 0; thread = (WINPR_THREAD *) Object; thread->exited = TRUE; thread->dwExitCode = dwExitCode; pthread_mutex_lock(&thread->mutex); #ifndef ANDROID pthread_cancel(thread->thread); #else WLog_ERR(TAG, "Function not supported on this platform!"); #endif pthread_mutex_unlock(&thread->mutex); return TRUE; } #if defined(WITH_DEBUG_THREADS) VOID DumpThreadHandles(void) { char** msg; size_t used, i; void* stack = winpr_backtrace(20); WLog_DBG(TAG, "---------------- Called from ----------------------------"); msg = winpr_backtrace_symbols(stack, &used); for (i=0; icreate_stack, &used); for (i=0; istarted) WLog_DBG(TAG, "Thread [%d] still running!", x); else { WLog_DBG(TAG, "Thread [%d] exited at:", x); msg = winpr_backtrace_symbols(thread->exit_stack, &used); for (i=0; i