21 #include <winpr/config.h>
23 #ifdef WINPR_HAVE_UNISTD_H
27 #include <winpr/assert.h>
30 #include <winpr/crt.h>
31 #include <winpr/synch.h>
32 #include <winpr/platform.h>
33 #include <winpr/sysinfo.h>
37 #include "../thread/thread.h"
38 #include <winpr/thread.h>
39 #include <winpr/debug.h>
42 #define TAG WINPR_TAG("sync.wait")
58 #include "../handle/handle.h"
60 #include "../pipe/pipe.h"
62 static struct timespec ts_from_ns(void)
64 const UINT64 ns = winpr_GetUnixTimeNS();
65 struct timespec timeout = { 0 };
66 timeout.tv_sec = WINPR_TIME_NS_TO_S(ns);
67 timeout.tv_nsec = WINPR_TIME_NS_REM_NS(ns);
76 #if !defined(WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK)
79 static long long ts_difftime(
const struct timespec* o,
const struct timespec* n)
81 long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec;
82 long long newValue = n->tv_sec * 1000000000LL + n->tv_nsec;
83 return newValue - oldValue;
87 #if (__ANDROID_API__ >= 21)
88 #define CONST_NEEDED const
94 #define CONST_NEEDED const
95 #define STATIC_NEEDED static
98 STATIC_NEEDED
int pthread_mutex_timedlock(pthread_mutex_t* mutex,
99 CONST_NEEDED
struct timespec* timeout)
101 struct timespec timenow = { 0 };
102 struct timespec sleepytime = { 0 };
103 unsigned long long diff = 0;
106 timenow = ts_from_ns();
107 diff = ts_difftime(&timenow, timeout);
108 sleepytime.tv_sec = diff / 1000000000LL;
109 sleepytime.tv_nsec = diff % 1000000000LL;
111 while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY)
113 timenow = ts_from_ns();
115 if (ts_difftime(timeout, &timenow) >= 0)
120 nanosleep(&sleepytime, NULL);
127 static void ts_add_ms(
struct timespec* ts, DWORD dwMilliseconds)
129 ts->tv_sec += dwMilliseconds / 1000L;
130 ts->tv_nsec += (dwMilliseconds % 1000L) * 1000000L;
131 ts->tv_sec += ts->tv_nsec / 1000000000L;
132 ts->tv_nsec = ts->tv_nsec % 1000000000L;
135 DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
139 WINPR_POLL_SET pollset = { 0 };
141 if (!winpr_Handle_GetInfo(hHandle, &Type, &Object))
143 WLog_ERR(TAG,
"invalid hHandle.");
144 SetLastError(ERROR_INVALID_HANDLE);
148 if (Type == HANDLE_TYPE_PROCESS && winpr_Handle_getFd(hHandle) == -1)
159 int ret = waitpid(process->pid, &(process->status), WNOHANG);
160 if (ret == process->pid)
162 process->dwExitCode = (DWORD)process->status;
163 return WAIT_OBJECT_0;
167 char ebuffer[256] = { 0 };
168 WLog_ERR(TAG,
"waitpid failure [%d] %s", errno,
169 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
170 SetLastError(ERROR_INTERNAL_ERROR);
175 waitDelay = (dwMilliseconds < 50) ? dwMilliseconds : 50;
177 status = SleepEx(waitDelay, bAlertable);
181 dwMilliseconds -= waitDelay;
183 }
while (dwMilliseconds > 50);
188 if (Type == HANDLE_TYPE_MUTEX)
190 WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object;
192 if (dwMilliseconds != INFINITE)
195 struct timespec timeout = ts_from_ns();
197 ts_add_ms(&timeout, dwMilliseconds);
198 status = pthread_mutex_timedlock(&mutex->mutex, &timeout);
200 if (ETIMEDOUT == status)
205 pthread_mutex_lock(&mutex->mutex);
208 return WAIT_OBJECT_0;
213 WINPR_THREAD* thread = NULL;
217 BOOL autoSignaled = FALSE;
221 thread = (WINPR_THREAD*)_GetCurrentThread();
226 if (thread->apc.treatingCompletions)
229 extraFds = thread->apc.length;
238 int fd = winpr_Handle_getFd(Object);
241 WLog_ERR(TAG,
"winpr_Handle_getFd did not return a fd!");
242 SetLastError(ERROR_INVALID_HANDLE);
246 if (!pollset_init(&pollset, 1 + extraFds))
248 WLog_ERR(TAG,
"unable to initialize pollset");
249 SetLastError(ERROR_INTERNAL_ERROR);
253 if (!pollset_add(&pollset, fd, Object->Mode))
255 WLog_ERR(TAG,
"unable to add fd in pollset");
259 if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
261 WLog_ERR(TAG,
"unable to collect APC fds");
267 status = pollset_poll(&pollset, dwMilliseconds);
270 char ebuffer[256] = { 0 };
271 WLog_ERR(TAG,
"pollset_poll() failure [%d] %s", errno,
272 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
278 if (bAlertable && apc_executeCompletions(thread, &pollset, 1))
279 ret = WAIT_IO_COMPLETION;
281 isSet = pollset_isSignaled(&pollset, 0);
282 pollset_uninit(&pollset);
287 return winpr_Handle_cleanup(Object);
291 pollset_uninit(&pollset);
292 SetLastError(ERROR_INTERNAL_ERROR);
296 DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
298 return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE);
301 DWORD WaitForMultipleObjectsEx(DWORD nCount,
const HANDLE* lpHandles, BOOL bWaitAll,
302 DWORD dwMilliseconds, BOOL bAlertable)
306 DWORD poll_map[MAXIMUM_WAIT_OBJECTS] = { 0 };
307 BOOL signalled_handles[MAXIMUM_WAIT_OBJECTS] = { FALSE };
312 WINPR_THREAD* thread = NULL;
313 WINPR_POLL_SET pollset = { 0 };
314 DWORD ret = WAIT_FAILED;
319 if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS))
321 WLog_ERR(TAG,
"invalid handles count(%" PRIu32
")", nCount);
327 thread = winpr_GetCurrentThread();
332 if (thread->apc.treatingCompletions)
335 extraFds = thread->apc.length;
344 if (!pollset_init(&pollset, nCount + extraFds))
346 WLog_ERR(TAG,
"unable to initialize pollset for nCount=%" PRIu32
" extraCount=%" PRIu32
"",
353 now = GetTickCount64();
354 if (dwMilliseconds != INFINITE)
355 dueTime = now + dwMilliseconds;
357 dueTime = 0xFFFFFFFFFFFFFFFF;
361 BOOL autoSignaled = FALSE;
366 for (; idx < nCount; idx++)
370 if (signalled_handles[idx])
373 poll_map[polled] = idx;
376 if (!winpr_Handle_GetInfo(lpHandles[idx], &Type, &Object))
378 WLog_ERR(TAG,
"invalid event file descriptor at %" PRIu32, idx);
379 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
380 SetLastError(ERROR_INVALID_HANDLE);
384 fd = winpr_Handle_getFd(Object);
387 WLog_ERR(TAG,
"invalid file descriptor at %" PRIu32, idx);
388 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
389 SetLastError(ERROR_INVALID_HANDLE);
393 if (!pollset_add(&pollset, fd, Object->Mode))
395 WLog_ERR(TAG,
"unable to register fd in pollset at %" PRIu32, idx);
396 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
397 SetLastError(ERROR_INVALID_HANDLE);
405 if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
407 WLog_ERR(TAG,
"unable to register APC fds");
408 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
409 SetLastError(ERROR_INTERNAL_ERROR);
419 if (dwMilliseconds == INFINITE)
422 waitTime = (DWORD)(dueTime - now);
424 status = pollset_poll(&pollset, waitTime);
427 char ebuffer[256] = { 0 };
428 #ifdef WINPR_HAVE_POLL_H
429 WLog_ERR(TAG,
"poll() handle %" PRIu32
" (%" PRIu32
") failure [%d] %s", idx,
430 nCount, errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
432 WLog_ERR(TAG,
"select() handle %" PRIu32
" (%" PRIu32
") failure [%d] %s", idx,
433 nCount, errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
435 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
436 SetLastError(ERROR_INTERNAL_ERROR);
442 if (bAlertable && apc_executeCompletions(thread, &pollset, polled))
444 ret = WAIT_IO_COMPLETION;
451 for (DWORD index = 0; index < polled; index++)
453 DWORD handlesIndex = 0;
454 BOOL signal_set = FALSE;
457 handlesIndex = poll_map[index];
459 handlesIndex = index;
461 signal_set = pollset_isSignaled(&pollset, index);
464 DWORD rc = winpr_Handle_cleanup(lpHandles[handlesIndex]);
465 if (rc != WAIT_OBJECT_0)
467 WLog_ERR(TAG,
"error in cleanup function for handle at index=%" PRIu32,
475 signalled_handles[handlesIndex] = TRUE;
478 for (; signalled < nCount; signalled++)
480 if (!signalled_handles[signalled])
486 ret = (WAIT_OBJECT_0 + handlesIndex);
490 if (signalled >= nCount)
499 if (bAlertable && thread->apc.length > extraFds)
501 pollset_uninit(&pollset);
502 extraFds = thread->apc.length;
503 if (!pollset_init(&pollset, nCount + extraFds))
505 WLog_ERR(TAG,
"unable reallocate pollset");
506 SetLastError(ERROR_INTERNAL_ERROR);
511 pollset_reset(&pollset);
513 now = GetTickCount64();
514 }
while (now < dueTime);
519 pollset_uninit(&pollset);
523 DWORD WaitForMultipleObjects(DWORD nCount,
const HANDLE* lpHandles, BOOL bWaitAll,
524 DWORD dwMilliseconds)
526 return WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, FALSE);
529 DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds,
532 if (!SetEvent(hObjectToSignal))
535 return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);