WINPR_API HANDLE _GetCurrentThread(void);
WINPR_API DWORD GetCurrentThreadId(void);
+ typedef void (*PAPCFUNC)(ULONG_PTR Parameter);
+ WINPR_API DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData);
+
WINPR_API DWORD ResumeThread(HANDLE hThread);
WINPR_API DWORD SuspendThread(HANDLE hThread);
WINPR_API BOOL SwitchToThread(void);
#include <sys/eventfd.h>
#endif
+#include <fcntl.h>
#include <errno.h>
#include "../handle/handle.h"
#include "../pipe/pipe.h"
#include "../log.h"
+#include "event.h"
#define TAG WINPR_TAG("synch.event")
+#ifdef HAVE_SYS_EVENTFD_H
+#if !defined(WITH_EVENTFD_READ_WRITE)
+static int eventfd_read(int fd, eventfd_t* value)
+{
+ return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
+}
+
+static int eventfd_write(int fd, eventfd_t value)
+{
+ return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
+}
+#endif
+#endif
+
+BOOL winpr_event_init(WINPR_EVENT_IMPL* event)
+{
+#ifdef HAVE_SYS_EVENTFD_H
+ event->fds[1] = -1;
+ event->fds[0] = eventfd(0, EFD_NONBLOCK);
+
+ return event->fds[0] >= 0;
+#else
+ int flags;
+
+ if (pipe(event->fds) < 0)
+ return FALSE;
+
+ flags = fcntl(event->fds[0], F_GETFL);
+ if (flags < 0)
+ goto out_error;
+
+ if (fcntl(event->fds[0], F_SETFL, flags | O_NONBLOCK) < 0)
+ goto out_error;
+
+ return TRUE;
+
+out_error:
+ winpr_event_uninit(&event);
+ return FALSE;
+#endif
+}
+
+void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd)
+{
+ event->fds[0] = fd;
+#ifndef HAVE_SYS_EVENTFD_H
+ event->fds[1] = fd;
+#endif
+}
+
+BOOL winpr_event_set(WINPR_EVENT_IMPL* event)
+{
+ int ret;
+ do
+ {
+#ifdef HAVE_SYS_EVENTFD_H
+ eventfd_t value = 1;
+ ret = eventfd_write(event->fds[0], value);
+#else
+ ret = write(event->fds[1], "-", 1);
+#endif
+ } while (ret < 0 && errno == EINTR);
+
+ return ret >= 0;
+}
+
+BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
+{
+ int ret;
+ do
+ {
+ do
+ {
+#ifdef HAVE_SYS_EVENTFD_H
+ eventfd_t value = 1;
+ ret = eventfd_read(event->fds[0], &value);
+#else
+ char value;
+ ret = read(event->fds[1], &value, 1);
+#endif
+ } while (ret < 0 && errno == EINTR);
+ } while (ret >= 0);
+
+ return (errno == EAGAIN);
+}
+
+void winpr_event_uninit(WINPR_EVENT_IMPL* event)
+{
+ if (event->fds[0] != -1)
+ close(event->fds[0]);
+#ifndef HAVE_SYS_EVENTFD_H
+ if (event->fds[1] != -1)
+ close(event->fds[1]);
+#endif
+}
+
static BOOL EventCloseHandle(HANDLE handle);
static BOOL EventIsHandled(HANDLE handle)
if (!EventIsHandled(handle))
return -1;
- return event->pipe_fd[0];
+ return event->impl.fds[0];
}
static BOOL EventCloseHandle_(WINPR_EVENT* event)
return FALSE;
if (!event->bAttached)
- {
- if (event->pipe_fd[0] != -1)
- {
- close(event->pipe_fd[0]);
- event->pipe_fd[0] = -1;
- }
-
- if (event->pipe_fd[1] != -1)
- {
- close(event->pipe_fd[1]);
- event->pipe_fd[1] = -1;
- }
- }
+ winpr_event_uninit(&event->impl);
free(event->name);
free(event);
if (!event->bManualReset)
WLog_ERR(TAG, "auto-reset events not yet implemented");
- event->pipe_fd[0] = -1;
- event->pipe_fd[1] = -1;
-#ifdef HAVE_SYS_EVENTFD_H
- event->pipe_fd[0] = eventfd(0, EFD_NONBLOCK);
-
- if (event->pipe_fd[0] < 0)
- goto fail;
-
-#else
-
- if (pipe(event->pipe_fd) < 0)
+ if (!winpr_event_init(&event->impl))
goto fail;
-#endif
-
if (bInitialState)
{
if (!SetEvent(event))
return NULL;
}
-#ifdef HAVE_SYS_EVENTFD_H
-#if !defined(WITH_EVENTFD_READ_WRITE)
-static int eventfd_read(int fd, eventfd_t* value)
-{
- return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
-}
-
-static int eventfd_write(int fd, eventfd_t value)
-{
- return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
-}
-#endif
-#endif
-
BOOL SetEvent(HANDLE hEvent)
{
ULONG Type;
WINPR_HANDLE* Object;
- int length;
BOOL status;
WINPR_EVENT* event;
status = FALSE;
{
event = (WINPR_EVENT*)Object;
-#ifdef HAVE_SYS_EVENTFD_H
- eventfd_t val = 1;
-
- do
- {
- length = eventfd_write(event->pipe_fd[0], val);
- } while ((length < 0) && (errno == EINTR));
-
- status = (length == 0) ? TRUE : FALSE;
-#else
-
- if (WaitForSingleObject(hEvent, 0) != WAIT_OBJECT_0)
- {
- length = write(event->pipe_fd[1], "-", 1);
-
- if (length == 1)
- status = TRUE;
- }
- else
- {
- status = TRUE;
- }
-
-#endif
+ status = winpr_event_set(&event->impl);
}
return status;
{
ULONG Type;
WINPR_HANDLE* Object;
- int length;
- BOOL status = TRUE;
WINPR_EVENT* event;
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object))
event = (WINPR_EVENT*)Object;
- while (status && WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
- {
- do
- {
-#ifdef HAVE_SYS_EVENTFD_H
- eventfd_t value;
- length = eventfd_read(event->pipe_fd[0], &value);
-#else
- length = read(event->pipe_fd[0], &length, 1);
-#endif
- } while ((length < 0) && (errno == EINTR));
-
- if (length < 0)
- status = FALSE;
- }
-
- return status;
+ return winpr_event_reset(&event->impl);
}
#endif
{
event->bAttached = TRUE;
event->bManualReset = bManualReset;
- event->pipe_fd[0] = FileDescriptor;
- event->pipe_fd[1] = -1;
+ winpr_event_init_from_fd(&event->impl, FileDescriptor);
event->ops = &ops;
WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
handle = (HANDLE)event;
event = (WINPR_EVENT*)Object;
- if (!event->bAttached && event->pipe_fd[0] >= 0 && event->pipe_fd[0] != FileDescriptor)
- close(event->pipe_fd[0]);
+ if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
+ close(event->impl.fds[0]);
event->bAttached = TRUE;
event->Mode = mode;
- event->pipe_fd[0] = FileDescriptor;
+ event->impl.fds[0] = FileDescriptor;
return 0;
#else
return -1;
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * event implementation
+ *
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
+ * 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.
+ */
+#ifndef WINPR_LIBWINPR_SYNCH_EVENT_H_
+#define WINPR_LIBWINPR_SYNCH_EVENT_H_
+
+#include "../handle/handle.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
+
+struct winpr_event_impl
+{
+ int fds[2];
+};
+
+typedef struct winpr_event_impl WINPR_EVENT_IMPL;
+
+struct winpr_event
+{
+ WINPR_HANDLE_DEF();
+
+ WINPR_EVENT_IMPL impl;
+ BOOL bAttached;
+ BOOL bManualReset;
+ char* name;
+};
+typedef struct winpr_event WINPR_EVENT;
+
+BOOL winpr_event_init(WINPR_EVENT_IMPL* event);
+void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd);
+BOOL winpr_event_set(WINPR_EVENT_IMPL* event);
+BOOL winpr_event_reset(WINPR_EVENT_IMPL* event);
+void winpr_event_uninit(WINPR_EVENT_IMPL* event);
+
+#endif /* WINPR_LIBWINPR_SYNCH_EVENT_H_ */
+#ifndef _WIN32
#include <errno.h>
#include "pollset.h"
#include <winpr/handle.h>
+#include <winpr/sysinfo.h>
#include "../log.h"
#define TAG WINPR_TAG("sync.pollset")
if (!set->fdIndex)
return FALSE;
+ FD_ZERO(&set->rset_base);
FD_ZERO(&set->rset);
+ FD_ZERO(&set->wset_base);
FD_ZERO(&set->wset);
set->maxFd = 0;
set->nread = set->nwrite = 0;
void pollset_reset(WINPR_POLL_SET* set)
{
#ifndef HAVE_POLL_H
- FD_ZERO(&set->rset);
- FD_ZERO(&set->wset);
+ FD_ZERO(&set->rset_base);
+ FD_ZERO(&set->wset_base);
set->maxFd = 0;
set->nread = set->nwrite = 0;
#endif
FdIndex* fdIndex = &set->fdIndex[set->fillIndex];
if (mode & WINPR_FD_READ)
{
- FD_SET(fd, &set->rset);
+ FD_SET(fd, &set->rset_base);
set->nread++;
}
if (mode & WINPR_FD_WRITE)
{
- FD_SET(fd, &set->wset);
+ FD_SET(fd, &set->wset_base);
set->nwrite++;
}
int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds)
{
- int ret;
+ int ret = 0;
+ UINT64 dueTime, now;
+
+ now = GetTickCount64();
+ if (dwMilliseconds == INFINITE)
+ dueTime = 0xFFFFFFFFFFFFFFFF;
+ else
+ dueTime = now + dwMilliseconds;
+
#ifdef HAVE_POLL_H
+ int timeout;
+
do
{
- ret = poll(set->pollset, set->fillIndex, dwMilliseconds);
- } while (ret < 0 && errno == EINTR);
-#else
- struct timeval staticTimeout;
- struct timeval* timeout;
+ if (dwMilliseconds == INFINITE)
+ timeout = -1;
+ else
+ timeout = (int)(dueTime - now);
- if (dwMilliseconds == INFINITE || dwMilliseconds == 0)
- {
- timeout = NULL;
- }
- else
- {
- timeout = &staticTimeout;
- timeout->tv_sec = dwMilliseconds / 1000;
- timeout->tv_usec = (dwMilliseconds % 1000) * 1000;
- }
+ ret = poll(set->pollset, set->fillIndex, timeout);
+ if (ret >= 0)
+ return ret;
+
+ if (errno != EINTR)
+ return -1;
+
+ now = GetTickCount64();
+ } while (now < dueTime);
+#else
do
{
- ret = select(set->maxFd + 1, set->nread ? &set->rset : NULL,
- set->nwrite ? &set->wset : NULL, NULL, timeout);
- } while (ret < 0 && errno == EINTR);
+ struct timeval staticTimeout;
+ struct timeval* timeout;
+
+ fd_set* rset = NULL;
+ fd_set* wset = NULL;
+
+ if (dwMilliseconds == INFINITE)
+ {
+ timeout = NULL;
+ }
+ else
+ {
+ long waitTime = (long)(dueTime - now);
+
+ timeout = &staticTimeout;
+ timeout->tv_sec = waitTime / 1000;
+ timeout->tv_usec = (waitTime % 1000) * 1000;
+ }
+
+ if (set->nread)
+ {
+ rset = &set->rset;
+ memcpy(rset, &set->rset_base, sizeof(*rset));
+ }
+
+ if (set->nwrite)
+ {
+ wset = &set->wset;
+ memcpy(wset, &set->wset_base, sizeof(*wset));
+ }
+
+ ret = select(set->maxFd + 1, rset, wset, NULL, timeout);
+ if (ret >= 0)
+ return ret;
+
+ if (errno != EINTR)
+ return -1;
+
+ now = GetTickCount64();
+
+ } while (now < dueTime);
+
+ FD_ZERO(&set->rset);
+ FD_ZERO(&set->wset);
#endif
- return ret;
+ return 0; /* timeout */
}
BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx)
return FALSE;
#endif
}
+#endif
#include "config.h"
#endif
+#ifndef _WIN32
+
#ifdef HAVE_POLL_H
#include <poll.h>
#else
BOOL isStatic;
#else
FdIndex* fdIndex;
+ fd_set rset_base;
fd_set rset;
+ fd_set wset_base;
fd_set wset;
int nread, nwrite;
int maxFd;
int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds);
BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx);
+#endif
+
#endif /* WINPR_LIBWINPR_SYNCH_POLLSET_H_ */
#include <winpr/synch.h>
#include "../log.h"
+#include "../thread/apc.h"
+#include "../thread/thread.h"
+#include "../synch/pollset.h"
#define TAG WINPR_TAG("synch.sleep")
DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlertable)
{
- /* TODO: Implement bAlertable support */
- if (bAlertable)
- WLog_WARN(TAG, "%s does not support bAlertable", __FUNCTION__);
- Sleep(dwMilliseconds);
- return 0;
+ WINPR_THREAD* thread = winpr_GetCurrentThread();
+ WINPR_POLL_SET pollset;
+ int status;
+ DWORD ret = WAIT_FAILED;
+ BOOL autoSignalled;
+
+ if (!thread)
+ {
+ WLog_ERR(TAG, "unable to retrieve currentThread");
+ return WAIT_FAILED;
+ }
+
+ /* treat re-entrancy if a completion is calling us */
+ if (thread->apc.treatingCompletions)
+ bAlertable = FALSE;
+
+ if (!bAlertable || !thread->apc.length)
+ {
+ usleep(dwMilliseconds * 1000);
+ return 0;
+ }
+
+ if (!pollset_init(&pollset, thread->apc.length))
+ {
+ WLog_ERR(TAG, "unable to initialize pollset");
+ return WAIT_FAILED;
+ }
+
+ if (!apc_collectFds(thread, &pollset, &autoSignalled))
+ {
+ WLog_ERR(TAG, "unable to APC file descriptors");
+ goto out;
+ }
+
+ if (!autoSignalled)
+ {
+ /* we poll and wait only if no APC member is ready */
+ status = pollset_poll(&pollset, dwMilliseconds);
+ if (status < 0)
+ {
+ WLog_ERR(TAG, "polling of apc fds failed");
+ goto out;
+ }
+ }
+
+ if (apc_executeCompletions(thread, &pollset, 0))
+ {
+ ret = WAIT_IO_COMPLETION;
+ }
+ else
+ {
+ /* according to the spec return value is 0 see
+ * https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepex*/
+ ret = 0;
+ }
+out:
+ pollset_uninit(&pollset);
+ return ret;
}
#endif
#include <winpr/synch.h>
#include "../handle/handle.h"
+#include "../thread/apc.h"
+#include "event.h"
#ifndef _WIN32
};
typedef struct winpr_semaphore WINPR_SEMAPHORE;
-struct winpr_event
-{
- WINPR_HANDLE_DEF();
-
- int pipe_fd[2];
- BOOL bAttached;
- BOOL bManualReset;
- char* name;
-};
-typedef struct winpr_event WINPR_EVENT;
-
#ifdef HAVE_SYS_TIMERFD_H
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/timerfd.h>
-#endif
+#define TIMER_IMPL_TIMERFD
-#if defined(__APPLE__)
+#elif defined(WITH_POSIX_TIMER)
+#include <fcntl.h>
+#define TIMER_IMPL_POSIX
+
+#elif defined(__APPLE__)
+#define TIMER_IMPL_DISPATCH
#include <dispatch/dispatch.h>
+#else
+#error missing timer implementation
#endif
struct winpr_timer
PTIMERAPCROUTINE pfnCompletionRoutine;
LPVOID lpArgToCompletionRoutine;
-#ifdef WITH_POSIX_TIMER
+#ifdef TIMER_IMPL_TIMERFD
+ struct itimerspec timeout;
+#endif
+
+#ifdef TIMER_IMPL_POSIX
+ WINPR_EVENT_IMPL event;
timer_t tid;
struct itimerspec timeout;
#endif
-#if defined(__APPLE__)
+
+#ifdef TIMER_IMPL_DISPATCH
+ WINPR_EVENT_IMPL event;
dispatch_queue_t queue;
dispatch_source_t source;
- int pipe[2];
BOOL running;
#endif
char* name;
+
+ WINPR_APC_ITEM apcItem;
};
typedef struct winpr_timer WINPR_TIMER;
TestSynchMultipleThreads.c
TestSynchTimerQueue.c
TestSynchWaitableTimer.c
- TestSynchWaitableTimerAPC.c)
+ TestSynchWaitableTimerAPC.c
+ TestSynchAPC.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS
${${MODULE_PREFIX}_DRIVER}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * TestSyncAPC
+ *
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
+ * 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.
+ */
+#include <winpr/wtypes.h>
+#include <winpr/thread.h>
+#include <winpr/synch.h>
+
+typedef struct
+{
+ BOOL error;
+ BOOL called;
+} UserApcArg;
+
+void CALLBACK userApc(ULONG_PTR arg)
+{
+ UserApcArg* userArg = (UserApcArg*)arg;
+ userArg->called = TRUE;
+}
+
+static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter)
+{
+ /* this thread post an APC that will never get executed */
+ UserApcArg* userArg = (UserApcArg*)lpThreadParameter;
+ if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter))
+ {
+ userArg->error = TRUE;
+ return 1;
+ }
+
+ return 0;
+}
+
+static DWORD WINAPI cleanThread(LPVOID lpThreadParameter)
+{
+ Sleep(500);
+
+ SleepEx(500, TRUE);
+ return 0;
+}
+
+typedef struct
+{
+ HANDLE timer1;
+ DWORD timer1Calls;
+ HANDLE timer2;
+ DWORD timer2Calls;
+ BOOL endTest;
+} UncleanCloseData;
+
+static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
+{
+ UncleanCloseData* data = (UncleanCloseData*)lpArg;
+ data->timer1Calls++;
+ CloseHandle(data->timer2);
+ data->endTest = TRUE;
+}
+
+static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
+{
+ UncleanCloseData* data = (UncleanCloseData*)lpArg;
+ data->timer2Calls++;
+}
+
+static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter)
+{
+ LARGE_INTEGER dueTime;
+ UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter;
+ data->endTest = FALSE;
+
+ dueTime.QuadPart = -500;
+ if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE))
+ return 1;
+
+ dueTime.QuadPart = -900;
+ if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE))
+ return 1;
+
+ while (!data->endTest)
+ {
+ SleepEx(100, TRUE);
+ }
+ return 0;
+}
+
+int TestSynchAPC(int argc, char* argv[])
+{
+ HANDLE thread = NULL;
+ UserApcArg userApcArg;
+ UncleanCloseData uncleanCloseData;
+
+ userApcArg.error = FALSE;
+ userApcArg.called = FALSE;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ /* first post an APC and check it is executed during a SleepEx */
+ if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg))
+ return 1;
+
+ if (SleepEx(100, FALSE) != 0)
+ return 2;
+
+ if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION)
+ return 3;
+
+ if (!userApcArg.called)
+ return 4;
+
+ userApcArg.called = FALSE;
+
+ /* test that the APC is cleaned up even when not called */
+ thread = CreateThread(NULL, 0, uncleanThread, &userApcArg, 0, NULL);
+ if (!thread)
+ return 10;
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+ if (userApcArg.called || userApcArg.error)
+ return 11;
+
+ /* test a remote APC queuing */
+ thread = CreateThread(NULL, 0, cleanThread, &userApcArg, 0, NULL);
+ if (!thread)
+ return 20;
+
+ if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg))
+ return 21;
+
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+ if (!userApcArg.called)
+ return 22;
+
+#if 0
+ /* test cleanup of timer completions */
+ memset(&uncleanCloseData, 0, sizeof(uncleanCloseData));
+ uncleanCloseData.timer1 = CreateWaitableTimerA(NULL, FALSE, NULL);
+ if (!uncleanCloseData.timer1)
+ return 31;
+
+ uncleanCloseData.timer2 = CreateWaitableTimerA(NULL, FALSE, NULL);
+ if (!uncleanCloseData.timer2)
+ return 32;
+
+ thread = CreateThread(NULL, 0, closeHandleTest, &uncleanCloseData, 0, NULL);
+ if (!thread)
+ return 33;
+
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+ if (uncleanCloseData.timer1Calls != 1 || uncleanCloseData.timer2Calls != 0)
+ return 34;
+ CloseHandle(uncleanCloseData.timer1);
+#endif
+ return 0;
+}
int TestSynchWaitableTimerAPC(int argc, char* argv[])
{
int status = -1;
+ DWORD rc;
HANDLE hTimer = NULL;
BOOL bSuccess;
LARGE_INTEGER due;
}
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
-
if (!hTimer)
goto cleanup;
- due.QuadPart = -15000000LL; /* 1.5 seconds */
+ due.QuadPart = -1000 * 1000LL; /* 1 seconds */
apcData.StartTime = GetTickCount();
- bSuccess = SetWaitableTimer(hTimer, &due, 2000, TimerAPCProc, &apcData, FALSE);
+ bSuccess = SetWaitableTimer(hTimer, &due, 100, TimerAPCProc, &apcData, FALSE);
if (!bSuccess)
goto cleanup;
- /**
- * See Remarks at
- * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686786(v=vs.85).aspx The
- * SetWaitableTimer completion routine is executed by the thread that activates the timer using
- * SetWaitableTimer. However, the thread must be in an ALERTABLE state.
- */
-
- /**
- * Note: On WIN32 we need to use WaitForSingleObjectEx with parameter bAlertable = TRUE
- * However, WinPR currently (May 2016) does not have a working WaitForSingleObjectEx
- *implementation but its non-WIN32 WaitForSingleObject implementations seem to be alertable by
- *WinPR's timer implementations.
- **/
+ /* nothing shall happen after 1.2 second, because thread is not in alertable state */
+ rc = WaitForSingleObject(g_Event, 1200);
+ if (rc != WAIT_TIMEOUT)
+ goto cleanup;
for (;;)
{
- DWORD rc;
-#ifdef _WIN32
rc = WaitForSingleObjectEx(g_Event, INFINITE, TRUE);
-#else
- rc = WaitForSingleObject(g_Event, INFINITE);
-#endif
-
if (rc == WAIT_OBJECT_0)
break;
* Synchronization Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <signal.h>
#endif
+#include "event.h"
#include "synch.h"
#ifndef _WIN32
#include "../handle/handle.h"
+#include "../thread/thread.h"
#include "../log.h"
#define TAG WINPR_TAG("synch.timer")
if (timer->bManualReset)
return WAIT_OBJECT_0;
- length = read(timer->fd, (void*)&expirations, sizeof(UINT64));
+#ifdef TIMER_IMPL_TIMERFD
+ do
+ {
+ length = read(timer->fd, (void*)&expirations, sizeof(UINT64));
+ } while (length < 0 && errno == EINTR);
if (length != 8)
{
- if (length == -1)
+ if (length < 0)
{
switch (errno)
{
return WAIT_FAILED;
}
+#else
+ if (!winpr_event_reset(&timer->event))
+ {
+ WLog_ERR(TAG, "timer reset() failure");
+ return WAIT_FAILED;
+ }
+#endif
return WAIT_OBJECT_0;
}
+typedef struct
+{
+ WINPR_APC_ITEM apcItem;
+ WINPR_TIMER* timer;
+} TimerDeleter;
+
+static void TimerPostDelete_APC(LPVOID arg)
+{
+ TimerDeleter* deleter = (TimerDeleter*)arg;
+ free(deleter->timer);
+ deleter->apcItem.markedForFree = TRUE;
+ deleter->apcItem.markedForRemove = TRUE;
+}
+
BOOL TimerCloseHandle(HANDLE handle)
{
WINPR_TIMER* timer;
if (!TimerIsHandled(handle))
return FALSE;
- if (!timer->lpArgToCompletionRoutine)
- {
-#ifdef HAVE_SYS_TIMERFD_H
-
- if (timer->fd != -1)
- close(timer->fd);
-
+#ifdef TIMER_IMPL_TIMERFD
+ if (timer->fd != -1)
+ close(timer->fd);
#endif
- }
- else
- {
-#ifdef WITH_POSIX_TIMER
- timer_delete(timer->tid);
+
+#ifdef TIMER_IMPL_POSIX
+ timer_delete(timer->tid);
#endif
- }
-#if defined(__APPLE__)
+#ifdef TIMER_IMPL_DISPATCH
dispatch_release(timer->queue);
dispatch_release(timer->source);
-
- if (timer->pipe[0] != -1)
- close(timer->pipe[0]);
-
- if (timer->pipe[1] != -1)
- close(timer->pipe[1]);
-
#endif
- free(timer->name);
- free(timer);
- return TRUE;
-}
-
-#ifdef WITH_POSIX_TIMER
-
-static BOOL g_WaitableTimerSignalHandlerInstalled = FALSE;
-
-static void WaitableTimerHandler(void* arg)
-{
- WINPR_TIMER* timer = (WINPR_TIMER*)arg;
- if (!timer)
- return;
+#if defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH)
+ winpr_event_uninit(&timer->event);
+#endif
- if (timer->pfnCompletionRoutine)
+ free(timer->name);
+ if (timer->apcItem.linked)
{
- timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
+ TimerDeleter* deleter;
+ WINPR_APC_ITEM* apcItem;
- if (timer->lPeriod)
+ switch (apc_remove(&timer->apcItem))
{
- timer->timeout.it_interval.tv_sec = (timer->lPeriod / 1000); /* seconds */
- timer->timeout.it_interval.tv_nsec =
- ((timer->lPeriod % 1000) * 1000000); /* nanoseconds */
-
- if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0)
+ case APC_REMOVE_OK:
+ break;
+ case APC_REMOVE_DELAY_FREE:
{
- WLog_ERR(TAG, "timer_settime");
+ WINPR_THREAD* thread = winpr_GetCurrentThread();
+ if (!thread)
+ return FALSE;
+
+ deleter = calloc(1, sizeof(*deleter));
+ if (!deleter)
+ {
+ WLog_ERR(TAG, "unable to allocate a timer deleter");
+ return TRUE;
+ }
+
+ deleter->timer = timer;
+ apcItem = &deleter->apcItem;
+ apcItem->type = APC_TYPE_HANDLE_FREE;
+ apcItem->alwaysSignaled = TRUE;
+ apcItem->completion = TimerPostDelete_APC;
+ apcItem->completionArgs = deleter;
+ apc_register(thread, apcItem);
+ return TRUE;
}
+ case APC_REMOVE_ERROR:
+ default:
+ WLog_ERR(TAG, "unable to remove timer from APC list");
+ break;
}
}
+
+ free(timer);
+ return TRUE;
}
+
+#ifdef TIMER_IMPL_POSIX
+
static void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg)
{
WINPR_TIMER* timer = siginfo->si_value.sival_ptr;
+ UINT64 data = 1;
WINPR_UNUSED(arg);
if (!timer || (signum != SIGALRM))
return;
- WaitableTimerHandler(timer);
+ if (!winpr_event_set(&timer->event))
+ WLog_ERR(TAG, "error when notifying event");
}
-static int InstallWaitableTimerSignalHandler(void)
-{
- if (!g_WaitableTimerSignalHandlerInstalled)
- {
- struct sigaction action;
- sigemptyset(&action.sa_mask);
- sigaddset(&action.sa_mask, SIGALRM);
- action.sa_flags = SA_RESTART | SA_SIGINFO;
- action.sa_sigaction = WaitableTimerSignalHandler;
- sigaction(SIGALRM, &action, NULL);
- g_WaitableTimerSignalHandlerInstalled = TRUE;
- }
+static INIT_ONCE TimerSignalHandler_InitOnce = INIT_ONCE_STATIC_INIT;
- return 0;
+static BOOL InstallTimerSignalHandler(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
+{
+ struct sigaction action;
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGALRM);
+ action.sa_flags = SA_RESTART | SA_SIGINFO;
+ action.sa_sigaction = WaitableTimerSignalHandler;
+ sigaction(SIGALRM, &action, NULL);
+ return TRUE;
}
-
#endif
-#if defined(__APPLE__)
+#ifdef TIMER_IMPL_DISPATCH
static void WaitableTimerHandler(void* arg)
{
UINT64 data = 1;
if (!timer)
return;
- if (timer->pfnCompletionRoutine)
- timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
-
- if (write(timer->pipe[1], &data, sizeof(data)) != sizeof(data))
+ if (!winpr_event_set(&timer->event))
WLog_ERR(TAG, "failed to write to pipe");
if (timer->lPeriod == 0)
{
int result = 0;
- if (!timer->lpArgToCompletionRoutine)
+#ifdef TIMER_IMPL_TIMERFD
+ timer->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+ if (timer->fd <= 0)
+ return -1;
+#elif defined(TIMER_IMPL_POSIX)
+ struct sigevent sigev;
+ InitOnceExecuteOnce(&TimerSignalHandler_InitOnce, InstallTimerSignalHandler, NULL, NULL);
+ ZeroMemory(&sigev, sizeof(struct sigevent));
+ sigev.sigev_notify = SIGEV_SIGNAL;
+ sigev.sigev_signo = SIGALRM;
+ sigev.sigev_value.sival_ptr = (void*)timer;
+
+ if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0)
{
-#ifdef HAVE_SYS_TIMERFD_H
- timer->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
-
- if (timer->fd <= 0)
- return -1;
-
-#elif defined(__APPLE__)
-#else
- WLog_ERR(TAG, "%s: os specific implementation is missing", __FUNCTION__);
- result = -1;
-#endif
+ WLog_ERR(TAG, "timer_create");
+ return -1;
}
- else
- {
-#ifdef WITH_POSIX_TIMER
- struct sigevent sigev;
- InstallWaitableTimerSignalHandler();
- ZeroMemory(&sigev, sizeof(struct sigevent));
- sigev.sigev_notify = SIGEV_SIGNAL;
- sigev.sigev_signo = SIGALRM;
- sigev.sigev_value.sival_ptr = (void*)timer;
-
- if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0)
- {
- WLog_ERR(TAG, "timer_create");
- return -1;
- }
-
-#elif defined(__APPLE__)
-#else
- WLog_ERR(TAG, "%s: os specific implementation is missing", __FUNCTION__);
- result = -1;
+#elif !defined(TIMER_IMPL_DISPATCH)
+ WLog_ERR(TAG, "%s: os specific implementation is missing", __FUNCTION__);
+ result = -1;
#endif
- }
timer->bInit = TRUE;
return result;
}
+static BOOL timer_drain_fd(int fd)
+{
+ UINT64 expr;
+ int ret;
+
+ do
+ {
+ ret = read(fd, &expr, sizeof(expr));
+ } while (ret < 0 && errno == EINTR);
+
+ return ret >= 0;
+}
+
static HANDLE_OPS ops = { TimerIsHandled, TimerCloseHandle,
TimerGetFd, TimerCleanupHandle,
NULL, NULL,
timer->name = strdup(lpTimerName);
timer->ops = &ops;
-#if defined(__APPLE__)
-
- if (pipe(timer->pipe) != 0)
+#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX)
+ if (!winpr_event_init(&timer->event))
goto fail;
+#endif
+#if defined(TIMER_IMPL_DISPATCH)
timer->queue = dispatch_queue_create(TAG, DISPATCH_QUEUE_SERIAL);
if (!timer->queue)
dispatch_set_context(timer->source, timer);
dispatch_source_set_event_handler_f(timer->source, WaitableTimerHandler);
- timer->fd = timer->pipe[0];
-
- if (fcntl(timer->fd, F_SETFL, O_NONBLOCK) < 0)
- goto fail;
-
#endif
}
return handle;
-#if defined(__APPLE__)
+
+#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX)
fail:
TimerCloseHandle(handle);
return NULL;
return handle;
}
+static void timerAPC(LPVOID arg)
+{
+ WINPR_TIMER* timer = (WINPR_TIMER*)arg;
+
+ if (!timer->lPeriod)
+ {
+ /* this is a one time shot timer with a completion, let's remove us from
+ the APC list */
+ switch (apc_remove(&timer->apcItem))
+ {
+ case APC_REMOVE_OK:
+ case APC_REMOVE_DELAY_FREE:
+ break;
+ case APC_REMOVE_ERROR:
+ default:
+ WLog_ERR(TAG, "error removing the APC routine");
+ }
+ }
+
+ if (timer->pfnCompletionRoutine)
+ timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
+
+#ifdef TIMER_IMPL_TIMERFD
+ while (timer_drain_fd(timer->fd))
+ ;
+#else
+ winpr_event_reset(&timer->event);
+#endif
+}
+
BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine,
BOOL fResume)
ULONG Type;
WINPR_HANDLE* Object;
WINPR_TIMER* timer;
-#if defined(WITH_POSIX_TIMER) || defined(__APPLE__)
LONGLONG seconds = 0;
LONGLONG nanoseconds = 0;
-#ifdef HAVE_SYS_TIMERFD_H
int status = 0;
-#endif /* HAVE_SYS_TIMERFD_H */
-#endif /* WITH_POSIX_TIMER */
if (!winpr_Handle_GetInfo(hTimer, &Type, &Object))
return FALSE;
return FALSE;
}
-#ifdef WITH_POSIX_TIMER
+#if defined(TIMER_IMPL_TIMERFD) || defined(TIMER_IMPL_POSIX)
ZeroMemory(&(timer->timeout), sizeof(struct itimerspec));
if (lpDueTime->QuadPart < 0)
timer->timeout.it_value.tv_nsec = timer->timeout.it_interval.tv_nsec; /* nanoseconds */
}
- if (!timer->pfnCompletionRoutine)
+#ifdef TIMER_IMPL_TIMERFD
+ status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL);
+ if (status)
{
-#ifdef HAVE_SYS_TIMERFD_H
- status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL);
-
- if (status)
- {
- WLog_ERR(TAG, "timerfd_settime failure: %d", status);
- return FALSE;
- }
-
-#endif
+ WLog_ERR(TAG, "timerfd_settime failure: %d", status);
+ return FALSE;
}
- else
+#else
+ status = timer_settime(timer->tid, 0, &(timer->timeout), NULL);
+ if (status != 0)
{
- if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0)
- {
- WLog_ERR(TAG, "timer_settime");
- return FALSE;
- }
+ WLog_ERR(TAG, "timer_settime failure");
+ return FALSE;
}
+#endif
+#endif
-#elif defined(__APPLE__)
-
+#ifdef TIMER_IMPL_DISPATCH
if (lpDueTime->QuadPart < 0)
{
LONGLONG due = lpDueTime->QuadPart * (-1);
return FALSE;
}
+ if (!winpr_event_reset(&timer->event))
{
- /* Clean out old data from FD */
- BYTE buffer[32];
-
- while (read(timer->fd, buffer, sizeof(buffer)) > 0)
- ;
+ WLog_ERR(TAG, "error when resetting timer event");
}
{
dispatch_resume(timer->source);
timer->running = TRUE;
}
-
#endif
+
+ if (pfnCompletionRoutine)
+ {
+ WINPR_APC_ITEM* apcItem = &timer->apcItem;
+
+ /* install our APC routine that will call the completion */
+ apcItem->type = APC_TYPE_TIMER;
+ apcItem->alwaysSignaled = FALSE;
+ apcItem->pollFd = timer->fd;
+ apcItem->pollMode = WINPR_FD_READ;
+ apcItem->completion = timerAPC;
+ apcItem->completionArgs = timer;
+
+ if (!apcItem->linked)
+ {
+ WINPR_THREAD* thread = winpr_GetCurrentThread();
+ if (!thread)
+ return FALSE;
+
+ apc_register(thread, apcItem);
+ }
+ }
+ else
+ {
+ if (timer->apcItem.linked)
+ {
+ apc_remove(&timer->apcItem);
+ }
+ }
return TRUE;
}
#include <unistd.h>
#endif
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#else
-#ifndef _WIN32
-#include <sys/select.h>
-#endif
-#endif
-
#include <assert.h>
#include <errno.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/platform.h>
+#include <winpr/sysinfo.h>
#include "synch.h"
#include "pollset.h"
#endif
+/* Drop in replacement for pthread_mutex_timedlock
+ */
+#if !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)
+#include <pthread.h>
+
static long long ts_difftime(const struct timespec* o, const struct timespec* n)
{
long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec;
return newValue - oldValue;
}
-/* Drop in replacement for pthread_mutex_timedlock
- */
-#if !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)
-#include <pthread.h>
-
static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* timeout)
{
struct timespec timenow;
}
#endif
-
static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
{
ts->tv_sec += dwMilliseconds / 1000L;
ts->tv_nsec = ts->tv_nsec % 1000000000L;
}
-
-DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
+DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
{
ULONG Type;
WINPR_HANDLE* Object;
+ WINPR_POLL_SET pollset;
if (!winpr_Handle_GetInfo(hHandle, &Type, &Object))
{
else
{
int status;
- WINPR_POLL_SET pollset;
- int fd = winpr_Handle_getFd(Object);
+ WINPR_THREAD* thread;
+ BOOL isSet = FALSE;
+ size_t extraFds = 0;
+ DWORD ret;
+ BOOL autoSignaled = FALSE;
+ if (bAlertable)
+ {
+ thread = (WINPR_THREAD*)_GetCurrentThread();
+ if (!thread)
+ {
+ WLog_ERR(TAG, "failed to retrieve currentThread");
+ return WAIT_FAILED;
+ }
+
+ /* treat reentrancy, we can't switch to alertable state when we're already
+ treating completions */
+ if (thread->apc.treatingCompletions)
+ bAlertable = FALSE;
+ else
+ extraFds = thread->apc.length;
+ }
+
+ int fd = winpr_Handle_getFd(Object);
if (fd < 0)
{
WLog_ERR(TAG, "winpr_Handle_getFd did not return a fd!");
return WAIT_FAILED;
}
- if (!pollset_init(&pollset, 1))
+ if (!pollset_init(&pollset, 1 + extraFds))
{
WLog_ERR(TAG, "unable to initialize pollset");
SetLastError(ERROR_INTERNAL_ERROR);
if (!pollset_add(&pollset, fd, Object->Mode))
{
- pollset_uninit(&pollset);
- return WAIT_FAILED;
+ WLog_ERR(TAG, "unable to add fd in pollset");
+ goto out;
}
- status = pollset_poll(&pollset, dwMilliseconds);
- pollset_uninit(&pollset);
+ if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
+ {
+ WLog_ERR(TAG, "unable to collect APC fds");
+ goto out;
+ }
- if (status < 0)
+ if (!autoSignaled)
{
- WLog_ERR(TAG, "waitOnFd() failure [%d] %s", errno, strerror(errno));
- SetLastError(ERROR_INTERNAL_ERROR);
- return WAIT_FAILED;
+ status = pollset_poll(&pollset, dwMilliseconds);
+ if (status < 0)
+ {
+ WLog_ERR(TAG, "waitOnFd() failure [%d] %s", errno, strerror(errno));
+ goto out;
+ }
}
- if (status != 1)
- return WAIT_TIMEOUT;
+ ret = WAIT_TIMEOUT;
+ if (bAlertable && apc_executeCompletions(thread, &pollset, 1))
+ ret = WAIT_IO_COMPLETION;
+
+ isSet = pollset_isSignaled(&pollset, 0);
+ pollset_uninit(&pollset);
+
+ if (!isSet)
+ return ret;
return winpr_Handle_cleanup(Object);
}
+out:
+ pollset_uninit(&pollset);
SetLastError(ERROR_INTERNAL_ERROR);
return WAIT_FAILED;
}
-DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
+DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
{
- if (bAlertable)
- {
- /* TODO: Implement */
- WLog_ERR(TAG, "%s: Not implemented: bAlertable", __FUNCTION__);
- return WAIT_FAILED;
- }
- return WaitForSingleObject(hHandle, dwMilliseconds);
+ return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE);
}
-DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
- DWORD dwMilliseconds)
+DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
+ DWORD dwMilliseconds, BOOL bAlertable)
{
- struct timespec starttime;
- struct timespec timenow;
- unsigned long long diff;
DWORD signalled;
DWORD polled;
DWORD* poll_map = NULL;
- BOOL* signalled_idx = NULL;
+ BOOL* signalled_handles = NULL;
int fd = -1;
DWORD index;
int status;
ULONG Type;
- BOOL signal_handled = FALSE;
WINPR_HANDLE* Object;
+ WINPR_THREAD* thread;
WINPR_POLL_SET pollset;
DWORD ret = WAIT_FAILED;
+ size_t extraFds = 0;
+ UINT64 now, dueTime;
if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS))
{
return WAIT_FAILED;
}
- if (!pollset_init(&pollset, nCount))
+ if (bAlertable)
+ {
+ thread = winpr_GetCurrentThread();
+ if (!thread)
+ return WAIT_FAILED;
+
+ /* treat reentrancy, we can't switch to alertable state when we're already
+ treating completions */
+ if (thread->apc.treatingCompletions)
+ bAlertable = FALSE;
+ else
+ extraFds = thread->apc.length;
+ }
+
+ if (!pollset_init(&pollset, nCount + extraFds))
{
- WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 "", nCount);
+ WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 " extraCount=%" PRIu32 "",
+ nCount, extraFds);
return WAIT_FAILED;
}
if (bWaitAll)
{
- signalled_idx = alloca(nCount * sizeof(BOOL));
- memset(signalled_idx, FALSE, nCount * sizeof(BOOL));
+ signalled_handles = alloca(nCount * sizeof(BOOL));
+ memset(signalled_handles, FALSE, nCount * sizeof(BOOL));
+
poll_map = alloca(nCount * sizeof(DWORD));
memset(poll_map, 0, nCount * sizeof(DWORD));
}
signalled = 0;
- if (bWaitAll && (dwMilliseconds != INFINITE))
- clock_gettime(CLOCK_MONOTONIC, &starttime);
+ now = GetTickCount64();
+ if (dwMilliseconds != INFINITE)
+ dueTime = now + dwMilliseconds;
+ else
+ dueTime = 0xFFFFFFFFFFFFFFFF;
do
{
+ BOOL autoSignaled = FALSE;
polled = 0;
+ /* first collect file descriptors to poll */
for (index = 0; index < nCount; index++)
{
if (bWaitAll)
{
- if (signalled_idx[index])
+ if (signalled_handles[index])
continue;
poll_map[polled] = index;
polled++;
}
- status = pollset_poll(&pollset, dwMilliseconds);
- if (status < 0)
+ /* treat file descriptors of the APC if needed */
+ if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
{
-#ifdef HAVE_POLL_H
- WLog_ERR(TAG, "poll() handle %d (%" PRIu32 ") failure [%d] %s", index, nCount, errno,
- strerror(errno));
-#else
- WLog_ERR(TAG, "select() handle %d (%" PRIu32 ") failure [%d] %s", index, nCount, errno,
- strerror(errno));
-#endif
- winpr_log_backtrace(TAG, WLOG_ERROR, 20);
+ WLog_ERR(TAG, "unable to register APC fds");
SetLastError(ERROR_INTERNAL_ERROR);
goto out;
}
- if (status == 0)
+ /* poll file descriptors */
+ status = 0;
+ if (!autoSignaled)
{
- ret = WAIT_TIMEOUT;
- goto out;
- }
+ DWORD waitTime;
- if (bWaitAll && (dwMilliseconds != INFINITE))
- {
- clock_gettime(CLOCK_MONOTONIC, &timenow);
- diff = ts_difftime(&timenow, &starttime);
+ if (dwMilliseconds == INFINITE)
+ waitTime = INFINITE;
+ else
+ waitTime = (DWORD)(dueTime - now);
- if (diff / 1000 > dwMilliseconds)
+ status = pollset_poll(&pollset, waitTime);
+ if (status < 0)
{
- ret = WAIT_TIMEOUT;
+#ifdef HAVE_POLL_H
+ WLog_ERR(TAG, "poll() handle %d (%" PRIu32 ") failure [%d] %s", index, nCount,
+ errno, strerror(errno));
+#else
+ WLog_ERR(TAG, "select() handle %d (%" PRIu32 ") failure [%d] %s", index, nCount,
+ errno, strerror(errno));
+#endif
+ winpr_log_backtrace(TAG, WLOG_ERROR, 20);
+ SetLastError(ERROR_INTERNAL_ERROR);
goto out;
}
-
- dwMilliseconds -= (diff / 1000);
}
- signal_handled = FALSE;
-
- for (index = 0; index < polled; index++)
+ /* give priority to the APC queue, to return WAIT_IO_COMPLETION */
+ if (bAlertable && apc_executeCompletions(thread, &pollset, polled))
{
- DWORD idx;
- BOOL signal_set = FALSE;
-
- if (bWaitAll)
- idx = poll_map[index];
- else
- idx = index;
-
- signal_set = pollset_isSignaled(&pollset, index);
+ ret = WAIT_IO_COMPLETION;
+ goto out;
+ }
- if (signal_set)
+ /* then treat pollset */
+ if (status)
+ {
+ for (index = 0; index < polled; index++)
{
- DWORD rc = winpr_Handle_cleanup(lpHandles[idx]);
- if (rc != WAIT_OBJECT_0)
- {
- WLog_ERR(TAG, "error in cleanup function for handle at index=%d", idx);
- ret = rc;
- goto out;
- }
+ DWORD handlesIndex;
+ BOOL signal_set = FALSE;
if (bWaitAll)
- {
- signalled_idx[idx] = TRUE;
+ handlesIndex = poll_map[index];
+ else
+ handlesIndex = index;
- /* Continue checks from last position. */
- for (; signalled < nCount; signalled++)
+ signal_set = pollset_isSignaled(&pollset, index);
+ if (signal_set)
+ {
+ DWORD rc = winpr_Handle_cleanup(lpHandles[handlesIndex]);
+ if (rc != WAIT_OBJECT_0)
{
- if (!signalled_idx[signalled])
- break;
+ WLog_ERR(TAG, "error in cleanup function for handle at index=%d",
+ handlesIndex);
+ ret = rc;
+ goto out;
}
- }
- if (!bWaitAll)
- {
- ret = (WAIT_OBJECT_0 + index);
- goto out;
- }
+ if (bWaitAll)
+ {
+ signalled_handles[handlesIndex] = TRUE;
+
+ /* Continue checks from last position. */
+ for (; signalled < nCount; signalled++)
+ {
+ if (!signalled_handles[signalled])
+ break;
+ }
+ }
+ else
+ {
+ ret = (WAIT_OBJECT_0 + handlesIndex);
+ goto out;
+ }
- if (signalled >= nCount)
- {
- ret = WAIT_OBJECT_0;
- goto out;
+ if (signalled >= nCount)
+ {
+ ret = WAIT_OBJECT_0;
+ goto out;
+ }
}
+ }
+ }
- signal_handled = TRUE;
+ if (bAlertable && thread->apc.length > extraFds)
+ {
+ pollset_uninit(&pollset);
+ extraFds = thread->apc.length;
+ if (!pollset_init(&pollset, nCount + extraFds))
+ {
+ WLog_ERR(TAG, "unable reallocate pollset");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return WAIT_FAILED;
}
}
+ else
+ pollset_reset(&pollset);
- pollset_reset(&pollset);
- } while (bWaitAll || !signal_handled);
+ now = GetTickCount64();
+ } while (now < dueTime);
- WLog_ERR(TAG, "failed (unknown error)");
- SetLastError(ERROR_INTERNAL_ERROR);
+ ret = WAIT_TIMEOUT;
out:
pollset_uninit(&pollset);
return ret;
}
-DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
- DWORD dwMilliseconds, BOOL bAlertable)
+DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
+ DWORD dwMilliseconds)
{
- if (bAlertable)
- {
- /* TODO: Implement */
- WLog_ERR(TAG, "%s: Not implemented: bAlertable", __FUNCTION__);
- return WAIT_FAILED;
- }
-
- return WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds);
+ return WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, FALSE);
}
DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds,
# limitations under the License.
winpr_module_add(
+ apc.h
+ apc.c
argv.c
process.c
processor.c
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * APC implementation
+ *
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
+ * 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.
+ */
+#ifndef _WIN32
+
+#include "apc.h"
+#include "thread.h"
+#include "../log.h"
+#include "../synch/pollset.h"
+
+#define TAG WINPR_TAG("apc")
+
+BOOL apc_init(APC_QUEUE* apc)
+{
+ pthread_mutexattr_t attr;
+ BOOL ret = FALSE;
+
+ pthread_mutexattr_init(&attr);
+ if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
+ {
+ WLog_ERR(TAG, "failed to initialize mutex attributes to recursive");
+ return FALSE;
+ }
+
+ memset(apc, 0, sizeof(*apc));
+
+ if (pthread_mutex_init(&apc->mutex, &attr) != 0)
+ {
+ WLog_ERR(TAG, "failed to initialize main thread APC mutex");
+ goto out;
+ }
+
+ ret = TRUE;
+out:
+ pthread_mutexattr_destroy(&attr);
+ return ret;
+}
+
+BOOL apc_uninit(APC_QUEUE* apc)
+{
+ return pthread_mutex_destroy(&apc->mutex) == 0;
+}
+
+void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem)
+{
+ WINPR_APC_ITEM** nextp;
+ APC_QUEUE* apc = &thread->apc;
+
+ pthread_mutex_lock(&apc->mutex);
+ if (apc->tail)
+ {
+ nextp = &apc->tail->next;
+ addItem->last = apc->tail;
+ }
+ else
+ {
+ nextp = &apc->head;
+ }
+
+ *nextp = addItem;
+ apc->tail = addItem;
+ apc->length++;
+
+ addItem->markedForRemove = FALSE;
+ addItem->boundThread = GetCurrentThreadId();
+ addItem->linked = TRUE;
+ pthread_mutex_unlock(&apc->mutex);
+}
+
+static INLINE void apc_item_remove(APC_QUEUE* apc, WINPR_APC_ITEM* item)
+{
+ if (!item->last)
+ apc->head = item->next;
+ else
+ item->last->next = item->next;
+
+ if (!item->next)
+ apc->tail = item->last;
+ else
+ item->next->last = item->last;
+
+ apc->length--;
+}
+
+APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item)
+{
+ WINPR_THREAD* thread = winpr_GetCurrentThread();
+ APC_QUEUE* apc;
+ APC_REMOVE_RESULT ret = APC_REMOVE_OK;
+
+ if (!item->linked)
+ return APC_REMOVE_OK;
+
+ if (item->boundThread != GetCurrentThreadId())
+ {
+ WLog_ERR(TAG, "removing an APC entry should be done in the creating thread");
+ return APC_REMOVE_ERROR;
+ }
+
+ if (!thread)
+ {
+ WLog_ERR(TAG, "unable to retrieve current thread");
+ return APC_REMOVE_ERROR;
+ }
+
+ apc = &thread->apc;
+ pthread_mutex_lock(&apc->mutex);
+ if (apc->treatingCompletions)
+ {
+ item->markedForRemove = TRUE;
+ ret = APC_REMOVE_DELAY_FREE;
+ goto out;
+ }
+
+ apc_item_remove(apc, item);
+
+out:
+ pthread_mutex_unlock(&apc->mutex);
+ item->boundThread = 0xFFFFFFFF;
+ item->linked = FALSE;
+ return ret;
+}
+
+BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled)
+{
+ WINPR_APC_ITEM* item;
+ BOOL ret = FALSE;
+ APC_QUEUE* apc = &thread->apc;
+
+ *haveAutoSignaled = FALSE;
+ pthread_mutex_lock(&apc->mutex);
+ item = apc->head;
+ for (; item; item = item->next)
+ {
+ if (item->alwaysSignaled)
+ *haveAutoSignaled = TRUE;
+ else if (!pollset_add(set, item->pollFd, item->pollMode))
+ goto out;
+ }
+
+ ret = TRUE;
+out:
+ pthread_mutex_unlock(&apc->mutex);
+ return ret;
+}
+
+int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t idx)
+{
+ APC_QUEUE* apc = &thread->apc;
+ WINPR_APC_ITEM *item, *nextItem;
+ int ret = 0;
+
+ pthread_mutex_lock(&apc->mutex);
+ apc->treatingCompletions = TRUE;
+
+ /* first pass to compute signaled items */
+ for (item = apc->head; item; item = item->next)
+ {
+ item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx);
+ if (!item->alwaysSignaled)
+ idx++;
+ }
+
+ /* second pass: run completions */
+ for (item = apc->head; item; item = nextItem)
+ {
+ if (item->isSignaled)
+ {
+ if (item->completion && !item->markedForRemove)
+ item->completion(item->completionArgs);
+ ret++;
+ }
+
+ nextItem = item->next;
+
+ if (item->markedForRemove)
+ {
+ apc_item_remove(apc, item);
+
+ if (item->markedForFree)
+ free(item);
+ }
+ }
+
+ /* third pass: to do final cleanup */
+ for (item = apc->head; item; item = nextItem)
+ {
+ nextItem = item->next;
+
+ if (item->markedForRemove)
+ {
+ apc_item_remove(apc, item);
+ if (item->markedForFree)
+ free(item);
+ }
+ }
+
+ apc->treatingCompletions = FALSE;
+ pthread_mutex_unlock(&apc->mutex);
+
+ return ret;
+}
+
+void apc_cleanupThread(WINPR_THREAD* thread)
+{
+ WINPR_APC_ITEM* item;
+ WINPR_APC_ITEM* nextItem;
+ APC_QUEUE* apc = &thread->apc;
+
+ pthread_mutex_lock(&apc->mutex);
+ item = apc->head;
+ for (; item; item = nextItem)
+ {
+ nextItem = item->next;
+
+ if (item->type == APC_TYPE_HANDLE_FREE)
+ item->completion(item->completionArgs);
+
+ item->last = item->next = NULL;
+ item->linked = FALSE;
+ if (item->markedForFree)
+ free(item);
+ }
+
+ apc->head = apc->tail = NULL;
+ pthread_mutex_unlock(&apc->mutex);
+}
+
+#endif
--- /dev/null
+/**
+ * WinPR: Windows Portable Runtime
+ * APC implementation
+ *
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
+ * 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.
+ */
+
+#ifndef WINPR_APC_H
+#define WINPR_APC_H
+
+#include <winpr/winpr.h>
+#include <winpr/wtypes.h>
+
+#ifndef _WIN32
+
+#include <pthread.h>
+
+typedef struct winpr_thread WINPR_THREAD;
+typedef struct winpr_APC_item WINPR_APC_ITEM;
+typedef struct winpr_poll_set WINPR_POLL_SET;
+
+typedef void (*apc_treatment)(LPVOID arg);
+
+typedef enum
+{
+ APC_TYPE_USER,
+ APC_TYPE_TIMER,
+ APC_TYPE_HANDLE_FREE
+} ApcType;
+
+struct winpr_APC_item
+{
+ ApcType type;
+ int pollFd;
+ DWORD pollMode;
+ apc_treatment completion;
+ LPVOID completionArgs;
+ BOOL markedForFree;
+
+ /* private fields used by the APC */
+ BOOL alwaysSignaled;
+ BOOL isSignaled;
+ DWORD boundThread;
+ BOOL linked;
+ BOOL markedForRemove;
+ WINPR_APC_ITEM *last, *next;
+};
+
+typedef enum
+{
+ APC_REMOVE_OK,
+ APC_REMOVE_ERROR,
+ APC_REMOVE_DELAY_FREE
+} APC_REMOVE_RESULT;
+
+typedef struct
+{
+ pthread_mutex_t mutex;
+ DWORD length;
+ WINPR_APC_ITEM *head, *tail;
+ BOOL treatingCompletions;
+} APC_QUEUE;
+
+BOOL apc_init(APC_QUEUE* apc);
+BOOL apc_uninit(APC_QUEUE* apc);
+void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem);
+APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item);
+BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled);
+int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex);
+void apc_cleanupThread(WINPR_THREAD* thread);
+#endif
+
+#endif /* WINPR_APC_H */
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Hewlett-Packard Development Company, L.P.
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <winpr/collections.h>
#include "thread.h"
+#include "apc.h"
#include "../handle/handle.h"
#include "../log.h"
#define TAG WINPR_TAG("thread")
+static WINPR_THREAD mainThread;
static wListDictionary* thread_list = NULL;
static BOOL ThreadCloseHandle(HANDLE handle);
if (!ThreadIsHandled(handle))
return -1;
- return pThread->pipe_fd[0];
+ return pThread->event.fds[0];
}
static DWORD ThreadCleanupHandle(HANDLE handle)
*/
static BOOL set_event(WINPR_THREAD* thread)
{
- int length;
- BOOL status = FALSE;
-#ifdef HAVE_SYS_EVENTFD_H
- eventfd_t val = 1;
-
- do
- {
- length = eventfd_write(thread->pipe_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;
+ return winpr_event_set(&thread->event);
}
static BOOL reset_event(WINPR_THREAD* thread)
{
- int length;
- BOOL status = FALSE;
-#ifdef HAVE_SYS_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;
+ return winpr_event_reset(&thread->event);
}
static BOOL thread_compare(const void* a, const void* b)
return rc;
}
+static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
+static pthread_t mainThreadId;
+static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
+
+BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
+{
+ if (!apc_init(&mainThread.apc))
+ {
+ WLog_ERR(TAG, "failed to initialize APC");
+ goto out;
+ }
+
+ mainThread.Type = HANDLE_TYPE_THREAD;
+ mainThreadId = pthread_self();
+
+ currentThreadTlsIndex = TlsAlloc();
+ if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
+ {
+ WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
+ }
+
+out:
+ return TRUE;
+}
+
/* Thread launcher function responsible for registering
* cleanup handlers and calling pthread_exit, if not done
* in thread function. */
goto exit;
}
+ if (!TlsSetValue(currentThreadTlsIndex, thread))
+ {
+ WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self());
+ goto exit;
+ }
+
if (!(fkt = thread->lpStartAddress))
{
WLog_ERR(TAG, "Thread function argument is %p", (void*)fkt);
if (thread)
{
+ apc_cleanupThread(thread);
+
if (!thread->exited)
thread->dwExitCode = rc;
thread->create_stack = winpr_backtrace(20);
dump_thread(thread);
#endif
- thread->pipe_fd[0] = -1;
- thread->pipe_fd[1] = -1;
-#ifdef HAVE_SYS_EVENTFD_H
- thread->pipe_fd[0] = eventfd(0, EFD_NONBLOCK);
- if (thread->pipe_fd[0] < 0)
+ if (!winpr_event_init(&thread->event))
{
- WLog_ERR(TAG, "failed to create thread pipe fd 0");
- goto error_pipefd0;
+ WLog_ERR(TAG, "failed to create event");
+ goto error_event;
}
-#else
-
- if (pipe(thread->pipe_fd) < 0)
+ if (pthread_mutex_init(&thread->mutex, NULL) != 0)
{
- WLog_ERR(TAG, "failed to create thread pipe");
- goto error_pipefd0;
+ WLog_ERR(TAG, "failed to initialize thread mutex");
+ goto error_mutex;
}
+ if (!apc_init(&thread->apc))
{
- int flags = fcntl(thread->pipe_fd[0], F_GETFL);
- fcntl(thread->pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
- }
-
-#endif
-
- if (pthread_mutex_init(&thread->mutex, 0) != 0)
- {
- WLog_ERR(TAG, "failed to initialize thread mutex");
- goto error_mutex;
+ WLog_ERR(TAG, "failed to initialize APC");
+ goto error_APC;
}
if (pthread_mutex_init(&thread->threadIsReadyMutex, NULL) != 0)
if (!thread_list)
{
+ InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
thread_list = ListDictionary_New(TRUE);
if (!thread_list)
error_thread_ready:
pthread_mutex_destroy(&thread->threadIsReadyMutex);
error_thread_ready_mutex:
+ apc_uninit(&thread->apc);
+error_APC:
pthread_mutex_destroy(&thread->mutex);
error_mutex:
-
- if (thread->pipe_fd[1] >= 0)
- close(thread->pipe_fd[1]);
-
- if (thread->pipe_fd[0] >= 0)
- close(thread->pipe_fd[0]);
-
-error_pipefd0:
+ winpr_event_uninit(&thread->event);
+error_event:
free(thread);
return NULL;
}
{
int rc;
WINPR_THREAD* thread = (WINPR_THREAD*)obj;
- rc = pthread_cond_destroy(&thread->threadIsReady);
+ if (!apc_uninit(&thread->apc))
+ WLog_ERR(TAG, "failed to destroy APC");
+
+ rc = pthread_cond_destroy(&thread->threadIsReady);
if (rc)
- WLog_ERR(TAG, "failed to destroy a condition variable [%d] %s (%d)", rc, strerror(errno),
+ WLog_ERR(TAG, "failed to destroy thread->threadIsReady [%d] %s (%d)", rc, strerror(errno),
errno);
rc = pthread_mutex_destroy(&thread->threadIsReadyMutex);
-
if (rc)
- WLog_ERR(TAG, "failed to destroy a condition variable mutex [%d] %s (%d)", rc,
+ WLog_ERR(TAG, "failed to destroy thread->threadIsReadyMutex [%d] %s (%d)", rc,
strerror(errno), errno);
rc = pthread_mutex_destroy(&thread->mutex);
-
if (rc)
- WLog_ERR(TAG, "failed to destroy mutex [%d] %s (%d)", rc, strerror(errno), errno);
+ WLog_ERR(TAG, "failed to destroy thread->mutex [%d] %s (%d)", rc, strerror(errno), errno);
- if (thread->pipe_fd[0] >= 0)
- close(thread->pipe_fd[0]);
-
- if (thread->pipe_fd[1] >= 0)
- close(thread->pipe_fd[1]);
+ winpr_event_uninit(&thread->event);
if (thread_list && ListDictionary_Contains(thread_list, &thread->thread))
ListDictionary_Remove(thread_list, &thread->thread);
return TRUE;
}
-HANDLE _GetCurrentThread(VOID)
+WINPR_THREAD* winpr_GetCurrentThread(VOID)
{
- HANDLE hdl = NULL;
- pthread_t tid = pthread_self();
+ WINPR_THREAD* ret;
- if (!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))
+ InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
+ if (mainThreadId == pthread_self())
+ return (HANDLE)&mainThread;
+
+ ret = TlsGetValue(currentThreadTlsIndex);
+ if (!ret)
{
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 ret;
+}
- return hdl;
+HANDLE _GetCurrentThread(VOID)
+{
+ return (HANDLE)winpr_GetCurrentThread();
}
DWORD GetCurrentThreadId(VOID)
return (DWORD)tid & 0xffffffffUL;
}
+typedef struct
+{
+ WINPR_APC_ITEM apc;
+ PAPCFUNC completion;
+ ULONG_PTR completionArg;
+} UserApcItem;
+
+void userAPC(LPVOID arg)
+{
+ UserApcItem* userApc = (UserApcItem*)arg;
+
+ userApc->completion(userApc->completionArg);
+
+ userApc->apc.markedForRemove = TRUE;
+}
+
+DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
+{
+ ULONG Type;
+ WINPR_HANDLE* Object;
+ WINPR_THREAD* thread;
+ WINPR_APC_ITEM* apc;
+ UserApcItem* apcItem;
+
+ if (!pfnAPC)
+ return 1;
+
+ if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
+ {
+ WLog_ERR(TAG, "hThread is not a thread");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (DWORD)0;
+ }
+ thread = (WINPR_THREAD*)Object;
+
+ apcItem = calloc(1, sizeof(*apcItem));
+ if (!apcItem)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (DWORD)0;
+ }
+
+ apc = &apcItem->apc;
+ apc->type = APC_TYPE_USER;
+ apc->markedForFree = TRUE;
+ apc->alwaysSignaled = TRUE;
+ apc->completion = userAPC;
+ apc->completionArgs = apc;
+ apcItem->completion = pfnAPC;
+ apcItem->completionArg = dwData;
+ apc_register(hThread, apc);
+ return 1;
+}
+
DWORD ResumeThread(HANDLE hThread)
{
ULONG Type;
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Hewlett-Packard Development Company, L.P.
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <winpr/thread.h>
#include "../handle/handle.h"
+#include "../synch/event.h"
+#include "apc.h"
typedef void* (*pthread_start_routine)(void*);
+typedef struct winpr_APC_item WINPR_APC_ITEM;
struct winpr_thread
{
WINPR_HANDLE_DEF();
BOOL started;
- int pipe_fd[2];
+ WINPR_EVENT_IMPL event;
BOOL mainProcess;
BOOL detached;
BOOL joined;
pthread_cond_t threadIsReady;
LPTHREAD_START_ROUTINE lpStartAddress;
LPSECURITY_ATTRIBUTES lpThreadAttributes;
+ APC_QUEUE apc;
#if defined(WITH_DEBUG_THREADS)
void* create_stack;
void* exit_stack;
};
typedef struct winpr_thread WINPR_THREAD;
+WINPR_THREAD* winpr_GetCurrentThread(VOID);
+
struct winpr_process
{
WINPR_HANDLE_DEF();