| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| org.gradle.jvmargs=-Xmx4096M | ||
| android.useAndroidX = true | ||
| android.enableJetifier = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,8 @@ winpr_module_add( | |
| event.c | ||
| init.c | ||
| mutex.c | ||
| pollset.c | ||
| pollset.h | ||
| semaphore.c | ||
| sleep.c | ||
| synch.h | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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_ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| #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") | ||
|
|
||
| #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_base); | ||
| FD_ZERO(&set->rset); | ||
| FD_ZERO(&set->wset_base); | ||
| 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_base); | ||
| FD_ZERO(&set->wset_base); | ||
| 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_base); | ||
| set->nread++; | ||
| } | ||
|
|
||
| if (mode & WINPR_FD_WRITE) | ||
| { | ||
| FD_SET(fd, &set->wset_base); | ||
| 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 = 0; | ||
| UINT64 dueTime, now; | ||
|
|
||
| now = GetTickCount64(); | ||
| if (dwMilliseconds == INFINITE) | ||
| dueTime = 0xFFFFFFFFFFFFFFFF; | ||
| else | ||
| dueTime = now + dwMilliseconds; | ||
|
|
||
| #ifdef HAVE_POLL_H | ||
| int timeout; | ||
|
|
||
| do | ||
| { | ||
| 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 0; /* timeout */ | ||
| } | ||
|
|
||
| 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 | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * 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 | ||
|
|
||
| #ifndef _WIN32 | ||
|
|
||
| #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_base; | ||
| fd_set rset; | ||
| fd_set wset_base; | ||
| 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 | ||
|
|
||
| #endif /* WINPR_LIBWINPR_SYNCH_POLLSET_H_ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } |