VirtualBox

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use