FreeRDP
Loading...
Searching...
No Matches
wait.c
1
21#include <winpr/config.h>
22
23#ifdef WINPR_HAVE_UNISTD_H
24#include <unistd.h>
25#endif
26
27#include <winpr/assert.h>
28#include <errno.h>
29
30#include <winpr/crt.h>
31#include <winpr/synch.h>
32#include <winpr/platform.h>
33#include <winpr/sysinfo.h>
34
35#include "synch.h"
36#include "pollset.h"
37#include "../thread/thread.h"
38#include <winpr/thread.h>
39#include <winpr/debug.h>
40
41#include "../log.h"
42#define TAG WINPR_TAG("sync.wait")
43
51#ifndef _WIN32
52
53#include <stdlib.h>
54#include <time.h>
55#include <sys/time.h>
56#include <sys/wait.h>
57
58#include "../handle/handle.h"
59
60#include "../pipe/pipe.h"
61
62static struct timespec ts_from_ns(void)
63{
64 const UINT64 ns = winpr_GetUnixTimeNS();
65 struct timespec timeout = WINPR_C_ARRAY_INIT;
66 timeout.tv_sec = WINPR_TIME_NS_TO_S(ns);
67 timeout.tv_nsec = WINPR_TIME_NS_REM_NS(ns);
68 return timeout;
69}
70
76#if !defined(WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK)
77#include <pthread.h>
78
79static long long ts_difftime(const struct timespec* o, const struct timespec* n)
80{
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;
84}
85
86#ifdef ANDROID
87#if (__ANDROID_API__ >= 21)
88#define CONST_NEEDED const
89#else
90#define CONST_NEEDED
91#endif
92#define STATIC_NEEDED
93#else /* ANDROID */
94#define CONST_NEEDED const
95#define STATIC_NEEDED static
96#endif
97
98STATIC_NEEDED int pthread_mutex_timedlock(pthread_mutex_t* mutex,
99 CONST_NEEDED struct timespec* timeout)
100{
101 struct timespec timenow = WINPR_C_ARRAY_INIT;
102 struct timespec sleepytime = WINPR_C_ARRAY_INIT;
103 unsigned long long diff = 0;
104 int retcode = -1;
105 /* This is just to avoid a completely busy wait */
106 timenow = ts_from_ns();
107 diff = ts_difftime(&timenow, timeout);
108 sleepytime.tv_sec = diff / 1000000000LL;
109 sleepytime.tv_nsec = diff % 1000000000LL;
110
111 while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY)
112 {
113 timenow = ts_from_ns();
114
115 if (ts_difftime(timeout, &timenow) >= 0)
116 {
117 return ETIMEDOUT;
118 }
119
120 nanosleep(&sleepytime, nullptr);
121 }
122
123 return retcode;
124}
125#endif
126
127static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
128{
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;
133}
134
135DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
136{
137 ULONG Type = 0;
138 WINPR_HANDLE* Object = nullptr;
139 WINPR_POLL_SET pollset = WINPR_C_ARRAY_INIT;
140
141 if (!winpr_Handle_GetInfo(hHandle, &Type, &Object))
142 {
143 WLog_ERR(TAG, "invalid hHandle.");
144 SetLastError(ERROR_INVALID_HANDLE);
145 return WAIT_FAILED;
146 }
147
148 if (Type == HANDLE_TYPE_PROCESS && winpr_Handle_getFd(hHandle) == -1)
149 {
150 /* note: if we have pidfd support (under linux and we have managed to associate a
151 * pidfd with our process), we use the regular method with pollset below.
152 * If not (on other platforms) we do a waitpid */
153 WINPR_PROCESS* process = (WINPR_PROCESS*)Object;
154
155 while (TRUE)
156 {
157 int ret = waitpid(process->pid, &(process->status), WNOHANG);
158 if (ret == process->pid)
159 {
160 if (WIFEXITED(process->status))
161 process->dwExitCode = (DWORD)WEXITSTATUS(process->status);
162 else if (WIFSIGNALED(process->status))
163 process->dwExitCode = (DWORD)(128 + WTERMSIG(process->status));
164 else
165 process->dwExitCode = (DWORD)process->status;
166 return WAIT_OBJECT_0;
167 }
168 else if (ret < 0)
169 {
170 char ebuffer[256] = WINPR_C_ARRAY_INIT;
171 WLog_ERR(TAG, "waitpid failure [%d] %s", errno,
172 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
173 SetLastError(ERROR_INTERNAL_ERROR);
174 return WAIT_FAILED;
175 }
176
177 if (dwMilliseconds == 0)
178 return WAIT_TIMEOUT;
179
180 /* sleep by slices of 50ms */
181 DWORD waitDelay = 50;
182 if (dwMilliseconds != INFINITE)
183 {
184 if (dwMilliseconds < 50)
185 waitDelay = dwMilliseconds;
186 dwMilliseconds -= waitDelay;
187 }
188
189 DWORD status = SleepEx(waitDelay, bAlertable);
190 if (status != 0)
191 return status;
192 }
193 }
194
195 if (Type == HANDLE_TYPE_MUTEX)
196 {
197 WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object;
198
199 if (dwMilliseconds != INFINITE)
200 {
201 int status = 0;
202 struct timespec timeout = ts_from_ns();
203
204 ts_add_ms(&timeout, dwMilliseconds);
205 status = pthread_mutex_timedlock(&mutex->mutex, &timeout);
206
207 if (ETIMEDOUT == status)
208 return WAIT_TIMEOUT;
209 }
210 else
211 {
212 pthread_mutex_lock(&mutex->mutex);
213 }
214
215 return WAIT_OBJECT_0;
216 }
217 else
218 {
219 int status = -1;
220 WINPR_THREAD* thread = nullptr;
221 BOOL isSet = FALSE;
222 size_t extraFds = 0;
223 DWORD ret = 0;
224 BOOL autoSignaled = FALSE;
225
226 if (bAlertable)
227 {
228 thread = (WINPR_THREAD*)_GetCurrentThread();
229 if (thread)
230 {
231 /* treat reentrancy, we can't switch to alertable state when we're already
232 treating completions */
233 if (thread->apc.treatingCompletions)
234 bAlertable = FALSE;
235 else
236 extraFds = thread->apc.length;
237 }
238 else
239 {
240 /* called from a non WinPR thread */
241 bAlertable = FALSE;
242 }
243 }
244
245 int fd = winpr_Handle_getFd(Object);
246 if (fd < 0)
247 {
248 WLog_ERR(TAG, "winpr_Handle_getFd did not return a fd!");
249 SetLastError(ERROR_INVALID_HANDLE);
250 return WAIT_FAILED;
251 }
252
253 if (!pollset_init(&pollset, 1 + extraFds))
254 {
255 WLog_ERR(TAG, "unable to initialize pollset");
256 SetLastError(ERROR_INTERNAL_ERROR);
257 return WAIT_FAILED;
258 }
259
260 if (!pollset_add(&pollset, fd, Object->Mode))
261 {
262 WLog_ERR(TAG, "unable to add fd in pollset");
263 goto out;
264 }
265
266 if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
267 {
268 WLog_ERR(TAG, "unable to collect APC fds");
269 goto out;
270 }
271
272 if (!autoSignaled)
273 {
274 status = pollset_poll(&pollset, dwMilliseconds);
275 if (status < 0)
276 {
277 char ebuffer[256] = WINPR_C_ARRAY_INIT;
278 WLog_ERR(TAG, "pollset_poll() failure [%d] %s", errno,
279 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
280 goto out;
281 }
282 }
283
284 ret = WAIT_TIMEOUT;
285 if (bAlertable && apc_executeCompletions(thread, &pollset, 1))
286 ret = WAIT_IO_COMPLETION;
287
288 isSet = pollset_isSignaled(&pollset, 0);
289 pollset_uninit(&pollset);
290
291 if (!isSet)
292 return ret;
293
294 return winpr_Handle_cleanup(Object);
295 }
296
297out:
298 pollset_uninit(&pollset);
299 SetLastError(ERROR_INTERNAL_ERROR);
300 return WAIT_FAILED;
301}
302
303DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
304{
305 return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE);
306}
307
308DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
309 DWORD dwMilliseconds, BOOL bAlertable)
310{
311 DWORD signalled = 0;
312 DWORD polled = 0;
313 DWORD poll_map[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
314 BOOL signalled_handles[MAXIMUM_WAIT_OBJECTS] = { FALSE };
315 int fd = -1;
316 int status = -1;
317 ULONG Type = 0;
318 WINPR_HANDLE* Object = nullptr;
319 WINPR_THREAD* thread = nullptr;
320 WINPR_POLL_SET pollset = WINPR_C_ARRAY_INIT;
321 DWORD ret = WAIT_FAILED;
322 size_t extraFds = 0;
323 UINT64 now = 0;
324 UINT64 dueTime = 0;
325
326 if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS))
327 {
328 WLog_ERR(TAG, "invalid handles count(%" PRIu32 ")", nCount);
329 return WAIT_FAILED;
330 }
331
332 if (bAlertable)
333 {
334 thread = winpr_GetCurrentThread();
335 if (thread)
336 {
337 /* treat reentrancy, we can't switch to alertable state when we're already
338 treating completions */
339 if (thread->apc.treatingCompletions)
340 bAlertable = FALSE;
341 else
342 extraFds = thread->apc.length;
343 }
344 else
345 {
346 /* most probably we're not called from WinPR thread, so we can't have any APC */
347 bAlertable = FALSE;
348 }
349 }
350
351 if (!pollset_init(&pollset, nCount + extraFds))
352 {
353 WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 " extraCount=%" PRIuz "",
354 nCount, extraFds);
355 return WAIT_FAILED;
356 }
357
358 signalled = 0;
359
360 now = GetTickCount64();
361 if (dwMilliseconds != INFINITE)
362 dueTime = now + dwMilliseconds;
363 else
364 dueTime = 0xFFFFFFFFFFFFFFFF;
365
366 do
367 {
368 BOOL autoSignaled = FALSE;
369 polled = 0;
370
371 /* first collect file descriptors to poll */
372 DWORD idx = 0;
373 for (; idx < nCount; idx++)
374 {
375 if (bWaitAll)
376 {
377 if (signalled_handles[idx])
378 continue;
379
380 poll_map[polled] = idx;
381 }
382
383 if (!winpr_Handle_GetInfo(lpHandles[idx], &Type, &Object))
384 {
385 WLog_ERR(TAG, "invalid event file descriptor at %" PRIu32, idx);
386 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
387 SetLastError(ERROR_INVALID_HANDLE);
388 goto out;
389 }
390
391 fd = winpr_Handle_getFd(Object);
392 if (fd == -1)
393 {
394 WLog_ERR(TAG, "invalid file descriptor at %" PRIu32, idx);
395 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
396 SetLastError(ERROR_INVALID_HANDLE);
397 goto out;
398 }
399
400 if (!pollset_add(&pollset, fd, Object->Mode))
401 {
402 WLog_ERR(TAG, "unable to register fd in pollset at %" PRIu32, idx);
403 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
404 SetLastError(ERROR_INVALID_HANDLE);
405 goto out;
406 }
407
408 polled++;
409 }
410
411 /* treat file descriptors of the APC if needed */
412 if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
413 {
414 WLog_ERR(TAG, "unable to register APC fds");
415 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
416 SetLastError(ERROR_INTERNAL_ERROR);
417 goto out;
418 }
419
420 /* poll file descriptors */
421 status = 0;
422 if (!autoSignaled)
423 {
424 DWORD waitTime = 0;
425
426 if (dwMilliseconds == INFINITE)
427 waitTime = INFINITE;
428 else
429 waitTime = (DWORD)(dueTime - now);
430
431 status = pollset_poll(&pollset, waitTime);
432 if (status < 0)
433 {
434 char ebuffer[256] = WINPR_C_ARRAY_INIT;
435#ifdef WINPR_HAVE_POLL_H
436 WLog_ERR(TAG, "poll() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", idx,
437 nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
438#else
439 WLog_ERR(TAG, "select() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", idx,
440 nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
441#endif
442 winpr_log_backtrace(TAG, WLOG_ERROR, 20);
443 SetLastError(ERROR_INTERNAL_ERROR);
444 goto out;
445 }
446 }
447
448 /* give priority to the APC queue, to return WAIT_IO_COMPLETION */
449 if (bAlertable && apc_executeCompletions(thread, &pollset, polled))
450 {
451 ret = WAIT_IO_COMPLETION;
452 goto out;
453 }
454
455 /* then treat pollset */
456 if (status)
457 {
458 for (DWORD index = 0; index < polled; index++)
459 {
460 DWORD handlesIndex = 0;
461 BOOL signal_set = FALSE;
462
463 if (bWaitAll)
464 handlesIndex = poll_map[index];
465 else
466 handlesIndex = index;
467
468 signal_set = pollset_isSignaled(&pollset, index);
469 if (signal_set)
470 {
471 DWORD rc = winpr_Handle_cleanup(lpHandles[handlesIndex]);
472 if (rc != WAIT_OBJECT_0)
473 {
474 WLog_ERR(TAG, "error in cleanup function for handle at index=%" PRIu32,
475 handlesIndex);
476 ret = rc;
477 goto out;
478 }
479
480 if (bWaitAll)
481 {
482 signalled_handles[handlesIndex] = TRUE;
483
484 /* Continue checks from last position. */
485 for (; signalled < nCount; signalled++)
486 {
487 if (!signalled_handles[signalled])
488 break;
489 }
490 }
491 else
492 {
493 ret = (WAIT_OBJECT_0 + handlesIndex);
494 goto out;
495 }
496
497 if (signalled >= nCount)
498 {
499 ret = WAIT_OBJECT_0;
500 goto out;
501 }
502 }
503 }
504 }
505
506 if (bAlertable && thread->apc.length > extraFds)
507 {
508 pollset_uninit(&pollset);
509 extraFds = thread->apc.length;
510 if (!pollset_init(&pollset, nCount + extraFds))
511 {
512 WLog_ERR(TAG, "unable reallocate pollset");
513 SetLastError(ERROR_INTERNAL_ERROR);
514 return WAIT_FAILED;
515 }
516 }
517 else
518 pollset_reset(&pollset);
519
520 now = GetTickCount64();
521 } while (now < dueTime);
522
523 ret = WAIT_TIMEOUT;
524
525out:
526 pollset_uninit(&pollset);
527 return ret;
528}
529
530DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
531 DWORD dwMilliseconds)
532{
533 return WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, FALSE);
534}
535
536DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds,
537 BOOL bAlertable)
538{
539 if (!SetEvent(hObjectToSignal))
540 return WAIT_FAILED;
541
542 return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
543}
544
545#endif