diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 17af6d9cd..87ffefe6e 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -109,6 +109,7 @@ option(WITH_DEBUG_SCARD "Print smartcard debug messages" ${DEFAULT_DEBUG_OPTION} option(WITH_DEBUG_SND "Print rdpsnd debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_SVC "Print static virtual channel debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_TRANSPORT "Print transport debug messages." ${DEFAULT_DEBUG_OPTION}) +option(WITH_DEBUG_THREADS "Print thread debug messages, enables handle dump" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_TIMEZONE "Print timezone debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_WND "Print window order debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_X11_CLIPRDR "Print X11 clipboard redirection debug messages" ${DEFAULT_DEBUG_OPTION}) diff --git a/config.h.in b/config.h.in index 20463fa3c..f4db94989 100644 --- a/config.h.in +++ b/config.h.in @@ -83,6 +83,7 @@ #cmakedefine WITH_DEBUG_SVC #cmakedefine WITH_DEBUG_RDPEI #cmakedefine WITH_DEBUG_TIMEZONE +#cmakedefine WITH_DEBUG_THREADS #cmakedefine WITH_DEBUG_TRANSPORT #cmakedefine WITH_DEBUG_WND #cmakedefine WITH_DEBUG_X11 diff --git a/winpr/include/winpr/thread.h b/winpr/include/winpr/thread.h index 71beed8ef..54e9e192e 100644 --- a/winpr/include/winpr/thread.h +++ b/winpr/include/winpr/thread.h @@ -163,7 +163,7 @@ WINPR_API HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T d WINPR_API HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); -DECLSPEC_NORETURN WINPR_API VOID ExitThread(DWORD dwExitCode); +WINPR_API DECLSPEC_NORETURN VOID ExitThread(DWORD dwExitCode); WINPR_API BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); WINPR_API HANDLE _GetCurrentThread(void); @@ -200,7 +200,11 @@ WINPR_API BOOL TlsFree(DWORD dwTlsIndex); /* CommandLineToArgvA is not present in the original Windows API, WinPR always exports it */ -WINPR_API LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs); +WINPR_API LPSTR *CommandLineToArgvA(LPCSTR lpCmdLine, int *pNumArgs); + +#if defined(WITH_DEBUG_THREADS) +WINPR_API VOID DumpThreadHandles(void); +#endif #ifdef __cplusplus } diff --git a/winpr/libwinpr/handle/handle.c b/winpr/libwinpr/handle/handle.c index a634eb7ab..ff02aea9b 100644 --- a/winpr/libwinpr/handle/handle.c +++ b/winpr/libwinpr/handle/handle.c @@ -127,20 +127,7 @@ BOOL CloseHandle(HANDLE hObject) LeaveCriticalSection(&_HandleCloseCbsLock); - if (Type == HANDLE_TYPE_THREAD) - { - WINPR_THREAD* thread; - thread = (WINPR_THREAD*) Object; - - if (thread->started) - { - pthread_detach(thread->thread); - } - - free(thread); - return TRUE; - } - else if (Type == HANDLE_TYPE_PROCESS) + if (Type == HANDLE_TYPE_PROCESS) { WINPR_PROCESS* process; process = (WINPR_PROCESS*) Object; diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c index e7c099e39..df5c17087 100644 --- a/winpr/libwinpr/pool/pool.c +++ b/winpr/libwinpr/pool/pool.c @@ -100,11 +100,13 @@ static void* thread_pool_work_func(void* arg) } } + ExitThread(0); return NULL; } static void threads_close(void *thread) { + WaitForSingleObject(thread, INFINITE); CloseHandle(thread); } @@ -184,15 +186,6 @@ VOID CloseThreadpool(PTP_POOL ptpp) SetEvent(ptpp->TerminateEvent); - index = ArrayList_Count(ptpp->Threads) - 1; - - while (index >= 0) - { - thread = (HANDLE) ArrayList_GetItem(ptpp->Threads, index); - WaitForSingleObject(thread, INFINITE); - index--; - } - ArrayList_Free(ptpp->Threads); Queue_Free(ptpp->PendingQueue); CountdownEvent_Free(ptpp->WorkComplete); diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt index 68b76faa0..df6a8ce12 100644 --- a/winpr/libwinpr/synch/test/CMakeLists.txt +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -12,6 +12,7 @@ set(${MODULE_PREFIX}_TESTS TestSynchCritical.c TestSynchSemaphore.c TestSynchThread.c + TestSynchMultipleThreads.c TestSynchTimerQueue.c TestSynchWaitableTimer.c TestSynchWaitableTimerAPC.c) diff --git a/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c new file mode 100644 index 000000000..3ced1fa48 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c @@ -0,0 +1,154 @@ + +#include + +#include +#include +#include + +static void *test_thread(void *arg) +{ + long timeout = random(); + timeout %= 1000; + timeout += 100; + Sleep(timeout); + ExitThread(0); + return NULL; +} + +static int start_threads(DWORD count, HANDLE *threads) +{ + DWORD i; + + for (i=0; i (WAIT_OBJECT_0 + THREADS))) + { + printf("WaitForMultipleObjects INFINITE failed\n"); + rc = -1; + } + + if (WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForMultipleObjects bWaitAll, INFINITE failed\n"); + rc = -1; + } + + if (close_threads(THREADS, threads)) + return -1; + + if (rc) + return rc; + + /* WaitOne, timeout */ + if (start_threads(THREADS, threads)) + return -1; + + if (WaitForMultipleObjects(THREADS, threads, FALSE, 50) != WAIT_TIMEOUT) + { + printf("WaitForMultipleObjects timeout 50 failed\n"); + rc = -1; + } + + if (WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForMultipleObjects bWaitAll, INFINITE failed\n"); + rc = -1; + } + + if (close_threads(THREADS, threads)) + return -1; + + /* WaitOne, timeout, multiple joins */ + if (start_threads(THREADS, threads)) + return -1; + + for (i=0; i -static long long ts_difftime(const struct timespec* o, - const struct timespec* n) +static long long ts_difftime(const struct timespec *o, + const struct timespec *n) { long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec; long long newValue = n->tv_sec * 1000000000LL + n->tv_nsec; return newValue - oldValue; } -static int pthread_timedjoin_np(pthread_t td, void** res, - struct timespec* timeout) -{ - struct timespec timenow; - struct timespec sleepytime; - /* This is just to avoid a completely busy wait */ - sleepytime.tv_sec = 0; - sleepytime.tv_nsec = 10000000; /* 10ms */ - - do - { - if (pthread_kill(td, 0)) - return pthread_join(td, res); - - nanosleep(&sleepytime, NULL); - clock_gettime(CLOCK_MONOTONIC, &timenow); - - if (ts_difftime(timeout, &timenow) >= 0) - { - return ETIMEDOUT; - } - } - while (TRUE); - - return ETIMEDOUT; -} +/* Drop in replacement for the linux pthread_timedjoin_np and + * pthread_mutex_timedlock functions. + */ +#if !defined(HAVE_PTHREAD_GNU_EXT) +#include #if defined(__FreeBSD__) /*the only way to get it work is to remove the static*/ -int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* timeout) +int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) #else -static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* timeout) +static int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) #endif { struct timespec timenow; struct timespec sleepytime; + unsigned long long diff; int retcode; /* This is just to avoid a completely busy wait */ - sleepytime.tv_sec = 0; - sleepytime.tv_nsec = 10000000; /* 10ms */ + clock_gettime(CLOCK_MONOTONIC, &timenow); + diff = ts_difftime(&timenow, timeout); + sleepytime.tv_sec = diff / 1000000000LL; + sleepytime.tv_nsec = diff % 1000000000LL; while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY) { @@ -164,7 +141,7 @@ static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec } #endif -static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds) +static void ts_add_ms(struct timespec *ts, DWORD dwMilliseconds) { ts->tv_sec += dwMilliseconds / 1000L; ts->tv_nsec += (dwMilliseconds % 1000L) * 1000000L; @@ -223,48 +200,42 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if (Type == HANDLE_TYPE_THREAD) { - int status = 0; - WINPR_THREAD* thread; - void* thread_status = NULL; - thread = (WINPR_THREAD*) Object; + int status; + WINPR_THREAD *thread = (WINPR_THREAD *)Object; + status = waitOnFd(thread->pipe_fd[0], dwMilliseconds); - if (thread->started) + if (status < 0) { - if (dwMilliseconds != INFINITE) - { - struct timespec timeout; + WLog_ERR(TAG, "waitOnFd() failure [%d] %s", errno, strerror(errno)); + return WAIT_FAILED; + } - /* pthread_timedjoin_np returns ETIMEDOUT in case the timeout is 0, - * so set it to the smallest value to get a proper return value. */ - if (dwMilliseconds == 0) - dwMilliseconds ++; + if (status != 1) + return WAIT_TIMEOUT; - clock_gettime(CLOCK_MONOTONIC, &timeout); - ts_add_ms(&timeout, dwMilliseconds); - status = pthread_timedjoin_np(thread->thread, &thread_status, &timeout); + pthread_mutex_lock(&thread->mutex); - if (ETIMEDOUT == status) - return WAIT_TIMEOUT; - } - else - status = pthread_join(thread->thread, &thread_status); - - thread->started = FALSE; + if (!thread->joined) + { + status = pthread_join(thread->thread, NULL); if (status != 0) { WLog_ERR(TAG, "pthread_join failure: [%d] %s", status, strerror(status)); + pthread_mutex_unlock(&thread->mutex); + return WAIT_FAILED; } - - if (thread_status) - thread->dwExitCode = ((DWORD)(size_t) thread_status); + else + thread->joined = TRUE; } + + pthread_mutex_unlock(&thread->mutex); } else if (Type == HANDLE_TYPE_PROCESS) { - WINPR_PROCESS* process; - process = (WINPR_PROCESS*) Object; + WINPR_PROCESS *process; + process = (WINPR_PROCESS *) Object; if (waitpid(process->pid, &(process->status), 0) != -1) { @@ -276,8 +247,8 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) } else if (Type == HANDLE_TYPE_MUTEX) { - WINPR_MUTEX* mutex; - mutex = (WINPR_MUTEX*) Object; + WINPR_MUTEX *mutex; + mutex = (WINPR_MUTEX *) Object; if (dwMilliseconds != INFINITE) { @@ -298,9 +269,8 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) else if (Type == HANDLE_TYPE_EVENT) { int status; - WINPR_EVENT* event; - event = (WINPR_EVENT*) Object; - + WINPR_EVENT *event; + event = (WINPR_EVENT *) Object; status = waitOnFd(event->pipe_fd[0], dwMilliseconds); if (status < 0) @@ -314,8 +284,8 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) } else if (Type == HANDLE_TYPE_SEMAPHORE) { - WINPR_SEMAPHORE* semaphore; - semaphore = (WINPR_SEMAPHORE*) Object; + WINPR_SEMAPHORE *semaphore; + semaphore = (WINPR_SEMAPHORE *) Object; #ifdef WINPR_PIPE_SEMAPHORE if (semaphore->pipe_fd[0] != -1) @@ -344,16 +314,16 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) #else #if defined __APPLE__ - semaphore_wait(*((winpr_sem_t*) semaphore->sem)); + semaphore_wait(*((winpr_sem_t *) semaphore->sem)); #else - sem_wait((winpr_sem_t*) semaphore->sem); + sem_wait((winpr_sem_t *) semaphore->sem); #endif #endif } else if (Type == HANDLE_TYPE_TIMER) { - WINPR_TIMER* timer; - timer = (WINPR_TIMER*) Object; + WINPR_TIMER *timer; + timer = (WINPR_TIMER *) Object; #ifdef HAVE_EVENTFD_H if (timer->fd != -1) @@ -371,7 +341,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if (status != 1) return WAIT_TIMEOUT; - status = read(timer->fd, (void*) &expirations, sizeof(UINT64)); + status = read(timer->fd, (void *) &expirations, sizeof(UINT64)); if (status != 8) { @@ -405,7 +375,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { int fd; int status; - WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object; + WINPR_NAMED_PIPE *pipe = (WINPR_NAMED_PIPE *) Object; fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; if (fd == -1) @@ -444,15 +414,22 @@ DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertabl #define MAXIMUM_WAIT_OBJECTS 64 -DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds) +DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds) { + struct timespec starttime; + struct timespec timenow; + unsigned long long diff; + DWORD signalled; + DWORD polled; + DWORD *poll_map; + BOOL *signalled_idx; int fd = -1; int index; int status; ULONG Type; PVOID Object; #ifdef HAVE_POLL_H - struct pollfd* pollfds; + struct pollfd *pollfds; #else int maxfd; fd_set fds; @@ -465,202 +442,303 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl return WAIT_FAILED; } -#ifdef HAVE_POLL_H - pollfds = alloca(nCount * sizeof(struct pollfd)); -#else - maxfd = 0; - FD_ZERO(&fds); - ZeroMemory(&timeout, sizeof(timeout)); -#endif - if (bWaitAll) { - WLog_ERR(TAG, "bWaitAll not yet implemented"); - assert(0); + signalled_idx = alloca(nCount * sizeof(BOOL)); + memset(signalled_idx, FALSE, nCount * sizeof(BOOL)); + poll_map = alloca(nCount * sizeof(DWORD)); + memset(poll_map, 0, nCount * sizeof(DWORD)); } - for (index = 0; index < nCount; index++) +#ifdef HAVE_POLL_H + pollfds = alloca(nCount * sizeof(struct pollfd)); +#endif + signalled = 0; + + do { - if (!winpr_Handle_GetInfo(lpHandles[index], &Type, &Object)) - { - WLog_ERR(TAG, "invalid handle"); - return WAIT_FAILED; - } + if (bWaitAll && (dwMilliseconds != INFINITE)) + clock_gettime(CLOCK_MONOTONIC, &starttime); - if (Type == HANDLE_TYPE_EVENT) - { - fd = ((WINPR_EVENT*) Object)->pipe_fd[0]; +#ifndef HAVE_POLL_H + maxfd = 0; + FD_ZERO(&fds); + ZeroMemory(&timeout, sizeof(timeout)); +#endif + polled = 0; - if (fd == -1) + for (index = 0; index < nCount; index++) + { + if (bWaitAll) + { + if (signalled_idx[index]) + continue; + + poll_map[polled] = index; + } + + if (!winpr_Handle_GetInfo(lpHandles[index], &Type, &Object)) { WLog_ERR(TAG, "invalid event file descriptor"); return WAIT_FAILED; } - } - else if (Type == HANDLE_TYPE_SEMAPHORE) - { -#ifdef WINPR_PIPE_SEMAPHORE - fd = ((WINPR_SEMAPHORE*) Object)->pipe_fd[0]; -#else - WLog_ERR(TAG, "semaphore not supported"); - return WAIT_FAILED; -#endif - } - else if (Type == HANDLE_TYPE_TIMER) - { - WINPR_TIMER* timer = (WINPR_TIMER*) Object; - fd = timer->fd; - if (fd == -1) + if (Type == HANDLE_TYPE_EVENT) { - WLog_ERR(TAG, "invalid timer file descriptor"); - return WAIT_FAILED; - } - } - else if (Type == HANDLE_TYPE_NAMED_PIPE) - { - WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object; - fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; + fd = ((WINPR_EVENT *) Object)->pipe_fd[0]; - if (fd == -1) - { - WLog_ERR(TAG, "invalid timer file descriptor"); - return WAIT_FAILED; - } - } - else - { - WLog_ERR(TAG, "unknown handle type %d", (int) Type); - return WAIT_FAILED; - } - - if (fd == -1) - { - WLog_ERR(TAG, "invalid file descriptor"); - return WAIT_FAILED; - } - -#ifdef HAVE_POLL_H - pollfds[index].fd = fd; - pollfds[index].events = POLLIN; - pollfds[index].revents = 0; -#else - FD_SET(fd, &fds); - - if (fd > maxfd) - maxfd = fd; - -#endif - } - -#ifdef HAVE_POLL_H - - do - { - status = poll(pollfds, nCount, dwMilliseconds); - } - while (status < 0 && errno == EINTR); - -#else - - if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) - { - timeout.tv_sec = dwMilliseconds / 1000; - timeout.tv_usec = (dwMilliseconds % 1000) * 1000; - } - - do - { - status = select(maxfd + 1, &fds, 0, 0, - (dwMilliseconds == INFINITE) ? NULL : &timeout); - } - while (status < 0 && errno == EINTR); - -#endif - - if (status < 0) - { - WLog_ERR(TAG, "select() failure [%d] %s", errno, strerror(errno)); - return WAIT_FAILED; - } - - if (status == 0) - return WAIT_TIMEOUT; - - for (index = 0; index < nCount; index++) - { - winpr_Handle_GetInfo(lpHandles[index], &Type, &Object); - - if (Type == HANDLE_TYPE_EVENT) - { - fd = ((WINPR_EVENT*) Object)->pipe_fd[0]; - } - else if (Type == HANDLE_TYPE_SEMAPHORE) - { - fd = ((WINPR_SEMAPHORE*) Object)->pipe_fd[0]; - } - else if (Type == HANDLE_TYPE_TIMER) - { - WINPR_TIMER* timer = (WINPR_TIMER*) Object; - fd = timer->fd; - } - else if (Type == HANDLE_TYPE_NAMED_PIPE) - { - WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object; - fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; - } - -#ifdef HAVE_POLL_H - - if (pollfds[index].revents & POLLIN) -#else - if (FD_ISSET(fd, &fds)) -#endif - { - if (Type == HANDLE_TYPE_SEMAPHORE) - { - int length; - length = read(fd, &length, 1); - - if (length != 1) + if (fd == -1) { - WLog_ERR(TAG, "semaphore read() failure [%d] %s", errno, strerror(errno)); + WLog_ERR(TAG, "invalid event file descriptor"); return WAIT_FAILED; } } + else if (Type == HANDLE_TYPE_SEMAPHORE) + { +#ifdef WINPR_PIPE_SEMAPHORE + fd = ((WINPR_SEMAPHORE *) Object)->pipe_fd[0]; +#else + WLog_ERR(TAG, "semaphore not supported"); + return WAIT_FAILED; +#endif + } else if (Type == HANDLE_TYPE_TIMER) { - int length; - UINT64 expirations; - length = read(fd, (void*) &expirations, sizeof(UINT64)); + WINPR_TIMER *timer = (WINPR_TIMER *) Object; + fd = timer->fd; - if (length != 8) + if (fd == -1) { - if (length == -1) - { - if (errno == ETIMEDOUT) - return WAIT_TIMEOUT; - - WLog_ERR(TAG, "timer read() failure [%d] %s", errno, strerror(errno)); - } - else - { - WLog_ERR(TAG, "timer read() failure - incorrect number of bytes read"); - } - + WLog_ERR(TAG, "invalid timer file descriptor"); return WAIT_FAILED; } } + else if (Type == HANDLE_TYPE_THREAD) + { + WINPR_THREAD *thread = (WINPR_THREAD *) Object; + fd = thread->pipe_fd[0]; - return (WAIT_OBJECT_0 + index); + if (fd == -1) + { + WLog_ERR(TAG, "invalid thread file descriptor"); + return WAIT_FAILED; + } + } + else if (Type == HANDLE_TYPE_NAMED_PIPE) + { + WINPR_NAMED_PIPE *pipe = (WINPR_NAMED_PIPE *) Object; + fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; + + if (fd == -1) + { + WLog_ERR(TAG, "invalid timer file descriptor"); + return WAIT_FAILED; + } + } + else + { + WLog_ERR(TAG, "unknown handle type %d", (int) Type); + return WAIT_FAILED; + } + + if (fd == -1) + { + WLog_ERR(TAG, "invalid file descriptor"); + return WAIT_FAILED; + } + +#ifdef HAVE_POLL_H + pollfds[polled].fd = fd; + pollfds[polled].events = POLLIN; + pollfds[polled].revents = 0; +#else + FD_SET(fd, &fds); + + if (fd > maxfd) + maxfd = fd; + +#endif + polled++; + } + +#ifdef HAVE_POLL_H + + do + { + status = poll(pollfds, polled, dwMilliseconds); + } + while (status < 0 && errno == EINTR); + +#else + + if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) + { + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; + } + + do + { + status = select(maxfd + 1, &fds, 0, 0, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && errno == EINTR); + +#endif + + if (status < 0) + { +#ifdef HAVE_POLL_H + WLog_ERR(TAG, "poll() failure [%d] %s", errno, + strerror(errno)); +#else + WLog_ERR(TAG, "select() failure [%d] %s", errno, + strerror(errno)); +#endif + return WAIT_FAILED; + } + + if (status == 0) + return WAIT_TIMEOUT; + + if (bWaitAll && (dwMilliseconds != INFINITE)) + { + clock_gettime(CLOCK_MONOTONIC, &timenow); + diff = ts_difftime(&timenow, &starttime); + + if (diff / 1000 > dwMilliseconds) + return WAIT_TIMEOUT; + else + dwMilliseconds -= (diff / 1000); + } + + for (index = 0; index < polled; index++) + { + DWORD idx; + + if (bWaitAll) + idx = poll_map[index]; + else + idx = index; + + winpr_Handle_GetInfo(lpHandles[idx], &Type, &Object); + + if (Type == HANDLE_TYPE_EVENT) + { + fd = ((WINPR_EVENT *) Object)->pipe_fd[0]; + } + else if (Type == HANDLE_TYPE_SEMAPHORE) + { + fd = ((WINPR_SEMAPHORE *) Object)->pipe_fd[0]; + } + else if (Type == HANDLE_TYPE_TIMER) + { + WINPR_TIMER *timer = (WINPR_TIMER *) Object; + fd = timer->fd; + } + else if (Type == HANDLE_TYPE_THREAD) + { + WINPR_THREAD *thread = (WINPR_THREAD *) Object; + fd = thread->pipe_fd[0]; + } + else if (Type == HANDLE_TYPE_NAMED_PIPE) + { + WINPR_NAMED_PIPE *pipe = (WINPR_NAMED_PIPE *) Object; + fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; + } + +#ifdef HAVE_POLL_H + + if (pollfds[index].revents & POLLIN) +#else + if (FD_ISSET(fd, &fds)) +#endif + { + if (Type == HANDLE_TYPE_SEMAPHORE) + { + int length; + length = read(fd, &length, 1); + + if (length != 1) + { + WLog_ERR(TAG, "semaphore read() failure [%d] %s", errno, strerror(errno)); + return WAIT_FAILED; + } + } + else if (Type == HANDLE_TYPE_TIMER) + { + int length; + UINT64 expirations; + length = read(fd, (void *) &expirations, sizeof(UINT64)); + + if (length != 8) + { + if (length == -1) + { + if (errno == ETIMEDOUT) + return WAIT_TIMEOUT; + + WLog_ERR(TAG, "timer read() failure [%d] %s", errno, strerror(errno)); + } + else + { + WLog_ERR(TAG, "timer read() failure - incorrect number of bytes read"); + } + + return WAIT_FAILED; + } + } + else if (Type == HANDLE_TYPE_THREAD) + { + WINPR_THREAD *thread = (WINPR_THREAD *)Object; + pthread_mutex_lock(&thread->mutex); + + if (!thread->joined) + { + int status; + status = pthread_join(thread->thread, NULL); + + if (status != 0) + { + WLog_ERR(TAG, "pthread_join failure: [%d] %s", + status, strerror(status)); + pthread_mutex_unlock(&thread->mutex); + return WAIT_FAILED; + } + else + thread->joined = TRUE; + } + + pthread_mutex_unlock(&thread->mutex); + } + + if (bWaitAll) + { + signalled_idx[idx] = TRUE; + + /* Continue checks from last position. */ + for (; signalled < nCount; signalled++) + { + if (!signalled_idx[signalled]) + break; + } + } + + if (!bWaitAll) + return (WAIT_OBJECT_0 + index); + + if (bWaitAll && (signalled >= nCount)) + return (WAIT_OBJECT_0); + } } } + while (bWaitAll); WLog_ERR(TAG, "failed (unknown error)"); return WAIT_FAILED; } -DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, BOOL bAlertable) +DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, BOOL bAlertable) { WLog_ERR(TAG, "[ERROR] %s: Function not implemented."); assert(0); diff --git a/winpr/libwinpr/thread/thread.c b/winpr/libwinpr/thread/thread.c index 51b775b7d..b9107f7bd 100644 --- a/winpr/libwinpr/thread/thread.c +++ b/winpr/libwinpr/thread/thread.c @@ -21,13 +21,15 @@ #include "config.h" #endif +#include + #include #include /** * api-ms-win-core-processthreads-l1-1-1.dll - * + * * CreateRemoteThread * CreateRemoteThreadEx * CreateThread @@ -70,133 +72,467 @@ #include #include -#if defined(__linux__) && !defined(__ANDROID__) +#ifdef HAVE_UNISTD_H #include -#include -#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) +{ + 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->exited) + thread->dwExitCode = (DWORD)(size_t)rc; + + set_event(thread); + + if (thread->detached || !thread->started) + cleanup_handle(thread); + + pthread_exit(thread->dwExitCode); + 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); - thread->started = TRUE; - pthread_create(&thread->thread, &attr, (pthread_start_routine) thread->lpStartAddress, thread->lpParameter); - + 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) + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { HANDLE handle; - WINPR_THREAD* thread; - - thread = (WINPR_THREAD*) calloc(1, sizeof(WINPR_THREAD)); + WINPR_THREAD *thread; + thread = (WINPR_THREAD *) calloc(1, sizeof(WINPR_THREAD)); if (!thread) return NULL; - thread->started = FALSE; + 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; } -HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, - LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) +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) { - pthread_exit((void*) (size_t) dwExitCode); + 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 + { + 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); + + if (thread->detached || !thread->started) + cleanup_handle(thread); + + pthread_exit(thread->dwExitCode); + } } BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) { ULONG Type; PVOID Object; - WINPR_THREAD* thread; + WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return FALSE; - thread = (WINPR_THREAD*) Object; - + thread = (WINPR_THREAD *) Object; *lpExitCode = thread->dwExitCode; - return TRUE; } HANDLE _GetCurrentThread(VOID) { - return NULL; + 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) { -#if defined(__linux__) && !defined(__ANDROID__) - pid_t tid; - tid = syscall(SYS_gettid); - return (DWORD) tid; -#else pthread_t tid; tid = pthread_self(); return (DWORD) tid; -#endif } DWORD ResumeThread(HANDLE hThread) { ULONG Type; PVOID Object; - WINPR_THREAD* thread; + WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return 0; - thread = (WINPR_THREAD*) Object; - + 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; } @@ -209,23 +545,82 @@ BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) { ULONG Type; PVOID Object; - WINPR_THREAD* thread; + WINPR_THREAD *thread; if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) return 0; - thread = (WINPR_THREAD*) Object; - + 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