2 * WinPR: Windows Portable Runtime
3 * Process Thread Functions
5 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
26 #include <winpr/handle.h>
28 #include <winpr/thread.h>
31 * api-ms-win-core-processthreads-l1-1-1.dll
34 * CreateRemoteThreadEx
36 * DeleteProcThreadAttributeList
38 * FlushInstructionCache
39 * FlushProcessWriteBuffers
42 * GetCurrentThreadStackLimits
48 * GetThreadIdealProcessorEx
50 * GetThreadPriorityBoost
52 * InitializeProcThreadAttributeList
55 * QueryProcessAffinityUpdateMode
61 * SetThreadPriorityBoost
62 * SetThreadStackGuarantee
67 * UpdateProcThreadAttribute
72 #include <winpr/crt.h>
73 #include <winpr/platform.h>
80 #include <sys/eventfd.h>
83 #include <winpr/debug.h>
88 #include <winpr/collections.h>
92 #include "../handle/handle.h"
94 #define TAG WINPR_TAG("thread")
96 static wListDictionary* thread_list = NULL;
98 static BOOL ThreadCloseHandle(HANDLE handle);
99 static void cleanup_handle(void* obj);
101 static BOOL ThreadIsHandled(HANDLE handle)
103 WINPR_THREAD* pThread = (WINPR_THREAD*) handle;
105 if (!pThread || (pThread->Type != HANDLE_TYPE_THREAD))
107 SetLastError(ERROR_INVALID_HANDLE);
114 static int ThreadGetFd(HANDLE handle)
116 WINPR_THREAD* pThread = (WINPR_THREAD*) handle;
118 if (!ThreadIsHandled(handle))
121 return pThread->pipe_fd[0];
124 static DWORD ThreadCleanupHandle(HANDLE handle)
126 WINPR_THREAD* thread = (WINPR_THREAD*) handle;
128 if (!ThreadIsHandled(handle))
131 pthread_mutex_lock(&thread->mutex);
136 status = pthread_join(thread->thread, NULL);
140 WLog_ERR(TAG, "pthread_join failure: [%d] %s",
141 status, strerror(status));
142 pthread_mutex_unlock(&thread->mutex);
146 thread->joined = TRUE;
149 pthread_mutex_unlock(&thread->mutex);
151 return WAIT_OBJECT_0;
154 static HANDLE_OPS ops = {
162 static void dump_thread(WINPR_THREAD* thread)
164 #if defined(WITH_DEBUG_THREADS)
165 void* stack = winpr_backtrace(20);
168 WLog_DBG(TAG, "Called from:");
169 msg = winpr_backtrace_symbols(stack, &used);
171 for (i = 0; i < used; i++)
172 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
175 winpr_backtrace_free(stack);
176 WLog_DBG(TAG, "Thread handle created still not closed!");
177 msg = winpr_backtrace_symbols(thread->create_stack, &used);
179 for (i = 0; i < used; i++)
180 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
186 WLog_DBG(TAG, "Thread still running!");
188 else if (!thread->exit_stack)
190 WLog_DBG(TAG, "Thread suspended.");
194 WLog_DBG(TAG, "Thread exited at:");
195 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
197 for (i = 0; i < used; i++)
198 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
206 * TODO: implement thread suspend/resume using pthreads
207 * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
209 static BOOL set_event(WINPR_THREAD *thread)
213 #ifdef HAVE_EVENTFD_H
218 length = eventfd_write(thread->pipe_fd[0], val);
220 while ((length < 0) && (errno == EINTR));
222 status = (length == 0) ? TRUE : FALSE;
224 if (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)
226 length = write(thread->pipe_fd[1], "-", 1);
239 static BOOL reset_event(WINPR_THREAD *thread)
243 #ifdef HAVE_EVENTFD_H
248 length = eventfd_read(thread->pipe_fd[0], &value);
250 while ((length < 0) && (errno == EINTR));
252 if ((length > 0) && (!status))
256 length = read(thread->pipe_fd[0], &length, 1);
258 if ((length == 1) && (!status))
265 static BOOL thread_compare(void* a, void* b)
269 BOOL rc = pthread_equal(*p1, *p2);
273 /* Thread launcher function responsible for registering
274 * cleanup handlers and calling pthread_exit, if not done
275 * in thread function. */
276 static void* thread_launcher(void* arg)
280 WINPR_THREAD* thread = (WINPR_THREAD*) arg;
284 WLog_ERR(TAG, "Called with invalid argument %p", arg);
289 void *(*fkt)(void*) = (void*) thread->lpStartAddress;
293 WLog_ERR(TAG, "Thread function argument is %p", fkt);
297 /* pthread_create(3) doesn't guaranty the thread ID to be set
298 * before to invoke the start_routine() even if this seems to be
300 thread->thread = pthread_self();
302 /* also done by winpr_StartThread(). This ensures thread_list
303 * was updated before the thread could exit */
304 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
306 WLog_ERR(TAG, "Thread function argument is %p", fkt);
310 SetEvent(thread->hLaunchedEvent);
312 rc = fkt(thread->lpParameter);
320 thread->dwExitCode = (DWORD)(size_t)rc;
324 res = thread->dwExitCode;
325 if (thread->detached || !thread->started)
326 cleanup_handle(thread);
328 pthread_exit((void*) (size_t) res);
332 static BOOL winpr_StartThread(WINPR_THREAD *thread)
335 pthread_attr_init(&attr);
336 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
338 if (thread->dwStackSize > 0)
339 pthread_attr_setstacksize(&attr, (size_t) thread->dwStackSize);
341 thread->started = TRUE;
344 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
347 if (WaitForSingleObject(thread->hLaunchedEvent, INFINITE) != WAIT_OBJECT_0)
349 WLog_ERR(TAG, "failed to launch the thread");
353 pthread_attr_destroy(&attr);
358 pthread_attr_destroy(&attr);
362 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
363 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
366 WINPR_THREAD* thread;
368 thread = (WINPR_THREAD*) calloc(1, sizeof(WINPR_THREAD));
373 thread->hLaunchedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
374 if (!thread->hLaunchedEvent)
379 thread->dwStackSize = dwStackSize;
380 thread->lpParameter = lpParameter;
381 thread->lpStartAddress = lpStartAddress;
382 thread->lpThreadAttributes = lpThreadAttributes;
385 #if defined(WITH_DEBUG_THREADS)
386 thread->create_stack = winpr_backtrace(20);
389 thread->pipe_fd[0] = -1;
390 thread->pipe_fd[1] = -1;
392 #ifdef HAVE_EVENTFD_H
393 thread->pipe_fd[0] = eventfd(0, EFD_NONBLOCK);
395 if (thread->pipe_fd[0] < 0)
397 WLog_ERR(TAG, "failed to create thread pipe fd 0");
401 if (pipe(thread->pipe_fd) < 0)
403 WLog_ERR(TAG, "failed to create thread pipe");
408 int flags = fcntl(thread->pipe_fd[0], F_GETFL);
409 fcntl(thread->pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
413 if(pthread_mutex_init(&thread->mutex, 0) != 0)
415 WLog_ERR(TAG, "failed to initialize thread mutex");
419 WINPR_HANDLE_SET_TYPE(thread, HANDLE_TYPE_THREAD);
420 handle = (HANDLE) thread;
424 thread_list = ListDictionary_New(TRUE);
427 WLog_ERR(TAG, "Couldn't create global thread list");
428 goto error_thread_list;
430 thread_list->objectKey.fnObjectEquals = thread_compare;
433 if (!(dwCreationFlags & CREATE_SUSPENDED))
435 if (!winpr_StartThread(thread))
436 goto error_thread_list;
440 if (!set_event(thread))
441 goto error_thread_list;
447 pthread_mutex_destroy(&thread->mutex);
449 if (thread->pipe_fd[1] >= 0)
450 close(thread->pipe_fd[1]);
451 if (thread->pipe_fd[0] >= 0)
452 close(thread->pipe_fd[0]);
459 void cleanup_handle(void *obj)
461 WINPR_THREAD* thread = (WINPR_THREAD*) obj;
462 int rc = pthread_mutex_destroy(&thread->mutex);
465 WLog_ERR(TAG, "failed to destroy mutex [%d] %s (%d)",
466 rc, strerror(errno), errno);
468 if (thread->hLaunchedEvent)
469 CloseHandle(thread->hLaunchedEvent);
471 if (thread->pipe_fd[0])
472 close(thread->pipe_fd[0]);
474 if (thread->pipe_fd[1])
475 close(thread->pipe_fd[1]);
477 if (thread_list && ListDictionary_Contains(thread_list, &thread->thread))
478 ListDictionary_Remove(thread_list, &thread->thread);
480 #if defined(WITH_DEBUG_THREADS)
482 if (thread->create_stack)
483 winpr_backtrace_free(thread->create_stack);
485 if (thread->exit_stack)
486 winpr_backtrace_free(thread->exit_stack);
492 BOOL ThreadCloseHandle(HANDLE handle)
494 WINPR_THREAD* thread = (WINPR_THREAD*) handle;
498 WLog_ERR(TAG, "Thread list does not exist, check call!");
501 else if (!ListDictionary_Contains(thread_list, &thread->thread))
503 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
508 ListDictionary_Lock(thread_list);
511 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
513 WLog_ERR(TAG, "Thread running, setting to detached state!");
514 thread->detached = TRUE;
515 pthread_detach(thread->thread);
519 cleanup_handle(thread);
522 ListDictionary_Unlock(thread_list);
524 if (ListDictionary_Count(thread_list) < 1)
526 ListDictionary_Free(thread_list);
534 HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
535 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
537 WLog_ERR(TAG, "not implemented");
541 VOID ExitThread(DWORD dwExitCode)
544 pthread_t tid = pthread_self();
548 WLog_ERR(TAG, "function called without existing thread list!");
549 #if defined(WITH_DEBUG_THREADS)
554 else if (!ListDictionary_Contains(thread_list, &tid))
556 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
557 #if defined(WITH_DEBUG_THREADS)
564 WINPR_THREAD* thread;
566 ListDictionary_Lock(thread_list);
567 thread = ListDictionary_GetItemValue(thread_list, &tid);
570 thread->exited = TRUE;
571 thread->dwExitCode = dwExitCode;
572 #if defined(WITH_DEBUG_THREADS)
573 thread->exit_stack = winpr_backtrace(20);
575 ListDictionary_Unlock(thread_list);
578 rc = thread->dwExitCode;
579 if (thread->detached || !thread->started)
580 cleanup_handle(thread);
582 pthread_exit((void*) (size_t) rc);
586 BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
590 WINPR_THREAD* thread;
592 if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
595 thread = (WINPR_THREAD*) Object;
596 *lpExitCode = thread->dwExitCode;
600 HANDLE _GetCurrentThread(VOID)
603 pthread_t tid = pthread_self();
607 WLog_ERR(TAG, "function called without existing thread list!");
608 #if defined(WITH_DEBUG_THREADS)
612 else if (!ListDictionary_Contains(thread_list, &tid))
614 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
615 #if defined(WITH_DEBUG_THREADS)
621 hdl = ListDictionary_GetItemValue(thread_list, &tid);
627 DWORD GetCurrentThreadId(VOID)
630 tid = pthread_self();
632 /* Since pthread_t can be 64-bits on some systems, take just the */
633 /* lower 32-bits of it for the thread ID returned by this function. */
634 tid = (long)tid & 0xffffffff;
638 DWORD ResumeThread(HANDLE hThread)
642 WINPR_THREAD *thread;
644 if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
647 thread = (WINPR_THREAD*) Object;
648 pthread_mutex_lock(&thread->mutex);
650 if (!thread->started)
651 winpr_StartThread(thread);
653 WLog_WARN(TAG, "Thread already started!");
655 pthread_mutex_unlock(&thread->mutex);
659 DWORD SuspendThread(HANDLE hThread)
661 WLog_ERR(TAG, "Function not implemented!");
665 BOOL SwitchToThread(VOID)
670 BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
674 WINPR_THREAD* thread;
676 if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
679 thread = (WINPR_THREAD*) Object;
680 thread->exited = TRUE;
681 thread->dwExitCode = dwExitCode;
682 pthread_mutex_lock(&thread->mutex);
684 pthread_cancel(thread->thread);
686 WLog_ERR(TAG, "Function not supported on this platform!");
688 pthread_mutex_unlock(&thread->mutex);
693 #if defined(WITH_DEBUG_THREADS)
694 VOID DumpThreadHandles(void)
698 void* stack = winpr_backtrace(20);
699 WLog_DBG(TAG, "---------------- Called from ----------------------------");
700 msg = winpr_backtrace_symbols(stack, &used);
702 for (i = 0; i < used; i++)
704 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
708 winpr_backtrace_free(stack);
709 WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
713 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
717 ULONG_PTR *keys = NULL;
718 ListDictionary_Lock(thread_list);
719 int x, count = ListDictionary_GetKeys(thread_list, &keys);
720 WLog_DBG(TAG, "Dumping %d elements", count);
722 for (x = 0; x < count; x++)
724 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*) keys[x]);
725 WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
726 msg = winpr_backtrace_symbols(thread->create_stack, &used);
728 for (i = 0; i < used; i++)
730 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
737 WLog_DBG(TAG, "Thread [%d] still running!", x);
741 WLog_DBG(TAG, "Thread [%d] exited at:", x);
742 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
744 for (i=0; i<used; i++)
745 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
753 ListDictionary_Unlock(thread_list);
756 WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");