VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/poll-win.cpp@ 31942

Last change on this file since 31942 was 31942, checked in by vboxsync, 14 years ago

Runtime/poll: Fix retrieving events after WaitForMultipleObjects

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: poll-win.cpp 31942 2010-08-24 21:53:00Z vboxsync $ */
2/** @file
3 * IPRT - Polling I/O Handles, Windows Implementation.
4 *
5 * @todo merge poll-win.cpp and poll-posix.cpp, there is lots of common code.
6 */
7
8/*
9 * Copyright (C) 2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * The contents of this file may alternatively be used under the terms
20 * of the Common Development and Distribution License Version 1.0
21 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
22 * VirtualBox OSE distribution, in which case the provisions of the
23 * CDDL are applicable instead of those of the GPL.
24 *
25 * You may elect to license modified versions of this file under the
26 * terms and conditions of either the GPL or the CDDL or both.
27 */
28
29
30/*******************************************************************************
31* Header Files *
32*******************************************************************************/
33#include <Windows.h>
34
35#include <iprt/poll.h>
36#include "internal/iprt.h"
37
38#include <iprt/asm.h>
39#include <iprt/assert.h>
40#include <iprt/err.h>
41#include <iprt/mem.h>
42#include <iprt/pipe.h>
43#include <iprt/string.h>
44#include <iprt/thread.h>
45#include <iprt/time.h>
46
47#include "internal/pipe.h"
48#define IPRT_INTERNAL_SOCKET_POLLING_ONLY
49#include "internal/socket.h"
50#include "internal/magics.h"
51
52
53/*******************************************************************************
54* Structures and Typedefs *
55*******************************************************************************/
56/**
57 * Handle entry in a poll set.
58 */
59typedef struct RTPOLLSETHNDENT
60{
61 /** The handle type. */
62 RTHANDLETYPE enmType;
63 /** The handle ID. */
64 uint32_t id;
65 /** The events we're waiting for here. */
66 uint32_t fEvents;
67 /** Set if this is the final entry for this handle.
68 * If the handle is entered more than once, this will be clear for all but
69 * the last entry. */
70 bool fFinalEntry;
71 /** The handle union. */
72 RTHANDLEUNION u;
73} RTPOLLSETHNDENT;
74/** Pointer to a handle entry. */
75typedef RTPOLLSETHNDENT *PRTPOLLSETHNDENT;
76
77
78/**
79 * Poll set data, Windows.
80 */
81typedef struct RTPOLLSETINTERNAL
82{
83 /** The magic value (RTPOLLSET_MAGIC). */
84 uint32_t u32Magic;
85 /** Set when someone is polling or making changes. */
86 bool volatile fBusy;
87
88 /** The number of valid handles in the set. */
89 uint32_t cHandles;
90 /** The native handles. */
91 HANDLE ahNative[MAXIMUM_WAIT_OBJECTS];
92 /** Array of handles and IDs. */
93 RTPOLLSETHNDENT aHandles[MAXIMUM_WAIT_OBJECTS];
94} RTPOLLSETINTERNAL;
95
96
97/**
98 * Common worker for RTPoll and RTPollNoResume
99 */
100static int rtPollNoResumeWorker(RTPOLLSETINTERNAL *pThis, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
101{
102 int rc;
103
104 if (RT_UNLIKELY(pThis->cHandles == 0 && cMillies == RT_INDEFINITE_WAIT))
105 return VERR_DEADLOCK;
106
107 /*
108 * Check for special case, RTThreadSleep...
109 */
110 uint32_t const cHandles = pThis->cHandles;
111 if (cHandles == 0)
112 {
113 rc = RTThreadSleep(cMillies);
114 if (RT_SUCCESS(rc))
115 rc = VERR_TIMEOUT;
116 return rc;
117 }
118
119 /*
120 * Check + prepare the handles before waiting.
121 */
122 uint32_t fEvents = 0;
123 bool const fNoWait = cMillies == 0;
124 uint32_t i;
125 for (i = 0; i < cHandles; i++)
126 {
127 switch (pThis->aHandles[i].enmType)
128 {
129 case RTHANDLETYPE_PIPE:
130 fEvents = rtPipePollStart(pThis->aHandles[i].u.hPipe, pThis, pThis->aHandles[i].fEvents,
131 pThis->aHandles[i].fFinalEntry, fNoWait);
132 break;
133
134 case RTHANDLETYPE_SOCKET:
135 fEvents = rtSocketPollStart(pThis->aHandles[i].u.hSocket, pThis, pThis->aHandles[i].fEvents,
136 pThis->aHandles[i].fFinalEntry, fNoWait);
137 break;
138
139 default:
140 AssertFailed();
141 fEvents = UINT32_MAX;
142 break;
143 }
144 if (fEvents)
145 break;
146 }
147 if ( fEvents
148 || fNoWait)
149 {
150
151 if (pid)
152 *pid = pThis->aHandles[i].id;
153 if (pfEvents)
154 *pfEvents = fEvents;
155 rc = !fEvents
156 ? VERR_TIMEOUT
157 : fEvents != UINT32_MAX
158 ? VINF_SUCCESS
159 : VERR_INTERNAL_ERROR_4;
160
161 /* clean up */
162 if (!fNoWait)
163 while (i-- > 0)
164 {
165 switch (pThis->aHandles[i].enmType)
166 {
167 case RTHANDLETYPE_PIPE:
168 rtPipePollDone(pThis->aHandles[i].u.hPipe, pThis->aHandles[i].fEvents,
169 pThis->aHandles[i].fFinalEntry);
170 break;
171
172 case RTHANDLETYPE_SOCKET:
173 rtSocketPollDone(pThis->aHandles[i].u.hSocket, pThis->aHandles[i].fEvents,
174 pThis->aHandles[i].fFinalEntry);
175 break;
176
177 default:
178 AssertFailed();
179 break;
180 }
181 }
182
183 return rc;
184 }
185
186 /*
187 * Wait.
188 */
189 DWORD dwRc = WaitForMultipleObjectsEx(cHandles, &pThis->ahNative[0],
190 FALSE /*fWaitAll */,
191 cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
192 TRUE /*fAlertable*/);
193 if ( dwRc >= WAIT_OBJECT_0
194 && dwRc < WAIT_OBJECT_0 + cHandles)
195 rc = VERR_INTERRUPTED;
196 else if (dwRc == WAIT_TIMEOUT)
197 rc = VERR_TIMEOUT;
198 else if (dwRc == WAIT_IO_COMPLETION)
199 rc = VERR_INTERRUPTED;
200 else if (dwRc == WAIT_FAILED)
201 rc = RTErrConvertFromWin32(GetLastError());
202 else
203 {
204 AssertMsgFailed(("%u (%#x)\n", dwRc, dwRc));
205 rc = VERR_INTERNAL_ERROR_5;
206 }
207
208 /*
209 * Get event (if pending) and do wait cleanup.
210 */
211 /** @todo r=aeichner: The loop below overwrites events if
212 * more than one source has events pending.
213 */
214 i = cHandles;
215 while (i-- > 0)
216 {
217 fEvents = 0;
218 switch (pThis->aHandles[i].enmType)
219 {
220 case RTHANDLETYPE_PIPE:
221 fEvents = rtPipePollDone(pThis->aHandles[i].u.hPipe, pThis->aHandles[i].fEvents,
222 pThis->aHandles[i].fFinalEntry);
223 break;
224
225 case RTHANDLETYPE_SOCKET:
226 fEvents = rtSocketPollDone(pThis->aHandles[i].u.hSocket, pThis->aHandles[i].fEvents,
227 pThis->aHandles[i].fFinalEntry);
228 break;
229
230 default:
231 AssertFailed();
232 break;
233 }
234 if (fEvents)
235 {
236 Assert(fEvents != UINT32_MAX);
237 if (pfEvents)
238 *pfEvents = fEvents;
239 if (pid)
240 *pid = pThis->aHandles[i].id;
241 rc = VINF_SUCCESS;
242 }
243 }
244
245 return rc;
246}
247
248
249RTDECL(int) RTPoll(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
250{
251 RTPOLLSETINTERNAL *pThis = hPollSet;
252 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
253 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
254 AssertPtrNull(pfEvents);
255 AssertPtrNull(pid);
256
257 /*
258 * Set the busy flag and do the job.
259 */
260 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
261
262 int rc;
263 if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
264 {
265 do rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
266 while (rc == VERR_INTERRUPTED);
267 }
268 else
269 {
270 uint64_t MsStart = RTTimeMilliTS();
271 rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
272 while (RT_UNLIKELY(rc == VERR_INTERRUPTED))
273 {
274 if (RTTimeMilliTS() - MsStart >= cMillies)
275 {
276 rc = VERR_TIMEOUT;
277 break;
278 }
279 rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
280 }
281 }
282
283 ASMAtomicWriteBool(&pThis->fBusy, false);
284
285 return rc;
286}
287
288
289RTDECL(int) RTPollNoResume(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
290{
291 RTPOLLSETINTERNAL *pThis = hPollSet;
292 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
293 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
294 AssertPtrNull(pfEvents);
295 AssertPtrNull(pid);
296
297 /*
298 * Set the busy flag and do the job.
299 */
300 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
301
302 int rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
303
304 ASMAtomicWriteBool(&pThis->fBusy, false);
305
306 return rc;
307}
308
309
310RTDECL(int) RTPollSetCreate(PRTPOLLSET phPollSet)
311{
312 AssertPtrReturn(phPollSet, VERR_INVALID_POINTER);
313 RTPOLLSETINTERNAL *pThis = (RTPOLLSETINTERNAL *)RTMemAllocZ(sizeof(RTPOLLSETINTERNAL));
314 if (!pThis)
315 return VERR_NO_MEMORY;
316
317 pThis->u32Magic = RTPOLLSET_MAGIC;
318 pThis->fBusy = false;
319 pThis->cHandles = 0;
320 for (size_t i = 0; i < RT_ELEMENTS(pThis->ahNative); i++)
321 pThis->ahNative[i] = INVALID_HANDLE_VALUE;
322
323 *phPollSet = pThis;
324 return VINF_SUCCESS;
325}
326
327
328RTDECL(int) RTPollSetDestroy(RTPOLLSET hPollSet)
329{
330 RTPOLLSETINTERNAL *pThis = hPollSet;
331 if (pThis == NIL_RTPOLLSET)
332 return VINF_SUCCESS;
333 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
334 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
335 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
336
337 ASMAtomicWriteU32(&pThis->u32Magic, ~RTPOLLSET_MAGIC);
338 RTMemFree(pThis);
339
340 return VINF_SUCCESS;
341}
342
343
344RTDECL(int) RTPollSetAdd(RTPOLLSET hPollSet, PCRTHANDLE pHandle, uint32_t fEvents, uint32_t id)
345{
346 /*
347 * Validate the input (tedious).
348 */
349 RTPOLLSETINTERNAL *pThis = hPollSet;
350 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
351 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
352 AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
353 AssertReturn(fEvents, VERR_INVALID_PARAMETER);
354 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
355
356 if (!pHandle)
357 return VINF_SUCCESS;
358 AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
359 AssertReturn(pHandle->enmType > RTHANDLETYPE_INVALID && pHandle->enmType < RTHANDLETYPE_END, VERR_INVALID_PARAMETER);
360
361 /*
362 * Set the busy flag and do the job.
363 */
364 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
365
366 int rc = VINF_SUCCESS;
367 HANDLE hNative = INVALID_HANDLE_VALUE;
368 RTHANDLEUNION uh;
369 uh.uInt = 0;
370 switch (pHandle->enmType)
371 {
372 case RTHANDLETYPE_PIPE:
373 uh.hPipe = pHandle->u.hPipe;
374 if (uh.hPipe != NIL_RTPIPE)
375 rc = rtPipePollGetHandle(uh.hPipe, fEvents, &hNative);
376 break;
377
378 case RTHANDLETYPE_SOCKET:
379 uh.hSocket = pHandle->u.hSocket;
380 if (uh.hSocket != NIL_RTSOCKET)
381 rc = rtSocketPollGetHandle(uh.hSocket, fEvents, &hNative);
382 break;
383
384 case RTHANDLETYPE_FILE:
385 AssertMsgFailed(("Files are always ready for reading/writing and thus not pollable. Use native APIs for special devices.\n"));
386 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
387 break;
388
389 case RTHANDLETYPE_THREAD:
390 AssertMsgFailed(("Thread handles are currently not pollable\n"));
391 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
392 break;
393
394 default:
395 AssertMsgFailed(("\n"));
396 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
397 break;
398 }
399 if ( RT_SUCCESS(rc)
400 && hNative != INVALID_HANDLE_VALUE)
401 {
402 uint32_t const i = pThis->cHandles;
403
404 /* Check that the handle ID doesn't exist already. */
405 uint32_t iPrev = UINT32_MAX;
406 uint32_t j = i;
407 while (j-- > 0)
408 {
409 if (pThis->aHandles[j].id == id)
410 {
411 rc = VERR_POLL_HANDLE_ID_EXISTS;
412 break;
413 }
414 if ( pThis->aHandles[j].enmType == pHandle->enmType
415 && pThis->aHandles[j].u.uInt == uh.uInt)
416 iPrev = j;
417 }
418
419 /* Check that we won't overflow the poll set now. */
420 if ( RT_SUCCESS(rc)
421 && i + 1 > RT_ELEMENTS(pThis->ahNative))
422 rc = VERR_POLL_SET_IS_FULL;
423 if (RT_SUCCESS(rc))
424 {
425 /* Add the handles to the two parallel arrays. */
426 pThis->ahNative[i] = hNative;
427 pThis->aHandles[i].enmType = pHandle->enmType;
428 pThis->aHandles[i].u = uh;
429 pThis->aHandles[i].id = id;
430 pThis->aHandles[i].fEvents = fEvents;
431 pThis->aHandles[i].fFinalEntry = true;
432 pThis->cHandles = i + 1;
433
434 if (iPrev != UINT32_MAX)
435 {
436 Assert(pThis->aHandles[i].fFinalEntry);
437 pThis->aHandles[i].fFinalEntry = false;
438 }
439
440 rc = VINF_SUCCESS;
441 }
442 }
443
444 ASMAtomicWriteBool(&pThis->fBusy, false);
445 return rc;
446}
447
448
449RTDECL(int) RTPollSetRemove(RTPOLLSET hPollSet, uint32_t id)
450{
451 /*
452 * Validate the input.
453 */
454 RTPOLLSETINTERNAL *pThis = hPollSet;
455 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
456 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
457 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
458
459 /*
460 * Set the busy flag and do the job.
461 */
462 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
463
464 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
465 uint32_t i = pThis->cHandles;
466 while (i-- > 0)
467 if (pThis->aHandles[i].id == id)
468 {
469 /* Save some details for the duplicate searching. */
470 bool fFinalEntry = pThis->aHandles[i].fFinalEntry;
471 RTHANDLETYPE enmType = pThis->aHandles[i].enmType;
472 RTHANDLEUNION uh = pThis->aHandles[i].u;
473
474 /* Remove the entry. */
475 pThis->cHandles--;
476 size_t const cToMove = pThis->cHandles - i;
477 if (cToMove)
478 {
479 memmove(&pThis->aHandles[i], &pThis->aHandles[i + 1], cToMove * sizeof(pThis->aHandles[i]));
480 memmove(&pThis->ahNative[i], &pThis->ahNative[i + 1], cToMove * sizeof(pThis->ahNative[i]));
481 }
482
483 /* Check for duplicate and set the fFinalEntry flag. */
484 if (fFinalEntry)
485 while (i-- > 0)
486 if ( pThis->aHandles[i].u.uInt == uh.uInt
487 && pThis->aHandles[i].enmType == enmType)
488 {
489 Assert(!pThis->aHandles[i].fFinalEntry);
490 pThis->aHandles[i].fFinalEntry = true;
491 break;
492 }
493
494 rc = VINF_SUCCESS;
495 break;
496 }
497
498 ASMAtomicWriteBool(&pThis->fBusy, false);
499 return rc;
500}
501
502
503RTDECL(int) RTPollSetQueryHandle(RTPOLLSET hPollSet, uint32_t id, PRTHANDLE pHandle)
504{
505 /*
506 * Validate the input.
507 */
508 RTPOLLSETINTERNAL *pThis = hPollSet;
509 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
510 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
511 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
512 AssertPtrNullReturn(pHandle, VERR_INVALID_POINTER);
513
514 /*
515 * Set the busy flag and do the job.
516 */
517 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
518
519 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
520 uint32_t i = pThis->cHandles;
521 while (i-- > 0)
522 if (pThis->aHandles[i].id == id)
523 {
524 if (pHandle)
525 {
526 pHandle->enmType = pThis->aHandles[i].enmType;
527 pHandle->u = pThis->aHandles[i].u;
528 }
529 rc = VINF_SUCCESS;
530 break;
531 }
532
533 ASMAtomicWriteBool(&pThis->fBusy, false);
534 return rc;
535}
536
537
538RTDECL(uint32_t) RTPollSetGetCount(RTPOLLSET hPollSet)
539{
540 /*
541 * Validate the input.
542 */
543 RTPOLLSETINTERNAL *pThis = hPollSet;
544 AssertPtrReturn(pThis, UINT32_MAX);
545 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, UINT32_MAX);
546
547 /*
548 * Set the busy flag and do the job.
549 */
550 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), UINT32_MAX);
551 uint32_t cHandles = pThis->cHandles;
552 ASMAtomicWriteBool(&pThis->fBusy, false);
553
554 return cHandles;
555}
556
557RTDECL(int) RTPollSetEventsChange(RTPOLLSET hPollSet, uint32_t id, uint32_t fEvents)
558{
559 /*
560 * Validate the input.
561 */
562 RTPOLLSETINTERNAL *pThis = hPollSet;
563 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
564 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
565 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
566 AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
567 AssertReturn(fEvents, VERR_INVALID_PARAMETER);
568
569 /*
570 * Set the busy flag and do the job.
571 */
572 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
573
574 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
575 uint32_t i = pThis->cHandles;
576 while (i-- > 0)
577 if (pThis->aHandles[i].id == id)
578 {
579 pThis->aHandles[i].fEvents = fEvents;
580 rc = VINF_SUCCESS;
581 break;
582 }
583
584 ASMAtomicWriteBool(&pThis->fBusy, false);
585 return rc;
586}
587
588
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette