Externalize all the polling logic in a pollset component. This patch prepares the
support of APC and alertable state.

(cherry picked from commit dfbbf3b618)
This commit is contained in:
David Fort 2021-03-16 15:15:13 +01:00 коммит произвёл akallabeth
Родитель 28e6ca5082
Коммит 225285ae36
6 изменённых файлов: 314 добавлений и 175 удалений

Просмотреть файл

@ -174,6 +174,7 @@ extern "C"
#define WAIT_OBJECT_0 0x00000000L
#define WAIT_ABANDONED 0x00000080L
#define WAIT_IO_COMPLETION 0x000000C0L
#ifndef WAIT_TIMEOUT
#define WAIT_TIMEOUT 0x00000102L
@ -202,7 +203,8 @@ extern "C"
ULONG Version;
DWORD Flags;
union {
union
{
struct
{
HMODULE LocalizedReasonModule;

Просмотреть файл

@ -22,6 +22,8 @@ winpr_module_add(
event.c
init.c
mutex.c
pollset.c
pollset.h
semaphore.c
sleep.c
synch.h

Просмотреть файл

@ -0,0 +1,168 @@
#include <errno.h>
#include "pollset.h"
#include <winpr/handle.h>
#include "../log.h"
#define TAG WINPR_TAG("sync.pollset")
#ifdef HAVE_POLL_H
static DWORD handle_mode_to_pollevent(ULONG mode)
{
DWORD event = 0;
if (mode & WINPR_FD_READ)
event |= POLLIN;
if (mode & WINPR_FD_WRITE)
event |= POLLOUT;
return event;
}
#endif
BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles)
{
#ifdef HAVE_POLL_H
if (nhandles > MAXIMUM_WAIT_OBJECTS)
{
set->isStatic = FALSE;
set->pollset = calloc(nhandles, sizeof(*set->pollset));
if (!set->pollset)
return FALSE;
}
else
{
set->pollset = set->staticSet;
set->isStatic = TRUE;
}
#else
set->fdIndex = calloc(nhandles, sizeof(*set->fdIndex));
if (!set->fdIndex)
return FALSE;
FD_ZERO(&set->rset);
FD_ZERO(&set->wset);
set->maxFd = 0;
set->nread = set->nwrite = 0;
#endif
set->size = nhandles;
set->fillIndex = 0;
return TRUE;
}
void pollset_uninit(WINPR_POLL_SET* set)
{
#ifdef HAVE_POLL_H
if (!set->isStatic)
free(set->pollset);
#else
free(set->fdIndex);
#endif
}
void pollset_reset(WINPR_POLL_SET* set)
{
#ifndef HAVE_POLL_H
FD_ZERO(&set->rset);
FD_ZERO(&set->wset);
set->maxFd = 0;
set->nread = set->nwrite = 0;
#endif
set->fillIndex = 0;
}
BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode)
{
#ifdef HAVE_POLL_H
struct pollfd* item;
if (set->fillIndex == set->size)
return FALSE;
item = &set->pollset[set->fillIndex];
item->fd = fd;
item->revents = 0;
item->events = handle_mode_to_pollevent(mode);
#else
FdIndex* fdIndex = &set->fdIndex[set->fillIndex];
if (mode & WINPR_FD_READ)
{
FD_SET(fd, &set->rset);
set->nread++;
}
if (mode & WINPR_FD_WRITE)
{
FD_SET(fd, &set->wset);
set->nwrite++;
}
if (fd > set->maxFd)
set->maxFd = fd;
fdIndex->fd = fd;
fdIndex->mode = mode;
#endif
set->fillIndex++;
return TRUE;
}
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;
if (dwMilliseconds == INFINITE || dwMilliseconds == 0)
{
timeout = NULL;
}
else
{
timeout = &staticTimeout;
timeout->tv_sec = dwMilliseconds / 1000;
timeout->tv_usec = (dwMilliseconds % 1000) * 1000;
}
do
{
ret = select(set->maxFd + 1, set->nread ? &set->rset : NULL,
set->nwrite ? &set->wset : NULL, NULL, timeout);
} while (ret < 0 && errno == EINTR);
#endif
return ret;
}
BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx)
{
if (idx > set->fillIndex)
{
WLog_ERR(TAG, "%s: index=%d out of pollset(fillIndex=%d)", __FUNCTION__, idx,
set->fillIndex);
return FALSE;
}
#ifdef HAVE_POLL_H
return !!(set->pollset[idx].revents & set->pollset[idx].events);
#else
FdIndex* fdIndex = &set->fdIndex[idx];
if (fdIndex->fd < 0)
return FALSE;
if ((fdIndex->mode & WINPR_FD_READ) && FD_ISSET(fdIndex->fd, &set->rset))
return TRUE;
if ((fdIndex->mode & WINPR_FD_WRITE) && FD_ISSET(fdIndex->fd, &set->wset))
return TRUE;
return FALSE;
#endif
}

Просмотреть файл

@ -0,0 +1,67 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* pollset
*
* 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_POLLSET_H_
#define WINPR_LIBWINPR_SYNCH_POLLSET_H_
#include <winpr/wtypes.h>
#include <winpr/synch.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#else
#include <sys/select.h>
typedef struct
{
int fd;
ULONG mode;
} FdIndex;
#endif
struct winpr_poll_set
{
#ifdef HAVE_POLL_H
struct pollfd* pollset;
struct pollfd staticSet[MAXIMUM_WAIT_OBJECTS];
BOOL isStatic;
#else
FdIndex* fdIndex;
fd_set rset;
fd_set wset;
int nread, nwrite;
int maxFd;
#endif
size_t fillIndex;
size_t size;
};
typedef struct winpr_poll_set WINPR_POLL_SET;
BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles);
void pollset_uninit(WINPR_POLL_SET* set);
void pollset_reset(WINPR_POLL_SET* set);
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 /* WINPR_LIBWINPR_SYNCH_POLLSET_H_ */

Просмотреть файл

@ -87,7 +87,7 @@ int TestSynchWaitableTimerAPC(int argc, char* argv[])
if (rc == WAIT_OBJECT_0)
break;
if (rc == 0x000000C0L) /* WAIT_IO_COMPLETION */
if (rc == WAIT_IO_COMPLETION)
continue;
printf("Failed to wait for completion event (%" PRIu32 ")\n", GetLastError());

Просмотреть файл

@ -42,6 +42,7 @@
#include <winpr/platform.h>
#include "synch.h"
#include "pollset.h"
#include "../thread/thread.h"
#include <winpr/thread.h>
#include <winpr/debug.h>
@ -161,20 +162,6 @@ static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec
}
#endif
#ifdef HAVE_POLL_H
static DWORD handle_mode_to_pollevent(ULONG mode)
{
DWORD event = 0;
if (mode & WINPR_FD_READ)
event |= POLLIN;
if (mode & WINPR_FD_WRITE)
event |= POLLOUT;
return event;
}
#endif
static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
{
@ -184,53 +171,6 @@ static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
ts->tv_nsec = ts->tv_nsec % 1000000000L;
}
static int waitOnFd(int fd, ULONG mode, DWORD dwMilliseconds)
{
int status;
#ifdef HAVE_POLL_H
struct pollfd pollfds;
pollfds.fd = fd;
pollfds.events = handle_mode_to_pollevent(mode);
pollfds.revents = 0;
do
{
status = poll(&pollfds, 1, dwMilliseconds);
} while ((status < 0) && (errno == EINTR));
#else
struct timeval timeout;
fd_set rfds, wfds;
fd_set* prfds = NULL;
fd_set* pwfds = NULL;
fd_set* pefds = NULL;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
ZeroMemory(&timeout, sizeof(timeout));
if (mode & WINPR_FD_READ)
prfds = &rfds;
if (mode & WINPR_FD_WRITE)
pwfds = &wfds;
if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0))
{
timeout.tv_sec = dwMilliseconds / 1000;
timeout.tv_usec = (dwMilliseconds % 1000) * 1000;
}
do
{
status =
select(fd + 1, prfds, pwfds, pefds, (dwMilliseconds == INFINITE) ? NULL : &timeout);
} while (status < 0 && (errno == EINTR));
#endif
return status;
}
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
{
@ -246,8 +186,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
if (Type == HANDLE_TYPE_PROCESS)
{
WINPR_PROCESS* process;
process = (WINPR_PROCESS*)Object;
WINPR_PROCESS* process = (WINPR_PROCESS*)Object;
if (process->pid != waitpid(process->pid, &(process->status), 0))
{
@ -259,7 +198,8 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
process->dwExitCode = (DWORD)process->status;
return WAIT_OBJECT_0;
}
else if (Type == HANDLE_TYPE_MUTEX)
if (Type == HANDLE_TYPE_MUTEX)
{
WINPR_MUTEX* mutex;
mutex = (WINPR_MUTEX*)Object;
@ -285,6 +225,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
else
{
int status;
WINPR_POLL_SET pollset;
int fd = winpr_Handle_getFd(Object);
if (fd < 0)
@ -294,7 +235,21 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
return WAIT_FAILED;
}
status = waitOnFd(fd, Object->Mode, dwMilliseconds);
if (!pollset_init(&pollset, 1))
{
WLog_ERR(TAG, "unable to initialize pollset");
SetLastError(ERROR_INTERNAL_ERROR);
return WAIT_FAILED;
}
if (!pollset_add(&pollset, fd, Object->Mode))
{
pollset_uninit(&pollset);
return WAIT_FAILED;
}
status = pollset_poll(&pollset, dwMilliseconds);
pollset_uninit(&pollset);
if (status < 0)
{
@ -340,14 +295,8 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
ULONG Type;
BOOL signal_handled = FALSE;
WINPR_HANDLE* Object;
#ifdef HAVE_POLL_H
struct pollfd* pollfds;
#else
int maxfd;
fd_set rfds;
fd_set wfds;
struct timeval timeout;
#endif
WINPR_POLL_SET pollset;
DWORD ret = WAIT_FAILED;
if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS))
{
@ -355,6 +304,12 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
return WAIT_FAILED;
}
if (!pollset_init(&pollset, nCount))
{
WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 "", nCount);
return WAIT_FAILED;
}
if (bWaitAll)
{
signalled_idx = alloca(nCount * sizeof(BOOL));
@ -363,25 +318,13 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
memset(poll_map, 0, nCount * sizeof(DWORD));
}
#ifdef HAVE_POLL_H
pollfds = alloca(nCount * sizeof(struct pollfd));
#endif
signalled = 0;
if (bWaitAll && (dwMilliseconds != INFINITE))
clock_gettime(CLOCK_MONOTONIC, &starttime);
do
{
#ifndef HAVE_POLL_H
fd_set* prfds = NULL;
fd_set* pwfds = NULL;
maxfd = 0;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
ZeroMemory(&timeout, sizeof(timeout));
#endif
if (bWaitAll && (dwMilliseconds != INFINITE))
clock_gettime(CLOCK_MONOTONIC, &starttime);
polled = 0;
for (index = 0; index < nCount; index++)
@ -398,66 +341,28 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
{
WLog_ERR(TAG, "invalid event file descriptor");
SetLastError(ERROR_INVALID_HANDLE);
return WAIT_FAILED;
goto out;
}
fd = winpr_Handle_getFd(Object);
if (fd == -1)
{
WLog_ERR(TAG, "invalid file descriptor");
SetLastError(ERROR_INVALID_HANDLE);
return WAIT_FAILED;
goto out;
}
#ifdef HAVE_POLL_H
pollfds[polled].fd = fd;
pollfds[polled].events = handle_mode_to_pollevent(Object->Mode);
pollfds[polled].revents = 0;
#else
if (Object->Mode & WINPR_FD_READ)
if (!pollset_add(&pollset, fd, Object->Mode))
{
FD_SET(fd, &rfds);
prfds = &rfds;
WLog_ERR(TAG, "unable to register fd in pollset");
SetLastError(ERROR_INVALID_HANDLE);
goto out;
}
if (Object->Mode & WINPR_FD_WRITE)
{
FD_SET(fd, &wfds);
pwfds = &wfds;
}
if (fd > maxfd)
maxfd = fd;
#endif
polled++;
}
#ifdef HAVE_POLL_H
do
{
status = poll(pollfds, polled, dwMilliseconds);
} while (status < 0 && errno == EINTR);
#else
if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0))
{
timeout.tv_sec = dwMilliseconds / 1000;
timeout.tv_usec = (dwMilliseconds % 1000) * 1000;
}
do
{
status =
select(maxfd + 1, prfds, pwfds, 0, (dwMilliseconds == INFINITE) ? NULL : &timeout);
} while (status < 0 && errno == EINTR);
#endif
status = pollset_poll(&pollset, dwMilliseconds);
if (status < 0)
{
#ifdef HAVE_POLL_H
@ -469,11 +374,14 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
#endif
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
SetLastError(ERROR_INTERNAL_ERROR);
return WAIT_FAILED;
goto out;
}
if (status == 0)
return WAIT_TIMEOUT;
{
ret = WAIT_TIMEOUT;
goto out;
}
if (bWaitAll && (dwMilliseconds != INFINITE))
{
@ -481,9 +389,12 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
diff = ts_difftime(&timenow, &starttime);
if (diff / 1000 > dwMilliseconds)
return WAIT_TIMEOUT;
else
dwMilliseconds -= (diff / 1000);
{
ret = WAIT_TIMEOUT;
goto out;
}
dwMilliseconds -= (diff / 1000);
}
signal_handled = FALSE;
@ -498,40 +409,17 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
else
idx = index;
if (!winpr_Handle_GetInfo(lpHandles[idx], &Type, &Object))
{
WLog_ERR(TAG, "invalid hHandle.");
SetLastError(ERROR_INVALID_HANDLE);
return WAIT_FAILED;
}
fd = winpr_Handle_getFd(lpHandles[idx]);
if (fd == -1)
{
WLog_ERR(TAG, "invalid file descriptor");
SetLastError(ERROR_INVALID_HANDLE);
return WAIT_FAILED;
}
#ifdef HAVE_POLL_H
signal_set = pollfds[index].revents & pollfds[index].events;
#else
if (Object->Mode & WINPR_FD_READ)
signal_set = FD_ISSET(fd, &rfds) ? 1 : 0;
if (Object->Mode & WINPR_FD_WRITE)
signal_set |= FD_ISSET(fd, &wfds) ? 1 : 0;
#endif
signal_set = pollset_isSignaled(&pollset, index);
if (signal_set)
{
DWORD rc = winpr_Handle_cleanup(lpHandles[idx]);
if (rc != WAIT_OBJECT_0)
return rc;
{
WLog_ERR(TAG, "error in cleanup function for handle at index=%d", idx);
ret = rc;
goto out;
}
if (bWaitAll)
{
@ -546,19 +434,30 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
}
if (!bWaitAll)
return (WAIT_OBJECT_0 + index);
{
ret = (WAIT_OBJECT_0 + index);
goto out;
}
if (signalled >= nCount)
return (WAIT_OBJECT_0);
{
ret = WAIT_OBJECT_0;
goto out;
}
signal_handled = TRUE;
}
}
pollset_reset(&pollset);
} while (bWaitAll || !signal_handled);
WLog_ERR(TAG, "failed (unknown error)");
SetLastError(ERROR_INTERNAL_ERROR);
return WAIT_FAILED;
out:
pollset_uninit(&pollset);
return ret;
}
DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
@ -577,9 +476,10 @@ DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWait
DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds,
BOOL bAlertable)
{
WLog_ERR(TAG, "%s: Not implemented.", __FUNCTION__);
SetLastError(ERROR_NOT_SUPPORTED);
return WAIT_FAILED;
if (!SetEvent(hObjectToSignal))
return WAIT_FAILED;
return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
}
#endif