VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/poll.cpp@ 82968

Last change on this file since 82968 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.2 KB
Line 
1/* $Id: poll.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT - Polling I/O Handles, Windows+Posix Implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/cdefs.h>
32#ifdef RT_OS_WINDOWS
33# include <iprt/win/windows.h>
34
35#elif defined(RT_OS_OS2)
36# define INCL_BASE
37# include <os2.h>
38# include <limits.h>
39# include <sys/socket.h>
40
41#else
42# include <limits.h>
43# include <errno.h>
44# include <sys/poll.h>
45# if defined(RT_OS_SOLARIS)
46# include <sys/socket.h>
47# endif
48#endif
49
50#include <iprt/poll.h>
51#include "internal/iprt.h"
52
53#include <iprt/alloca.h>
54#include <iprt/asm.h>
55#include <iprt/assert.h>
56#include <iprt/err.h>
57#include <iprt/mem.h>
58#include <iprt/pipe.h>
59#include <iprt/socket.h>
60#include <iprt/string.h>
61#include <iprt/thread.h>
62#include <iprt/time.h>
63
64#include "internal/pipe.h"
65#define IPRT_INTERNAL_SOCKET_POLLING_ONLY
66#include "internal/socket.h"
67#include "internal/magics.h"
68
69
70/*********************************************************************************************************************************
71* Defined Constants And Macros *
72*********************************************************************************************************************************/
73/** The maximum poll set size.
74 * @remarks To help portability, we set this to the Windows limit. We can lift
75 * this restriction later if it becomes necessary. */
76#define RTPOLL_SET_MAX 64
77
78
79
80/*********************************************************************************************************************************
81* Structures and Typedefs *
82*********************************************************************************************************************************/
83/**
84 * Handle entry in a poll set.
85 */
86typedef struct RTPOLLSETHNDENT
87{
88 /** The handle type. */
89 RTHANDLETYPE enmType;
90 /** The handle ID. */
91 uint32_t id;
92 /** The events we're waiting for here. */
93 uint32_t fEvents;
94 /** Set if this is the final entry for this handle.
95 * If the handle is entered more than once, this will be clear for all but
96 * the last entry. */
97 bool fFinalEntry;
98 /** The handle union. */
99 RTHANDLEUNION u;
100} RTPOLLSETHNDENT;
101/** Pointer to a handle entry. */
102typedef RTPOLLSETHNDENT *PRTPOLLSETHNDENT;
103
104
105/**
106 * Poll set data.
107 */
108typedef struct RTPOLLSETINTERNAL
109{
110 /** The magic value (RTPOLLSET_MAGIC). */
111 uint32_t u32Magic;
112 /** Set when someone is polling or making changes. */
113 bool volatile fBusy;
114
115 /** The number of allocated handles. */
116 uint16_t cHandlesAllocated;
117 /** The number of valid handles in the set. */
118 uint16_t cHandles;
119
120#ifdef RT_OS_WINDOWS
121 /** Pointer to an array of native handles. */
122 HANDLE *pahNative;
123#elif defined(RT_OS_OS2)
124 /** The semaphore records. */
125 PSEMRECORD paSemRecs;
126 /** The multiple wait semaphore used for non-socket waits. */
127 HMUX hmux;
128 /** os2_select template. */
129 int *pafdSelect;
130 /** The number of sockets to monitor for read. */
131 uint16_t cReadSockets;
132 /** The number of sockets to monitor for write. */
133 uint16_t cWriteSockets;
134 /** The number of sockets to monitor for exceptions. */
135 uint16_t cXcptSockets;
136 /** The number of pipes. */
137 uint16_t cPipes;
138 /** Pointer to an array of native handles. */
139 PRTHCINTPTR pahNative;
140#else
141 /** Pointer to an array of pollfd structures. */
142 struct pollfd *paPollFds;
143#endif
144 /** Pointer to an array of handles and IDs. */
145 PRTPOLLSETHNDENT paHandles;
146} RTPOLLSETINTERNAL;
147
148
149
150/**
151 * Common worker for RTPoll and RTPollNoResume
152 */
153static int rtPollNoResumeWorker(RTPOLLSETINTERNAL *pThis, uint64_t MsStart, RTMSINTERVAL cMillies,
154 uint32_t *pfEvents, uint32_t *pid)
155{
156 int rc;
157
158 if (RT_UNLIKELY(pThis->cHandles == 0 && cMillies == RT_INDEFINITE_WAIT))
159 return VERR_DEADLOCK;
160
161 /*
162 * Check for special case, RTThreadSleep...
163 */
164 uint32_t const cHandles = pThis->cHandles;
165 if (cHandles == 0)
166 {
167 rc = RTThreadSleep(cMillies);
168 if (RT_SUCCESS(rc))
169 rc = VERR_TIMEOUT;
170 return rc;
171 }
172
173#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
174 /*
175 * Check + prepare the handles before waiting.
176 */
177 uint32_t fEvents = 0;
178 bool const fNoWait = cMillies == 0;
179 uint32_t i;
180 for (i = 0; i < cHandles; i++)
181 {
182 switch (pThis->paHandles[i].enmType)
183 {
184 case RTHANDLETYPE_PIPE:
185 fEvents = rtPipePollStart(pThis->paHandles[i].u.hPipe, pThis, pThis->paHandles[i].fEvents,
186 pThis->paHandles[i].fFinalEntry, fNoWait);
187 break;
188
189 case RTHANDLETYPE_SOCKET:
190 fEvents = rtSocketPollStart(pThis->paHandles[i].u.hSocket, pThis, pThis->paHandles[i].fEvents,
191 pThis->paHandles[i].fFinalEntry, fNoWait);
192 break;
193
194 default:
195 AssertFailed();
196 fEvents = UINT32_MAX;
197 break;
198 }
199 if (fEvents)
200 break;
201 }
202 if ( fEvents
203 || fNoWait)
204 {
205
206 if (pid)
207 *pid = pThis->paHandles[i].id;
208 if (pfEvents)
209 *pfEvents = fEvents;
210 rc = !fEvents
211 ? VERR_TIMEOUT
212 : fEvents != UINT32_MAX
213 ? VINF_SUCCESS
214 : VERR_INTERNAL_ERROR_4;
215
216 /* clean up */
217 if (!fNoWait)
218 while (i-- > 0)
219 {
220 switch (pThis->paHandles[i].enmType)
221 {
222 case RTHANDLETYPE_PIPE:
223 rtPipePollDone(pThis->paHandles[i].u.hPipe, pThis->paHandles[i].fEvents,
224 pThis->paHandles[i].fFinalEntry, false);
225 break;
226
227 case RTHANDLETYPE_SOCKET:
228 rtSocketPollDone(pThis->paHandles[i].u.hSocket, pThis->paHandles[i].fEvents,
229 pThis->paHandles[i].fFinalEntry, false);
230 break;
231
232 default:
233 AssertFailed();
234 break;
235 }
236 }
237
238 return rc;
239 }
240
241
242 /*
243 * Wait.
244 */
245# ifdef RT_OS_WINDOWS
246 RT_NOREF_PV(MsStart);
247
248 DWORD dwRc = WaitForMultipleObjectsEx(cHandles, pThis->pahNative,
249 FALSE /*fWaitAll */,
250 cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
251 TRUE /*fAlertable*/);
252 AssertCompile(WAIT_OBJECT_0 == 0);
253 if (dwRc < WAIT_OBJECT_0 + cHandles)
254 rc = VERR_INTERRUPTED;
255 else if (dwRc == WAIT_TIMEOUT)
256 rc = VERR_TIMEOUT;
257 else if (dwRc == WAIT_IO_COMPLETION)
258 rc = VERR_INTERRUPTED;
259 else if (dwRc == WAIT_FAILED)
260 rc = RTErrConvertFromWin32(GetLastError());
261 else
262 {
263 AssertMsgFailed(("%u (%#x)\n", dwRc, dwRc));
264 rc = VERR_INTERNAL_ERROR_5;
265 }
266
267# else /* RT_OS_OS2 */
268 APIRET orc;
269 ULONG ulUser = 0;
270 uint16_t cSockets = pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets;
271 if (cSockets == 0)
272 {
273 /* Only pipes. */
274 AssertReturn(pThis->cPipes > 0, VERR_INTERNAL_ERROR_2);
275 orc = DosWaitMuxWaitSem(pThis->hmux,
276 cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : RT_MIN(cMillies, SEM_INDEFINITE_WAIT - 1),
277 &ulUser);
278 rc = RTErrConvertFromOS2(orc);
279 }
280 else
281 {
282 int *pafdSelect = (int *)alloca(cSockets + 1);
283 if (pThis->cPipes == 0)
284 {
285 /* Only sockets. */
286 memcpy(pafdSelect, pThis->pafdSelect, sizeof(pThis->pafdSelect[0]) * (cSockets + 1));
287 rc = os2_select(pafdSelect, pThis->cReadSockets, pThis->cWriteSockets, pThis->cXcptSockets,
288 cMillies == RT_INDEFINITE_WAIT ? -1 : (long)RT_MIN(cMillies, LONG_MAX));
289 if (rc > 0)
290 rc = VINF_SUCCESS;
291 else if (rc == 0)
292 rc = VERR_TIMEOUT;
293 else
294 rc = RTErrConvertFromErrno(sock_errno());
295 }
296 else
297 {
298 /* Mix of both - taking the easy way out, not optimal, but whatever... */
299 do
300 {
301 orc = DosWaitMuxWaitSem(pThis->hmux, 8, &ulUser);
302 if (orc != ERROR_TIMEOUT && orc != ERROR_SEM_TIMEOUT)
303 {
304 rc = RTErrConvertFromOS2(orc);
305 break;
306 }
307
308 memcpy(pafdSelect, pThis->pafdSelect, sizeof(pThis->pafdSelect[0]) * (cSockets + 1));
309 rc = os2_select(pafdSelect, pThis->cReadSockets, pThis->cWriteSockets, pThis->cXcptSockets, 8);
310 if (rc != 0)
311 {
312 if (rc > 0)
313 rc = VINF_SUCCESS;
314 else
315 rc = RTErrConvertFromErrno(sock_errno());
316 break;
317 }
318 } while (cMillies == RT_INDEFINITE_WAIT || RTTimeMilliTS() - MsStart < cMillies);
319 }
320 }
321# endif /* RT_OS_OS2 */
322
323 /*
324 * Get event (if pending) and do wait cleanup.
325 */
326 bool fHarvestEvents = true;
327 for (i = 0; i < cHandles; i++)
328 {
329 fEvents = 0;
330 switch (pThis->paHandles[i].enmType)
331 {
332 case RTHANDLETYPE_PIPE:
333 fEvents = rtPipePollDone(pThis->paHandles[i].u.hPipe, pThis->paHandles[i].fEvents,
334 pThis->paHandles[i].fFinalEntry, fHarvestEvents);
335 break;
336
337 case RTHANDLETYPE_SOCKET:
338 fEvents = rtSocketPollDone(pThis->paHandles[i].u.hSocket, pThis->paHandles[i].fEvents,
339 pThis->paHandles[i].fFinalEntry, fHarvestEvents);
340 break;
341
342 default:
343 AssertFailed();
344 break;
345 }
346 if ( fEvents
347 && fHarvestEvents)
348 {
349 Assert(fEvents != UINT32_MAX);
350 fHarvestEvents = false;
351 if (pfEvents)
352 *pfEvents = fEvents;
353 if (pid)
354 *pid = pThis->paHandles[i].id;
355 rc = VINF_SUCCESS;
356 }
357 }
358
359#else /* POSIX */
360
361 RT_NOREF_PV(MsStart);
362
363 /* clear the revents. */
364 uint32_t i = pThis->cHandles;
365 while (i-- > 0)
366 pThis->paPollFds[i].revents = 0;
367
368 rc = poll(&pThis->paPollFds[0], pThis->cHandles,
369 cMillies == RT_INDEFINITE_WAIT || cMillies >= INT_MAX
370 ? -1
371 : (int)cMillies);
372 if (rc == 0)
373 return VERR_TIMEOUT;
374 if (rc < 0)
375 return RTErrConvertFromErrno(errno);
376 for (i = 0; i < pThis->cHandles; i++)
377 if (pThis->paPollFds[i].revents)
378 {
379 if (pfEvents)
380 {
381 *pfEvents = 0;
382 if (pThis->paPollFds[i].revents & (POLLIN
383# ifdef POLLRDNORM
384 | POLLRDNORM /* just in case */
385# endif
386# ifdef POLLRDBAND
387 | POLLRDBAND /* ditto */
388# endif
389# ifdef POLLPRI
390 | POLLPRI /* ditto */
391# endif
392# ifdef POLLMSG
393 | POLLMSG /* ditto */
394# endif
395# ifdef POLLWRITE
396 | POLLWRITE /* ditto */
397# endif
398# ifdef POLLEXTEND
399 | POLLEXTEND /* ditto */
400# endif
401 )
402 )
403 *pfEvents |= RTPOLL_EVT_READ;
404
405 if (pThis->paPollFds[i].revents & (POLLOUT
406# ifdef POLLWRNORM
407 | POLLWRNORM /* just in case */
408# endif
409# ifdef POLLWRBAND
410 | POLLWRBAND /* ditto */
411# endif
412 )
413 )
414 *pfEvents |= RTPOLL_EVT_WRITE;
415
416 if (pThis->paPollFds[i].revents & (POLLERR | POLLHUP | POLLNVAL
417# ifdef POLLRDHUP
418 | POLLRDHUP
419# endif
420 )
421 )
422 *pfEvents |= RTPOLL_EVT_ERROR;
423
424# if defined(RT_OS_SOLARIS)
425 /* Solaris does not return POLLHUP for sockets, just POLLIN. Check if a
426 POLLIN should also have RTPOLL_EVT_ERROR set or not, so we present a
427 behaviour more in line with linux and BSDs. Note that this will not
428 help is only RTPOLL_EVT_ERROR was requested, that will require
429 extending this hack quite a bit further (restart poll): */
430 if ( *pfEvents == RTPOLL_EVT_READ
431 && pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
432 {
433 uint8_t abBuf[64];
434 ssize_t rcRecv = recv(pThis->paPollFds[i].fd, abBuf, sizeof(abBuf), MSG_PEEK | MSG_DONTWAIT);
435 if (rcRecv == 0)
436 *pfEvents |= RTPOLL_EVT_ERROR;
437 }
438# endif
439 }
440 if (pid)
441 *pid = pThis->paHandles[i].id;
442 return VINF_SUCCESS;
443 }
444
445 AssertFailed();
446 RTThreadYield();
447 rc = VERR_INTERRUPTED;
448
449#endif /* POSIX */
450
451 return rc;
452}
453
454
455RTDECL(int) RTPoll(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
456{
457 RTPOLLSETINTERNAL *pThis = hPollSet;
458 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
459 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
460 AssertPtrNull(pfEvents);
461 AssertPtrNull(pid);
462
463 /*
464 * Set the busy flag and do the job.
465 */
466 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
467
468 int rc;
469 if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
470 {
471 do rc = rtPollNoResumeWorker(pThis, 0, cMillies, pfEvents, pid);
472 while (rc == VERR_INTERRUPTED);
473 }
474 else
475 {
476 uint64_t MsStart = RTTimeMilliTS();
477 rc = rtPollNoResumeWorker(pThis, MsStart, cMillies, pfEvents, pid);
478 while (RT_UNLIKELY(rc == VERR_INTERRUPTED))
479 {
480 if (RTTimeMilliTS() - MsStart >= cMillies)
481 {
482 rc = VERR_TIMEOUT;
483 break;
484 }
485 rc = rtPollNoResumeWorker(pThis, MsStart, cMillies, pfEvents, pid);
486 }
487 }
488
489 ASMAtomicWriteBool(&pThis->fBusy, false);
490
491 return rc;
492}
493
494
495RTDECL(int) RTPollNoResume(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
496{
497 RTPOLLSETINTERNAL *pThis = hPollSet;
498 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
499 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
500 AssertPtrNull(pfEvents);
501 AssertPtrNull(pid);
502
503 /*
504 * Set the busy flag and do the job.
505 */
506 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
507
508 int rc;
509 if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
510 rc = rtPollNoResumeWorker(pThis, 0, cMillies, pfEvents, pid);
511 else
512 rc = rtPollNoResumeWorker(pThis, RTTimeMilliTS(), cMillies, pfEvents, pid);
513
514 ASMAtomicWriteBool(&pThis->fBusy, false);
515
516 return rc;
517}
518
519
520RTDECL(int) RTPollSetCreate(PRTPOLLSET phPollSet)
521{
522 AssertPtrReturn(phPollSet, VERR_INVALID_POINTER);
523 RTPOLLSETINTERNAL *pThis = (RTPOLLSETINTERNAL *)RTMemAlloc(sizeof(RTPOLLSETINTERNAL));
524 if (!pThis)
525 return VERR_NO_MEMORY;
526
527 pThis->fBusy = false;
528 pThis->cHandles = 0;
529 pThis->cHandlesAllocated = 0;
530#ifdef RT_OS_WINDOWS
531 pThis->pahNative = NULL;
532#elif defined(RT_OS_OS2)
533 pThis->hmux = NULLHANDLE;
534 APIRET orc = DosCreateMuxWaitSem(NULL, &pThis->hmux, 0, NULL, DCMW_WAIT_ANY);
535 if (orc != NO_ERROR)
536 {
537 RTMemFree(pThis);
538 return RTErrConvertFromOS2(orc);
539 }
540 pThis->pafdSelect = NULL;
541 pThis->cReadSockets = 0;
542 pThis->cWriteSockets = 0;
543 pThis->cXcptSockets = 0;
544 pThis->cPipes = 0;
545 pThis->pahNative = NULL;
546#else
547 pThis->paPollFds = NULL;
548#endif
549 pThis->paHandles = NULL;
550 pThis->u32Magic = RTPOLLSET_MAGIC;
551
552 *phPollSet = pThis;
553 return VINF_SUCCESS;
554}
555
556
557RTDECL(int) RTPollSetDestroy(RTPOLLSET hPollSet)
558{
559 RTPOLLSETINTERNAL *pThis = hPollSet;
560 if (pThis == NIL_RTPOLLSET)
561 return VINF_SUCCESS;
562 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
563 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
564 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
565
566 ASMAtomicWriteU32(&pThis->u32Magic, ~RTPOLLSET_MAGIC);
567#ifdef RT_OS_WINDOWS
568 RTMemFree(pThis->pahNative);
569 pThis->pahNative = NULL;
570#elif defined(RT_OS_OS2)
571 DosCloseMuxWaitSem(pThis->hmux);
572 pThis->hmux = NULLHANDLE;
573 RTMemFree(pThis->pafdSelect);
574 pThis->pafdSelect = NULL;
575 RTMemFree(pThis->pahNative);
576 pThis->pahNative = NULL;
577#else
578 RTMemFree(pThis->paPollFds);
579 pThis->paPollFds = NULL;
580#endif
581 RTMemFree(pThis->paHandles);
582 pThis->paHandles = NULL;
583 RTMemFree(pThis);
584
585 return VINF_SUCCESS;
586}
587
588#ifdef RT_OS_OS2
589
590/**
591 * Checks if @a fd is in the specific socket subset.
592 *
593 * @returns true / false.
594 * @param pThis The poll set instance.
595 * @param iStart The index to start at.
596 * @param cFds The number of sockets to check.
597 * @param fd The socket to look for.
598 */
599static bool rtPollSetOs2IsSocketInSet(RTPOLLSETINTERNAL *pThis, uint16_t iStart, uint16_t cFds, int fd)
600{
601 int const *pfd = pThis->pafdSelect + iStart;
602 while (cFds-- > 0)
603 {
604 if (*pfd == fd)
605 return true;
606 pfd++;
607 }
608 return false;
609}
610
611
612/**
613 * Removes a socket from a select template subset.
614 *
615 * @param pThis The poll set instance.
616 * @param iStart The index to start at.
617 * @param pcSubSet The subset counter to decrement.
618 * @param fd The socket to remove.
619 */
620static void rtPollSetOs2RemoveSocket(RTPOLLSETINTERNAL *pThis, uint16_t iStart, uint16_t *pcFds, int fd)
621{
622 uint16_t cFds = *pcFds;
623 while (cFds-- > 0)
624 {
625 if (pThis->pafdSelect[iStart] == fd)
626 break;
627 iStart++;
628 }
629 AssertReturnVoid(iStart != UINT16_MAX);
630
631 /* Note! We keep a -1 entry at the end of the set, thus the + 1. */
632 memmove(&pThis->pafdSelect[iStart],
633 &pThis->pafdSelect[iStart + 1],
634 pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets + 1 - 1 - iStart);
635 *pcFds -= 1;
636
637 Assert(pThis->pafdSelect[pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets] == -1);
638}
639
640
641/**
642 * Adds a socket to a select template subset.
643 *
644 * @param pThis The poll set instance.
645 * @param iInsert The insertion point.
646 * ASSUMED to be at the end of the subset.
647 * @param pcSubSet The subset counter to increment.
648 * @param fd The socket to add.
649 */
650static void rtPollSetOs2AddSocket(RTPOLLSETINTERNAL *pThis, uint16_t iInsert, uint16_t *pcFds, int fd)
651{
652 Assert(!rtPollSetOs2IsSocketInSet(pThis, iInsert - *pcFds, *pcFds, fd));
653
654 /* Note! We keep a -1 entry at the end of the set, thus the + 1. */
655 memmove(&pThis->pafdSelect[iInsert + 1],
656 &pThis->pafdSelect[iInsert],
657 pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets + 1 - iInsert);
658 pThis->pafdSelect[iInsert] = fd;
659 *pcFds += 1;
660
661 Assert(pThis->pafdSelect[pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets] == -1);
662}
663
664
665/**
666 * OS/2 specific RTPollSetAdd worker.
667 *
668 * @returns IPRT status code.
669 * @param pThis The poll set instance.
670 * @param i The index of the new handle (not committed).
671 * @param fEvents The events to poll for.
672 */
673static int rtPollSetOs2Add(RTPOLLSETINTERNAL *pThis, unsigned i, uint32_t fEvents)
674{
675 if (pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
676 {
677 int const fdSocket = pThis->pahNative[i];
678 if ( (fEvents & RTPOLL_EVT_READ)
679 && rtPollSetOs2IsSocketInSet(pThis, 0, pThis->cReadSockets, fdSocket))
680 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets, &pThis->cReadSockets, fdSocket);
681
682 if ( (fEvents & RTPOLL_EVT_WRITE)
683 && rtPollSetOs2IsSocketInSet(pThis, pThis->cReadSockets, pThis->cWriteSockets, fdSocket))
684 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cWriteSockets, fdSocket);
685
686 if ( (fEvents & RTPOLL_EVT_ERROR)
687 && rtPollSetOs2IsSocketInSet(pThis, pThis->cReadSockets + pThis->cWriteSockets, pThis->cXcptSockets, fdSocket))
688 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets,
689 &pThis->cXcptSockets, fdSocket);
690 }
691 else if (pThis->paHandles[i].enmType == RTHANDLETYPE_PIPE)
692 {
693 SEMRECORD Rec = { (HSEM)pThis->pahNative[i], pThis->paHandles[i].id };
694 APIRET orc = DosAddMuxWaitSem(pThis->hmux, &Rec);
695 if (orc != NO_ERROR && orc != ERROR_DUPLICATE_HANDLE)
696 return RTErrConvertFromOS2(orc);
697 pThis->cPipes++;
698 }
699 else
700 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
701 return VINF_SUCCESS;
702}
703
704#endif /* RT_OS_OS2 */
705
706/**
707 * Grows the poll set.
708 *
709 * @returns VINF_SUCCESS or VERR_NO_MEMORY.
710 * @param pThis The poll set instance.
711 * @param cHandlesNew The new poll set size.
712 */
713static int rtPollSetGrow(RTPOLLSETINTERNAL *pThis, uint32_t cHandlesNew)
714{
715 Assert(cHandlesNew > pThis->cHandlesAllocated);
716
717 /* The common array. */
718 void *pvNew = RTMemRealloc(pThis->paHandles, cHandlesNew * sizeof(pThis->paHandles[0]));
719 if (!pvNew)
720 return VERR_NO_MEMORY;
721 pThis->paHandles = (PRTPOLLSETHNDENT)pvNew;
722
723
724 /* OS specific handles */
725#if defined(RT_OS_WINDOWS)
726 pvNew = RTMemRealloc(pThis->pahNative, cHandlesNew * sizeof(pThis->pahNative[0]));
727 if (!pvNew)
728 return VERR_NO_MEMORY;
729 pThis->pahNative = (HANDLE *)pvNew;
730
731#elif defined(RT_OS_OS2)
732 pvNew = RTMemRealloc(pThis->pahNative, cHandlesNew * sizeof(pThis->pahNative[0]));
733 if (!pvNew)
734 return VERR_NO_MEMORY;
735 pThis->pahNative = (PRTHCINTPTR)pvNew;
736
737 pvNew = RTMemRealloc(pThis->pafdSelect, (cHandlesNew * 3 + 1) * sizeof(pThis->pafdSelect[0]));
738 if (!pvNew)
739 return VERR_NO_MEMORY;
740 pThis->pafdSelect = (int *)pvNew;
741 if (pThis->cHandlesAllocated == 0)
742 pThis->pafdSelect[0] = -1;
743
744#else
745 pvNew = RTMemRealloc(pThis->paPollFds, cHandlesNew * sizeof(pThis->paPollFds[0]));
746 if (!pvNew)
747 return VERR_NO_MEMORY;
748 pThis->paPollFds = (struct pollfd *)pvNew;
749
750#endif
751
752 pThis->cHandlesAllocated = (uint16_t)cHandlesNew;
753 return VINF_SUCCESS;
754}
755
756
757RTDECL(int) RTPollSetAdd(RTPOLLSET hPollSet, PCRTHANDLE pHandle, uint32_t fEvents, uint32_t id)
758{
759 /*
760 * Validate the input (tedious).
761 */
762 RTPOLLSETINTERNAL *pThis = hPollSet;
763 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
764 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
765 AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
766 AssertReturn(fEvents, VERR_INVALID_PARAMETER);
767 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
768
769 if (!pHandle)
770 return VINF_SUCCESS;
771 AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
772 AssertReturn(pHandle->enmType > RTHANDLETYPE_INVALID && pHandle->enmType < RTHANDLETYPE_END, VERR_INVALID_PARAMETER);
773
774 /*
775 * Set the busy flag and do the job.
776 */
777
778 int rc = VINF_SUCCESS;
779 RTHCINTPTR hNative = -1;
780 RTHANDLEUNION uh;
781 uh.uInt = 0;
782 switch (pHandle->enmType)
783 {
784 case RTHANDLETYPE_PIPE:
785 uh.hPipe = pHandle->u.hPipe;
786 if (uh.hPipe == NIL_RTPIPE)
787 return VINF_SUCCESS;
788 rc = rtPipePollGetHandle(uh.hPipe, fEvents, &hNative);
789 break;
790
791 case RTHANDLETYPE_SOCKET:
792 uh.hSocket = pHandle->u.hSocket;
793 if (uh.hSocket == NIL_RTSOCKET)
794 return VINF_SUCCESS;
795 rc = rtSocketPollGetHandle(uh.hSocket, fEvents, &hNative);
796 break;
797
798 case RTHANDLETYPE_FILE:
799 AssertMsgFailed(("Files are always ready for reading/writing and thus not pollable. Use native APIs for special devices.\n"));
800 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
801 break;
802
803 case RTHANDLETYPE_THREAD:
804 AssertMsgFailed(("Thread handles are currently not pollable\n"));
805 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
806 break;
807
808 default:
809 AssertMsgFailed(("\n"));
810 rc = VERR_POLL_HANDLE_NOT_POLLABLE;
811 break;
812 }
813 if (RT_SUCCESS(rc))
814 {
815 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
816
817 uint32_t const i = pThis->cHandles;
818
819 /* Check that the handle ID doesn't exist already. */
820 uint32_t iPrev = UINT32_MAX;
821 uint32_t j = i;
822 while (j-- > 0)
823 {
824 if (pThis->paHandles[j].id == id)
825 {
826 rc = VERR_POLL_HANDLE_ID_EXISTS;
827 break;
828 }
829 if ( pThis->paHandles[j].enmType == pHandle->enmType
830 && pThis->paHandles[j].u.uInt == uh.uInt)
831 iPrev = j;
832 }
833
834 /* Check that we won't overflow the poll set now. */
835 if ( RT_SUCCESS(rc)
836 && i + 1 > RTPOLL_SET_MAX)
837 rc = VERR_POLL_SET_IS_FULL;
838
839 /* Grow the tables if necessary. */
840 if (RT_SUCCESS(rc) && i + 1 > pThis->cHandlesAllocated)
841 rc = rtPollSetGrow(pThis, pThis->cHandlesAllocated + 32);
842 if (RT_SUCCESS(rc))
843 {
844 /*
845 * Add the handles to the two parallel arrays.
846 */
847#ifdef RT_OS_WINDOWS
848 pThis->pahNative[i] = (HANDLE)hNative;
849#elif defined(RT_OS_OS2)
850 pThis->pahNative[i] = hNative;
851#else
852 pThis->paPollFds[i].fd = (int)hNative;
853 pThis->paPollFds[i].revents = 0;
854 pThis->paPollFds[i].events = 0;
855 if (fEvents & RTPOLL_EVT_READ)
856 pThis->paPollFds[i].events |= POLLIN;
857 if (fEvents & RTPOLL_EVT_WRITE)
858 pThis->paPollFds[i].events |= POLLOUT;
859 if (fEvents & RTPOLL_EVT_ERROR)
860# ifdef RT_OS_DARWIN
861 pThis->paPollFds[i].events |= POLLERR | POLLHUP;
862# else
863 pThis->paPollFds[i].events |= POLLERR;
864# endif
865#endif
866 pThis->paHandles[i].enmType = pHandle->enmType;
867 pThis->paHandles[i].u = uh;
868 pThis->paHandles[i].id = id;
869 pThis->paHandles[i].fEvents = fEvents;
870 pThis->paHandles[i].fFinalEntry = true;
871
872 if (iPrev != UINT32_MAX)
873 {
874 Assert(pThis->paHandles[iPrev].fFinalEntry);
875 pThis->paHandles[iPrev].fFinalEntry = false;
876 }
877
878 /*
879 * Validations and OS specific updates.
880 */
881#ifdef RT_OS_WINDOWS
882 /* none */
883#elif defined(RT_OS_OS2)
884 rc = rtPollSetOs2Add(pThis, i, fEvents);
885#else /* POSIX */
886 if (poll(&pThis->paPollFds[i], 1, 0) < 0)
887 {
888 rc = RTErrConvertFromErrno(errno);
889 pThis->paPollFds[i].fd = -1;
890 }
891#endif /* POSIX */
892
893 if (RT_SUCCESS(rc))
894 {
895 /*
896 * Commit it to the set.
897 */
898 pThis->cHandles++; Assert(pThis->cHandles == i + 1);
899 rc = VINF_SUCCESS;
900 }
901 }
902 }
903
904 ASMAtomicWriteBool(&pThis->fBusy, false);
905 return rc;
906}
907
908
909RTDECL(int) RTPollSetRemove(RTPOLLSET hPollSet, uint32_t id)
910{
911 /*
912 * Validate the input.
913 */
914 RTPOLLSETINTERNAL *pThis = hPollSet;
915 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
916 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
917 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
918
919 /*
920 * Set the busy flag and do the job.
921 */
922 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
923
924 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
925 uint32_t i = pThis->cHandles;
926 while (i-- > 0)
927 if (pThis->paHandles[i].id == id)
928 {
929 /* Save some details for the duplicate searching. */
930 bool const fFinalEntry = pThis->paHandles[i].fFinalEntry;
931 RTHANDLETYPE const enmType = pThis->paHandles[i].enmType;
932 RTHANDLEUNION const uh = pThis->paHandles[i].u;
933#ifdef RT_OS_OS2
934 uint32_t fRemovedEvents = pThis->paHandles[i].fEvents;
935 RTHCINTPTR const hNative = pThis->pahNative[i];
936#endif
937
938 /* Remove the entry. */
939 pThis->cHandles--;
940 size_t const cToMove = pThis->cHandles - i;
941 if (cToMove)
942 {
943 memmove(&pThis->paHandles[i], &pThis->paHandles[i + 1], cToMove * sizeof(pThis->paHandles[i]));
944#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
945 memmove(&pThis->pahNative[i], &pThis->pahNative[i + 1], cToMove * sizeof(pThis->pahNative[i]));
946#else
947 memmove(&pThis->paPollFds[i], &pThis->paPollFds[i + 1], cToMove * sizeof(pThis->paPollFds[i]));
948#endif
949 }
950
951 /* Check for duplicate and set the fFinalEntry flag. */
952 if (fFinalEntry)
953 while (i-- > 0)
954 if ( pThis->paHandles[i].u.uInt == uh.uInt
955 && pThis->paHandles[i].enmType == enmType)
956 {
957 Assert(!pThis->paHandles[i].fFinalEntry);
958 pThis->paHandles[i].fFinalEntry = true;
959 break;
960 }
961
962#ifdef RT_OS_OS2
963 /*
964 * Update OS/2 wait structures.
965 */
966 uint32_t fNewEvents = 0;
967 i = pThis->cHandles;
968 while (i-- > 0)
969 if ( pThis->paHandles[i].u.uInt == uh.uInt
970 && pThis->paHandles[i].enmType == enmType)
971 fNewEvents |= pThis->paHandles[i].fEvents;
972 if (enmType == RTHANDLETYPE_PIPE)
973 {
974 pThis->cPipes--;
975 if (fNewEvents == 0)
976 {
977 APIRET orc = DosDeleteMuxWaitSem(pThis->hmux, (HSEM)hNative);
978 AssertMsg(orc == NO_ERROR, ("%d\n", orc));
979 }
980 }
981 else if ( fNewEvents != (fNewEvents | fRemovedEvents)
982 && enmType == RTHANDLETYPE_SOCKET)
983 {
984 fRemovedEvents = fNewEvents ^ (fNewEvents | fRemovedEvents);
985 if (fRemovedEvents & RTPOLL_EVT_ERROR)
986 rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cXcptSockets, (int)hNative);
987 if (fRemovedEvents & RTPOLL_EVT_WRITE)
988 rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets, &pThis->cWriteSockets, (int)hNative);
989 if (fRemovedEvents & RTPOLL_EVT_READ)
990 rtPollSetOs2RemoveSocket(pThis, 0, &pThis->cReadSockets, (int)hNative);
991 }
992#endif /* RT_OS_OS2 */
993 rc = VINF_SUCCESS;
994 break;
995 }
996
997 ASMAtomicWriteBool(&pThis->fBusy, false);
998 return rc;
999}
1000
1001
1002RTDECL(int) RTPollSetQueryHandle(RTPOLLSET hPollSet, uint32_t id, PRTHANDLE pHandle)
1003{
1004 /*
1005 * Validate the input.
1006 */
1007 RTPOLLSETINTERNAL *pThis = hPollSet;
1008 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1009 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
1010 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
1011 AssertPtrNullReturn(pHandle, VERR_INVALID_POINTER);
1012
1013 /*
1014 * Set the busy flag and do the job.
1015 */
1016 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
1017
1018 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
1019 uint32_t i = pThis->cHandles;
1020 while (i-- > 0)
1021 if (pThis->paHandles[i].id == id)
1022 {
1023 if (pHandle)
1024 {
1025 pHandle->enmType = pThis->paHandles[i].enmType;
1026 pHandle->u = pThis->paHandles[i].u;
1027 }
1028 rc = VINF_SUCCESS;
1029 break;
1030 }
1031
1032 ASMAtomicWriteBool(&pThis->fBusy, false);
1033 return rc;
1034}
1035
1036
1037RTDECL(uint32_t) RTPollSetGetCount(RTPOLLSET hPollSet)
1038{
1039 /*
1040 * Validate the input.
1041 */
1042 RTPOLLSETINTERNAL *pThis = hPollSet;
1043 AssertPtrReturn(pThis, UINT32_MAX);
1044 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, UINT32_MAX);
1045
1046 /*
1047 * Set the busy flag and do the job.
1048 */
1049 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), UINT32_MAX);
1050 uint32_t cHandles = pThis->cHandles;
1051 ASMAtomicWriteBool(&pThis->fBusy, false);
1052
1053 return cHandles;
1054}
1055
1056RTDECL(int) RTPollSetEventsChange(RTPOLLSET hPollSet, uint32_t id, uint32_t fEvents)
1057{
1058 /*
1059 * Validate the input.
1060 */
1061 RTPOLLSETINTERNAL *pThis = hPollSet;
1062 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1063 AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
1064 AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
1065 AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
1066 AssertReturn(fEvents, VERR_INVALID_PARAMETER);
1067
1068 /*
1069 * Set the busy flag and do the job.
1070 */
1071 AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
1072
1073 int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
1074 uint32_t i = pThis->cHandles;
1075 while (i-- > 0)
1076 if (pThis->paHandles[i].id == id)
1077 {
1078 if (pThis->paHandles[i].fEvents != fEvents)
1079 {
1080#if defined(RT_OS_WINDOWS)
1081 /*nothing*/
1082#elif defined(RT_OS_OS2)
1083 if (pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
1084 {
1085 uint32_t fOldEvents = 0;
1086 uint32_t j = pThis->cHandles;
1087 while (j-- > 0)
1088 if ( pThis->paHandles[j].enmType == RTHANDLETYPE_SOCKET
1089 && pThis->paHandles[j].u.uInt == pThis->paHandles[i].u.uInt
1090 && j != i)
1091 fOldEvents |= pThis->paHandles[j].fEvents;
1092 uint32_t fNewEvents = fOldEvents | fEvents;
1093 fOldEvents |= pThis->paHandles[i].fEvents;
1094 if (fOldEvents != fEvents)
1095 {
1096 int const fdSocket = pThis->pahNative[i];
1097 uint32_t const fChangedEvents = fOldEvents ^ fNewEvents;
1098
1099 if ((fChangedEvents & RTPOLL_EVT_READ) && (fNewEvents & RTPOLL_EVT_READ))
1100 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets, &pThis->cReadSockets, fdSocket);
1101 else if (fChangedEvents & RTPOLL_EVT_READ)
1102 rtPollSetOs2RemoveSocket(pThis, 0, &pThis->cReadSockets, fdSocket);
1103
1104 if ((fChangedEvents & RTPOLL_EVT_WRITE) && (fNewEvents & RTPOLL_EVT_WRITE))
1105 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets,
1106 &pThis->cWriteSockets, fdSocket);
1107 else if (fChangedEvents & RTPOLL_EVT_WRITE)
1108 rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets, &pThis->cWriteSockets, fdSocket);
1109
1110 if ((fChangedEvents & RTPOLL_EVT_ERROR) && (fNewEvents & RTPOLL_EVT_ERROR))
1111 rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets,
1112 &pThis->cXcptSockets, fdSocket);
1113 else if (fChangedEvents & RTPOLL_EVT_ERROR)
1114 rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cXcptSockets,
1115 fdSocket);
1116 }
1117 }
1118#else
1119 pThis->paPollFds[i].events = 0;
1120 if (fEvents & RTPOLL_EVT_READ)
1121 pThis->paPollFds[i].events |= POLLIN;
1122 if (fEvents & RTPOLL_EVT_WRITE)
1123 pThis->paPollFds[i].events |= POLLOUT;
1124 if (fEvents & RTPOLL_EVT_ERROR)
1125 pThis->paPollFds[i].events |= POLLERR;
1126#endif
1127 pThis->paHandles[i].fEvents = fEvents;
1128 }
1129 rc = VINF_SUCCESS;
1130 break;
1131 }
1132
1133 ASMAtomicWriteBool(&pThis->fBusy, false);
1134 return rc;
1135}
1136
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use