winpr: correctly implement APC
This patch implements APC functions and fixes waitable timers with completions.
This commit is contained in:
parent
dfbbf3b618
commit
aeba30a505
@ -213,6 +213,9 @@ extern "C"
|
||||
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);
|
||||
|
@ -41,14 +41,112 @@
|
||||
#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)
|
||||
@ -71,7 +169,7 @@ static int EventGetFd(HANDLE handle)
|
||||
if (!EventIsHandled(handle))
|
||||
return -1;
|
||||
|
||||
return event->pipe_fd[0];
|
||||
return event->impl.fds[0];
|
||||
}
|
||||
|
||||
static BOOL EventCloseHandle_(WINPR_EVENT* event)
|
||||
@ -80,19 +178,7 @@ 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);
|
||||
@ -161,21 +247,9 @@ HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
|
||||
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)
|
||||
if (!winpr_event_init(&event->impl))
|
||||
goto fail;
|
||||
|
||||
#else
|
||||
|
||||
if (pipe(event->pipe_fd) < 0)
|
||||
goto fail;
|
||||
|
||||
#endif
|
||||
|
||||
if (bInitialState)
|
||||
{
|
||||
if (!SetEvent(event))
|
||||
@ -246,25 +320,10 @@ HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
|
||||
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;
|
||||
@ -273,30 +332,7 @@ BOOL SetEvent(HANDLE hEvent)
|
||||
{
|
||||
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;
|
||||
@ -306,8 +342,6 @@ BOOL ResetEvent(HANDLE hEvent)
|
||||
{
|
||||
ULONG Type;
|
||||
WINPR_HANDLE* Object;
|
||||
int length;
|
||||
BOOL status = TRUE;
|
||||
WINPR_EVENT* event;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object))
|
||||
@ -315,23 +349,7 @@ BOOL ResetEvent(HANDLE hEvent)
|
||||
|
||||
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
|
||||
@ -348,8 +366,7 @@ HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL
|
||||
{
|
||||
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;
|
||||
@ -416,12 +433,12 @@ int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
|
||||
|
||||
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;
|
||||
|
56
winpr/libwinpr/synch/event.h
Normal file
56
winpr/libwinpr/synch/event.h
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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_ */
|
@ -1,7 +1,9 @@
|
||||
#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")
|
||||
@ -41,7 +43,9 @@ BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles)
|
||||
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;
|
||||
@ -65,8 +69,8 @@ void pollset_uninit(WINPR_POLL_SET* set)
|
||||
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
|
||||
@ -88,13 +92,13 @@ BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode)
|
||||
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++;
|
||||
}
|
||||
|
||||
@ -110,35 +114,85 @@ BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode)
|
||||
|
||||
int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds)
|
||||
{
|
||||
int ret;
|
||||
#ifdef HAVE_POLL_H
|
||||
do
|
||||
{
|
||||
ret = poll(set->pollset, set->fillIndex, dwMilliseconds);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
#else
|
||||
struct timeval staticTimeout;
|
||||
struct timeval* timeout;
|
||||
int ret = 0;
|
||||
UINT64 dueTime, now;
|
||||
|
||||
if (dwMilliseconds == INFINITE || dwMilliseconds == 0)
|
||||
{
|
||||
timeout = NULL;
|
||||
}
|
||||
now = GetTickCount64();
|
||||
if (dwMilliseconds == INFINITE)
|
||||
dueTime = 0xFFFFFFFFFFFFFFFF;
|
||||
else
|
||||
{
|
||||
timeout = &staticTimeout;
|
||||
timeout->tv_sec = dwMilliseconds / 1000;
|
||||
timeout->tv_usec = (dwMilliseconds % 1000) * 1000;
|
||||
}
|
||||
dueTime = now + dwMilliseconds;
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
int timeout;
|
||||
|
||||
do
|
||||
{
|
||||
ret = select(set->maxFd + 1, set->nread ? &set->rset : NULL,
|
||||
set->nwrite ? &set->wset : NULL, NULL, timeout);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
if (dwMilliseconds == INFINITE)
|
||||
timeout = -1;
|
||||
else
|
||||
timeout = (int)(dueTime - now);
|
||||
|
||||
ret = poll(set->pollset, set->fillIndex, timeout);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (errno != EINTR)
|
||||
return -1;
|
||||
|
||||
now = GetTickCount64();
|
||||
} while (now < dueTime);
|
||||
|
||||
#else
|
||||
do
|
||||
{
|
||||
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)
|
||||
@ -166,3 +220,4 @@ BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx)
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#else
|
||||
@ -46,7 +48,9 @@ struct winpr_poll_set
|
||||
BOOL isStatic;
|
||||
#else
|
||||
FdIndex* fdIndex;
|
||||
fd_set rset_base;
|
||||
fd_set rset;
|
||||
fd_set wset_base;
|
||||
fd_set wset;
|
||||
int nread, nwrite;
|
||||
int maxFd;
|
||||
@ -64,4 +68,6 @@ BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode);
|
||||
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_ */
|
||||
|
@ -26,6 +26,9 @@
|
||||
#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")
|
||||
|
||||
@ -47,11 +50,64 @@ VOID Sleep(DWORD dwMilliseconds)
|
||||
|
||||
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
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
#include "../thread/apc.h"
|
||||
#include "event.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
@ -65,26 +67,22 @@ struct winpr_semaphore
|
||||
};
|
||||
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
|
||||
@ -98,17 +96,25 @@ 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;
|
||||
|
||||
|
@ -15,7 +15,8 @@ set(${MODULE_PREFIX}_TESTS
|
||||
TestSynchMultipleThreads.c
|
||||
TestSynchTimerQueue.c
|
||||
TestSynchWaitableTimer.c
|
||||
TestSynchWaitableTimerAPC.c)
|
||||
TestSynchWaitableTimerAPC.c
|
||||
TestSynchAPC.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS
|
||||
${${MODULE_PREFIX}_DRIVER}
|
||||
|
174
winpr/libwinpr/synch/test/TestSynchAPC.c
Normal file
174
winpr/libwinpr/synch/test/TestSynchAPC.c
Normal file
@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -7,9 +7,7 @@
|
||||
|
||||
static DWORD WINAPI test_thread(LPVOID arg)
|
||||
{
|
||||
long timeout = rand();
|
||||
timeout %= 1000;
|
||||
timeout += 100;
|
||||
long timeout = 100 + (rand() % 1000);
|
||||
Sleep(timeout);
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
@ -54,14 +52,16 @@ int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
#define THREADS 24
|
||||
DWORD rc = 0, ev, i;
|
||||
HANDLE threads[THREADS];
|
||||
DWORD ret;
|
||||
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(THREADS, threads))
|
||||
return 1;
|
||||
|
||||
if (WaitForMultipleObjects(THREADS, threads, TRUE, 50) != WAIT_TIMEOUT)
|
||||
ret = WaitForMultipleObjects(THREADS, threads, TRUE, 50);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForMultipleObjects bWaitAll, timeout 50 failed\n");
|
||||
printf("WaitForMultipleObjects bWaitAll, timeout 50 failed, ret=%d\n", ret);
|
||||
rc = 2;
|
||||
}
|
||||
|
||||
@ -82,7 +82,6 @@ int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
return 5;
|
||||
|
||||
ev = WaitForMultipleObjects(THREADS, threads, FALSE, INFINITE);
|
||||
|
||||
if (ev > (WAIT_OBJECT_0 + THREADS))
|
||||
{
|
||||
printf("WaitForMultipleObjects INFINITE failed\n");
|
||||
@ -105,9 +104,10 @@ int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
if (start_threads(THREADS, threads))
|
||||
return 9;
|
||||
|
||||
if (WaitForMultipleObjects(THREADS, threads, FALSE, 50) != WAIT_TIMEOUT)
|
||||
ret = WaitForMultipleObjects(THREADS, threads, FALSE, 50);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForMultipleObjects timeout 50 failed\n");
|
||||
printf("WaitForMultipleObjects timeout 50 failed, ret=%d\n", ret);
|
||||
rc = 10;
|
||||
}
|
||||
|
||||
@ -129,9 +129,10 @@ int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
|
||||
for (i = 0; i < THREADS; i++)
|
||||
{
|
||||
if (WaitForMultipleObjects(THREADS, threads, FALSE, 0) != WAIT_TIMEOUT)
|
||||
ret = WaitForMultipleObjects(THREADS, threads, FALSE, 0);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForMultipleObjects timeout 50 failed\n");
|
||||
printf("WaitForMultipleObjects timeout 50 failed, ret=%d\n", ret);
|
||||
rc = 15;
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ static VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwT
|
||||
int TestSynchWaitableTimerAPC(int argc, char* argv[])
|
||||
{
|
||||
int status = -1;
|
||||
DWORD rc;
|
||||
HANDLE hTimer = NULL;
|
||||
BOOL bSuccess;
|
||||
LARGE_INTEGER due;
|
||||
@ -50,40 +51,24 @@ int TestSynchWaitableTimerAPC(int argc, char* argv[])
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
* 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.
|
||||
@ -34,11 +35,13 @@
|
||||
#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")
|
||||
@ -80,11 +83,15 @@ static DWORD TimerCleanupHandle(HANDLE handle)
|
||||
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)
|
||||
{
|
||||
@ -105,10 +112,31 @@ static DWORD TimerCleanupHandle(HANDLE handle)
|
||||
|
||||
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;
|
||||
@ -117,96 +145,97 @@ BOOL TimerCloseHandle(HANDLE handle)
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#ifdef TIMER_IMPL_POSIX
|
||||
timer_delete(timer->tid);
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH)
|
||||
winpr_event_uninit(&timer->event);
|
||||
#endif
|
||||
|
||||
free(timer->name);
|
||||
if (timer->apcItem.linked)
|
||||
{
|
||||
TimerDeleter* deleter;
|
||||
WINPR_APC_ITEM* apcItem;
|
||||
|
||||
switch (apc_remove(&timer->apcItem))
|
||||
{
|
||||
case APC_REMOVE_OK:
|
||||
break;
|
||||
case APC_REMOVE_DELAY_FREE:
|
||||
{
|
||||
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 WITH_POSIX_TIMER
|
||||
#ifdef TIMER_IMPL_POSIX
|
||||
|
||||
static BOOL g_WaitableTimerSignalHandlerInstalled = FALSE;
|
||||
|
||||
static void WaitableTimerHandler(void* arg)
|
||||
{
|
||||
WINPR_TIMER* timer = (WINPR_TIMER*)arg;
|
||||
|
||||
if (!timer)
|
||||
return;
|
||||
|
||||
if (timer->pfnCompletionRoutine)
|
||||
{
|
||||
timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
|
||||
|
||||
if (timer->lPeriod)
|
||||
{
|
||||
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)
|
||||
{
|
||||
WLog_ERR(TAG, "timer_settime");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
static INIT_ONCE TimerSignalHandler_InitOnce = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
static BOOL InstallTimerSignalHandler(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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;
|
||||
@ -215,10 +244,7 @@ static void WaitableTimerHandler(void* arg)
|
||||
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)
|
||||
@ -235,47 +261,45 @@ static int InitializeWaitableTimer(WINPR_TIMER* timer)
|
||||
{
|
||||
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,
|
||||
@ -317,11 +341,12 @@ HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManua
|
||||
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)
|
||||
@ -334,16 +359,12 @@ HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManua
|
||||
|
||||
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;
|
||||
@ -394,6 +415,36 @@ HANDLE CreateWaitableTimerExW(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR l
|
||||
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)
|
||||
@ -401,13 +452,9 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
|
||||
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;
|
||||
@ -438,7 +485,7 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
|
||||
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)
|
||||
@ -475,30 +522,24 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
|
||||
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;
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "timerfd_settime failure: %d", status);
|
||||
return FALSE;
|
||||
}
|
||||
#else
|
||||
status = timer_settime(timer->tid, 0, &(timer->timeout), NULL);
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "timer_settime failure");
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "timer_settime");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#ifdef TIMER_IMPL_DISPATCH
|
||||
if (lpDueTime->QuadPart < 0)
|
||||
{
|
||||
LONGLONG due = lpDueTime->QuadPart * (-1);
|
||||
@ -516,12 +557,9 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
|
||||
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");
|
||||
}
|
||||
|
||||
{
|
||||
@ -538,8 +576,36 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -26,20 +26,13 @@
|
||||
#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"
|
||||
@ -122,6 +115,11 @@ int _mach_safe_clock_gettime(int clk_id, struct timespec* t)
|
||||
|
||||
#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;
|
||||
@ -129,11 +127,6 @@ static long long ts_difftime(const struct timespec* o, const struct timespec* n)
|
||||
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;
|
||||
@ -162,7 +155,6 @@ static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
|
||||
{
|
||||
ts->tv_sec += dwMilliseconds / 1000L;
|
||||
@ -171,11 +163,11 @@ static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
|
||||
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))
|
||||
{
|
||||
@ -225,9 +217,30 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
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!");
|
||||
@ -235,7 +248,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
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);
|
||||
@ -244,59 +257,67 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
|
||||
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);
|
||||
if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to collect APC fds");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!autoSignaled)
|
||||
{
|
||||
status = pollset_poll(&pollset, dwMilliseconds);
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "waitOnFd() failure [%d] %s", errno, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = WAIT_TIMEOUT;
|
||||
if (bAlertable && apc_executeCompletions(thread, &pollset, 1))
|
||||
ret = WAIT_IO_COMPLETION;
|
||||
|
||||
isSet = pollset_isSignaled(&pollset, 0);
|
||||
pollset_uninit(&pollset);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "waitOnFd() failure [%d] %s", errno, strerror(errno));
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (status != 1)
|
||||
return WAIT_TIMEOUT;
|
||||
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))
|
||||
{
|
||||
@ -304,34 +325,55 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (!pollset_init(&pollset, nCount))
|
||||
if (bAlertable)
|
||||
{
|
||||
WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 "", nCount);
|
||||
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 " 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;
|
||||
@ -362,115 +404,127 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
|
||||
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;
|
||||
DWORD waitTime;
|
||||
|
||||
if (dwMilliseconds == INFINITE)
|
||||
waitTime = INFINITE;
|
||||
else
|
||||
waitTime = (DWORD)(dueTime - now);
|
||||
|
||||
status = pollset_poll(&pollset, waitTime);
|
||||
if (status < 0)
|
||||
{
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
/* give priority to the APC queue, to return WAIT_IO_COMPLETION */
|
||||
if (bAlertable && apc_executeCompletions(thread, &pollset, polled))
|
||||
{
|
||||
ret = WAIT_IO_COMPLETION;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bWaitAll && (dwMilliseconds != INFINITE))
|
||||
/* then treat pollset */
|
||||
if (status)
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &timenow);
|
||||
diff = ts_difftime(&timenow, &starttime);
|
||||
|
||||
if (diff / 1000 > dwMilliseconds)
|
||||
for (index = 0; index < polled; index++)
|
||||
{
|
||||
ret = WAIT_TIMEOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dwMilliseconds -= (diff / 1000);
|
||||
}
|
||||
|
||||
signal_handled = FALSE;
|
||||
|
||||
for (index = 0; index < polled; index++)
|
||||
{
|
||||
DWORD idx;
|
||||
BOOL signal_set = FALSE;
|
||||
|
||||
if (bWaitAll)
|
||||
idx = poll_map[index];
|
||||
else
|
||||
idx = index;
|
||||
|
||||
signal_set = pollset_isSignaled(&pollset, index);
|
||||
|
||||
if (signal_set)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (!bWaitAll)
|
||||
{
|
||||
ret = (WAIT_OBJECT_0 + index);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (signalled >= nCount)
|
||||
{
|
||||
ret = WAIT_OBJECT_0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
signal_handled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
pollset_reset(&pollset);
|
||||
} while (bWaitAll || !signal_handled);
|
||||
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);
|
||||
|
||||
WLog_ERR(TAG, "failed (unknown error)");
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
now = GetTickCount64();
|
||||
} while (now < dueTime);
|
||||
|
||||
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,
|
||||
|
@ -16,6 +16,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
winpr_module_add(
|
||||
apc.h
|
||||
apc.c
|
||||
argv.c
|
||||
process.c
|
||||
processor.c
|
||||
|
244
winpr/libwinpr/thread/apc.c
Normal file
244
winpr/libwinpr/thread/apc.c
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* 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
|
85
winpr/libwinpr/thread/apc.h
Normal file
85
winpr/libwinpr/thread/apc.h
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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 */
|
@ -4,6 +4,8 @@
|
||||
*
|
||||
* 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.
|
||||
@ -89,11 +91,13 @@
|
||||
#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);
|
||||
@ -119,7 +123,7 @@ static int ThreadGetFd(HANDLE handle)
|
||||
if (!ThreadIsHandled(handle))
|
||||
return -1;
|
||||
|
||||
return pThread->pipe_fd[0];
|
||||
return pThread->event.fds[0];
|
||||
}
|
||||
|
||||
static DWORD ThreadCleanupHandle(HANDLE handle)
|
||||
@ -224,58 +228,12 @@ static void dump_thread(WINPR_THREAD* thread)
|
||||
*/
|
||||
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)
|
||||
@ -286,6 +244,31 @@ 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. */
|
||||
@ -301,6 +284,12 @@ static void* thread_launcher(void* arg)
|
||||
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);
|
||||
@ -329,6 +318,8 @@ exit:
|
||||
|
||||
if (thread)
|
||||
{
|
||||
apc_cleanupThread(thread);
|
||||
|
||||
if (!thread->exited)
|
||||
thread->dwExitCode = rc;
|
||||
|
||||
@ -404,38 +395,25 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize
|
||||
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)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create thread pipe");
|
||||
goto error_pipefd0;
|
||||
}
|
||||
|
||||
{
|
||||
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)
|
||||
if (pthread_mutex_init(&thread->mutex, NULL) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to initialize thread mutex");
|
||||
goto error_mutex;
|
||||
}
|
||||
|
||||
if (!apc_init(&thread->apc))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to initialize APC");
|
||||
goto error_APC;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&thread->threadIsReadyMutex, NULL) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to initialize a mutex for a condition variable");
|
||||
@ -453,6 +431,7 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize
|
||||
|
||||
if (!thread_list)
|
||||
{
|
||||
InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
|
||||
thread_list = ListDictionary_New(TRUE);
|
||||
|
||||
if (!thread_list)
|
||||
@ -481,16 +460,12 @@ error_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;
|
||||
}
|
||||
@ -499,28 +474,25 @@ void cleanup_handle(void* obj)
|
||||
{
|
||||
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);
|
||||
@ -645,31 +617,28 @@ BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
|
||||
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)
|
||||
@ -681,6 +650,60 @@ 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;
|
||||
|
@ -4,6 +4,7 @@
|
||||
*
|
||||
* 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.
|
||||
@ -28,15 +29,18 @@
|
||||
#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;
|
||||
@ -50,6 +54,7 @@ struct winpr_thread
|
||||
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;
|
||||
@ -57,6 +62,8 @@ struct winpr_thread
|
||||
};
|
||||
typedef struct winpr_thread WINPR_THREAD;
|
||||
|
||||
WINPR_THREAD* winpr_GetCurrentThread(VOID);
|
||||
|
||||
struct winpr_process
|
||||
{
|
||||
WINPR_HANDLE_DEF();
|
||||
|
Loading…
Reference in New Issue
Block a user