VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/pipe-win.cpp@ 27509

Last change on this file since 27509 was 27509, checked in by vboxsync, 15 years ago

iprt: Poll on sockets on windows (untested). RTPollSetCount -> RTPollSetGetCount.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.1 KB
Line 
1/* $Id: pipe-win.cpp 27509 2010-03-18 23:47:16Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <Windows.h>
36
37#include <iprt/pipe.h>
38#include "internal/iprt.h"
39
40#include <iprt/asm.h>
41#include <iprt/assert.h>
42#include <iprt/critsect.h>
43#include <iprt/err.h>
44#include <iprt/mem.h>
45#include <iprt/string.h>
46#include <iprt/poll.h>
47#include <iprt/process.h>
48#include <iprt/thread.h>
49#include <iprt/time.h>
50#include "internal/pipe.h"
51#include "internal/magics.h"
52
53
54/*******************************************************************************
55* Defined Constants And Macros *
56*******************************************************************************/
57/** The pipe buffer size we prefere. */
58#define RTPIPE_NT_SIZE _64K
59
60
61/*******************************************************************************
62* Structures and Typedefs *
63*******************************************************************************/
64typedef struct RTPIPEINTERNAL
65{
66 /** Magic value (RTPIPE_MAGIC). */
67 uint32_t u32Magic;
68 /** The pipe handle. */
69 HANDLE hPipe;
70 /** Set if this is the read end, clear if it's the write end. */
71 bool fRead;
72 /** Set if there is already pending I/O. */
73 bool fIOPending;
74 /** Set if the zero byte read that the poll code using is pending. */
75 bool fZeroByteRead;
76 /** Set if the pipe is broken. */
77 bool fBrokenPipe;
78 /** Set if we've promised that the handle is writable. */
79 bool fPromisedWritable;
80 /** The number of users of the current mode. */
81 uint32_t cModeUsers;
82 /** The overlapped I/O structure we use. */
83 OVERLAPPED Overlapped;
84 /** Bounce buffer for writes. */
85 uint8_t *pbBounceBuf;
86 /** Amount of used buffer space. */
87 size_t cbBounceBufUsed;
88 /** Amount of allocated buffer space. */
89 size_t cbBounceBufAlloc;
90 /** The handle of the poll set currently polling on this pipe.
91 * We can only have one poller at the time (lazy bird). */
92 RTPOLLSET hPollSet;
93 /** The number of references to the handle in hPollSet. */
94 uint32_t cPolls;
95 /** Critical section protecting the above members.
96 * (Taking the lazy/simple approach.) */
97 RTCRITSECT CritSect;
98 /** Buffer for the zero byte read. */
99 uint8_t abBuf[8];
100} RTPIPEINTERNAL;
101
102
103/* from ntdef.h */
104typedef LONG NTSTATUS;
105
106/* from ntddk.h */
107typedef struct _IO_STATUS_BLOCK {
108 union {
109 NTSTATUS Status;
110 PVOID Pointer;
111 };
112 ULONG_PTR Information;
113} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
114
115typedef enum _FILE_INFORMATION_CLASS {
116 FilePipeInformation = 23,
117 FilePipeLocalInformation = 24,
118 FilePipeRemoteInformation = 25,
119} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
120
121/* from ntifs.h */
122typedef struct _FILE_PIPE_LOCAL_INFORMATION {
123 ULONG NamedPipeType;
124 ULONG NamedPipeConfiguration;
125 ULONG MaximumInstances;
126 ULONG CurrentInstances;
127 ULONG InboundQuota;
128 ULONG ReadDataAvailable;
129 ULONG OutboundQuota;
130 ULONG WriteQuotaAvailable;
131 ULONG NamedPipeState;
132 ULONG NamedPipeEnd;
133} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
134
135#define FILE_PIPE_DISCONNECTED_STATE 0x00000001
136#define FILE_PIPE_LISTENING_STATE 0x00000002
137#define FILE_PIPE_CONNECTED_STATE 0x00000003
138#define FILE_PIPE_CLOSING_STATE 0x00000004
139
140
141extern "C" NTSYSAPI NTSTATUS WINAPI NtQueryInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, LONG, FILE_INFORMATION_CLASS);
142
143
144/**
145 * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
146 *
147 * @returns Success inidicator (true/false).
148 * @param pThis The pipe.
149 * @param pInfo The info structure.
150 */
151static bool rtPipeQueryInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
152{
153 IO_STATUS_BLOCK Ios;
154 RT_ZERO(Ios);
155 RT_ZERO(*pInfo);
156 NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
157 return rcNt >= 0;
158}
159
160
161RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
162{
163 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
164 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
165 AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
166
167 /*
168 * Create the read end of the pipe.
169 */
170 DWORD dwErr;
171 HANDLE hPipeR;
172 HANDLE hPipeW;
173 int rc;
174 for (;;)
175 {
176 static volatile uint32_t g_iNextPipe = 0;
177 char szName[128];
178 RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
179
180 SECURITY_ATTRIBUTES SecurityAttributes;
181 PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
182 if (fFlags & RTPIPE_C_INHERIT_READ)
183 {
184 SecurityAttributes.nLength = sizeof(SecurityAttributes);
185 SecurityAttributes.lpSecurityDescriptor = NULL;
186 SecurityAttributes.bInheritHandle = TRUE;
187 pSecurityAttributes = &SecurityAttributes;
188 }
189
190 DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
191#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
192 dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
193#endif
194
195 DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
196#ifdef PIPE_REJECT_REMOTE_CLIENTS
197 dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
198#endif
199
200 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
201 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
202#ifdef PIPE_REJECT_REMOTE_CLIENTS
203 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
204 {
205 dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
206 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
207 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
208 }
209#endif
210#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
211 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
212 {
213 dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
214 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
215 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
216 }
217#endif
218 if (hPipeR != INVALID_HANDLE_VALUE)
219 {
220 /*
221 * Connect to the pipe (the write end).
222 * We add FILE_READ_ATTRIBUTES here to make sure we can query the
223 * pipe state later on.
224 */
225 pSecurityAttributes = NULL;
226 if (fFlags & RTPIPE_C_INHERIT_WRITE)
227 {
228 SecurityAttributes.nLength = sizeof(SecurityAttributes);
229 SecurityAttributes.lpSecurityDescriptor = NULL;
230 SecurityAttributes.bInheritHandle = TRUE;
231 pSecurityAttributes = &SecurityAttributes;
232 }
233
234 hPipeW = CreateFileA(szName,
235 GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
236 0 /*dwShareMode*/,
237 pSecurityAttributes,
238 OPEN_EXISTING /* dwCreationDisposition */,
239 FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
240 NULL /*hTemplateFile*/);
241 if (hPipeW != INVALID_HANDLE_VALUE)
242 break;
243 dwErr = GetLastError();
244 CloseHandle(hPipeR);
245 }
246 else
247 dwErr = GetLastError();
248 if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
249 && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
250 return RTErrConvertFromWin32(dwErr);
251 /* else: try again with a new name */
252 }
253
254 /*
255 * Create the two handles.
256 */
257 RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
258 if (pThisR)
259 {
260 RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
261 if (pThisW)
262 {
263 rc = RTCritSectInit(&pThisR->CritSect);
264 if (RT_SUCCESS(rc))
265 {
266 rc = RTCritSectInit(&pThisW->CritSect);
267 if (RT_SUCCESS(rc))
268 {
269 pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
270 TRUE /*fInitialState*/, NULL /*pName*/);
271 if (pThisR->Overlapped.hEvent != NULL)
272 {
273 pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
274 TRUE /*fInitialState*/, NULL /*pName*/);
275 if (pThisW->Overlapped.hEvent != NULL)
276 {
277 pThisR->u32Magic = RTPIPE_MAGIC;
278 pThisW->u32Magic = RTPIPE_MAGIC;
279 pThisR->hPipe = hPipeR;
280 pThisW->hPipe = hPipeW;
281 pThisR->fRead = true;
282 pThisW->fRead = false;
283 //pThisR->fIOPending = false;
284 //pThisW->fIOPending = false;
285 //pThisR->fZeroByteRead = false;
286 //pThisW->fZeroByteRead = false;
287 //pThisR->fBrokenPipe = false;
288 //pThisW->fBrokenPipe = false;
289 //pThisW->fPromisedWritable= false;
290 //pThisR->fPromisedWritable= false;
291 //pThisR->cModeUsers = 0;
292 //pThisW->cModeUsers = 0;
293 //pThisR->pbBounceBuf = NULL;
294 //pThisW->pbBounceBuf = NULL;
295 //pThisR->cbBounceBufUsed = 0;
296 //pThisW->cbBounceBufUsed = 0;
297 //pThisR->cbBounceBufAlloc= 0;
298 //pThisW->cbBounceBufAlloc= 0;
299 pThisR->hPollSet = NIL_RTPOLLSET;
300 pThisW->hPollSet = NIL_RTPOLLSET;
301 //pThisW->cPolls = 0;
302 //pThisR->cPolls = 0;
303
304 *phPipeRead = pThisR;
305 *phPipeWrite = pThisW;
306 return VINF_SUCCESS;
307 }
308 CloseHandle(pThisR->Overlapped.hEvent);
309 }
310 RTCritSectDelete(&pThisW->CritSect);
311 }
312 RTCritSectDelete(&pThisR->CritSect);
313 }
314 RTMemFree(pThisW);
315 }
316 else
317 rc = VERR_NO_MEMORY;
318 RTMemFree(pThisR);
319 }
320 else
321 rc = VERR_NO_MEMORY;
322
323 CloseHandle(hPipeR);
324 CloseHandle(hPipeW);
325 return rc;
326}
327
328
329/**
330 * Common worker for handling I/O completion.
331 *
332 * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
333 *
334 * @returns IPRT status code.
335 * @param pThis The pipe instance handle.
336 */
337static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
338{
339 int rc;
340 DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
341 if (dwRc == WAIT_OBJECT_0)
342 {
343 DWORD cbWritten = 0;
344 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
345 {
346 for (;;)
347 {
348 if (cbWritten >= pThis->cbBounceBufUsed)
349 {
350 pThis->fIOPending = false;
351 rc = VINF_SUCCESS;
352 break;
353 }
354
355 /* resubmit the remainder of the buffer - can this actually happen? */
356 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
357 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
358 if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
359 &cbWritten, &pThis->Overlapped))
360 {
361 if (GetLastError() == ERROR_IO_PENDING)
362 rc = VINF_TRY_AGAIN;
363 else
364 {
365 pThis->fIOPending = false;
366 if (GetLastError() == ERROR_NO_DATA)
367 rc = VERR_BROKEN_PIPE;
368 else
369 rc = RTErrConvertFromWin32(GetLastError());
370 if (rc == VERR_BROKEN_PIPE)
371 pThis->fBrokenPipe = true;
372 }
373 break;
374 }
375 Assert(cbWritten > 0);
376 }
377 }
378 else
379 {
380 pThis->fIOPending = false;
381 rc = RTErrConvertFromWin32(GetLastError());
382 }
383 }
384 else if (dwRc == WAIT_TIMEOUT)
385 rc = VINF_TRY_AGAIN;
386 else
387 {
388 pThis->fIOPending = false;
389 if (dwRc == WAIT_ABANDONED)
390 rc = VERR_INVALID_HANDLE;
391 else
392 rc = RTErrConvertFromWin32(GetLastError());
393 }
394 return rc;
395}
396
397
398
399RTDECL(int) RTPipeClose(RTPIPE hPipe)
400{
401 RTPIPEINTERNAL *pThis = hPipe;
402 if (pThis == NIL_RTPIPE)
403 return VINF_SUCCESS;
404 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
405 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
406
407 /*
408 * Do the cleanup.
409 */
410 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
411 RTCritSectEnter(&pThis->CritSect);
412 Assert(pThis->cModeUsers == 0);
413
414 if (!pThis->fRead && pThis->fIOPending)
415 rtPipeWriteCheckCompletion(pThis);
416
417 CloseHandle(pThis->hPipe);
418 pThis->hPipe = INVALID_HANDLE_VALUE;
419
420 CloseHandle(pThis->Overlapped.hEvent);
421 pThis->Overlapped.hEvent = NULL;
422
423 RTMemFree(pThis->pbBounceBuf);
424 pThis->pbBounceBuf = NULL;
425
426 RTCritSectLeave(&pThis->CritSect);
427 RTCritSectDelete(&pThis->CritSect);
428
429 RTMemFree(pThis);
430
431 return VINF_SUCCESS;
432}
433
434
435RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
436{
437 RTPIPEINTERNAL *pThis = hPipe;
438 AssertPtrReturn(pThis, (RTHCINTPTR)(unsigned int)-1);
439 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, (RTHCINTPTR)(unsigned int)-1);
440
441 return (RTHCINTPTR)pThis->hPipe;
442}
443
444
445RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
446{
447 RTPIPEINTERNAL *pThis = hPipe;
448 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
449 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
450 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
451 AssertPtr(pcbRead);
452 AssertPtr(pvBuf);
453
454 int rc = RTCritSectEnter(&pThis->CritSect);
455 if (RT_SUCCESS(rc))
456 {
457 /* No concurrent readers, sorry. */
458 if (pThis->cModeUsers == 0)
459 {
460 pThis->cModeUsers++;
461
462 /*
463 * Kick of a an overlapped read. It should return immedately if
464 * there is bytes in the buffer. If not, we'll cancel it and see
465 * what we get back.
466 */
467 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
468 DWORD cbRead = 0;
469 if ( cbToRead == 0
470 || ReadFile(pThis->hPipe, pvBuf,
471 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
472 &cbRead, &pThis->Overlapped))
473 {
474 *pcbRead = cbRead;
475 rc = VINF_SUCCESS;
476 }
477 else if (GetLastError() == ERROR_IO_PENDING)
478 {
479 pThis->fIOPending = true;
480 RTCritSectLeave(&pThis->CritSect);
481
482 if (!CancelIo(pThis->hPipe))
483 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
484 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
485 {
486 *pcbRead = cbRead;
487 rc = VINF_SUCCESS;
488 }
489 else if (GetLastError() == ERROR_OPERATION_ABORTED)
490 {
491 *pcbRead = 0;
492 rc = VINF_TRY_AGAIN;
493 }
494 else
495 rc = RTErrConvertFromWin32(GetLastError());
496
497 RTCritSectEnter(&pThis->CritSect);
498 pThis->fIOPending = false;
499 }
500 else
501 rc = RTErrConvertFromWin32(GetLastError());
502 if (rc == VERR_BROKEN_PIPE)
503 pThis->fBrokenPipe = true;
504
505 pThis->cModeUsers--;
506 }
507 else
508 rc = VERR_WRONG_ORDER;
509 RTCritSectLeave(&pThis->CritSect);
510 }
511 return rc;
512}
513
514
515RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
516{
517 RTPIPEINTERNAL *pThis = hPipe;
518 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
519 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
520 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
521 AssertPtr(pvBuf);
522
523 int rc = RTCritSectEnter(&pThis->CritSect);
524 if (RT_SUCCESS(rc))
525 {
526 /* No concurrent readers, sorry. */
527 if (pThis->cModeUsers == 0)
528 {
529 pThis->cModeUsers++;
530
531 size_t cbTotalRead = 0;
532 while (cbToRead > 0)
533 {
534 /*
535 * Kick of a an overlapped read. It should return immedately if
536 * there is bytes in the buffer. If not, we'll cancel it and see
537 * what we get back.
538 */
539 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
540 DWORD cbRead = 0;
541 pThis->fIOPending = true;
542 RTCritSectLeave(&pThis->CritSect);
543
544 if (ReadFile(pThis->hPipe, pvBuf,
545 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
546 &cbRead, &pThis->Overlapped))
547 rc = VINF_SUCCESS;
548 else if (GetLastError() == ERROR_IO_PENDING)
549 {
550 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
551 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
552 rc = VINF_SUCCESS;
553 else
554 rc = RTErrConvertFromWin32(GetLastError());
555 }
556 else
557 rc = RTErrConvertFromWin32(GetLastError());
558
559 RTCritSectEnter(&pThis->CritSect);
560 pThis->fIOPending = false;
561 if (RT_FAILURE(rc))
562 break;
563
564 /* advance */
565 cbToRead -= cbRead;
566 cbTotalRead += cbRead;
567 pvBuf = (uint8_t *)pvBuf + cbRead;
568 }
569
570 if (rc == VERR_BROKEN_PIPE)
571 pThis->fBrokenPipe = true;
572
573 if (pcbRead)
574 {
575 *pcbRead = cbTotalRead;
576 if ( RT_FAILURE(rc)
577 && cbTotalRead
578 && rc != VERR_INVALID_POINTER)
579 rc = VINF_SUCCESS;
580 }
581
582 pThis->cModeUsers--;
583 }
584 else
585 rc = VERR_WRONG_ORDER;
586 RTCritSectLeave(&pThis->CritSect);
587 }
588 return rc;
589}
590
591
592RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
593{
594 RTPIPEINTERNAL *pThis = hPipe;
595 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
596 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
597 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
598 AssertPtr(pcbWritten);
599 AssertPtr(pvBuf);
600
601 int rc = RTCritSectEnter(&pThis->CritSect);
602 if (RT_SUCCESS(rc))
603 {
604 /* No concurrent readers, sorry. */
605 if (pThis->cModeUsers == 0)
606 {
607 pThis->cModeUsers++;
608
609 /* If I/O is pending, check if it has completed. */
610 if (pThis->fIOPending)
611 rc = rtPipeWriteCheckCompletion(pThis);
612 else
613 rc = VINF_SUCCESS;
614 if (rc == VINF_SUCCESS)
615 {
616 Assert(!pThis->fIOPending);
617
618 /* Adjust the number of bytes to write to fit into the current
619 buffer quota, unless we've promissed stuff in RTPipeSelectOne.
620 WriteQuotaAvailable better not be zero when it shouldn't!! */
621 FILE_PIPE_LOCAL_INFORMATION Info;
622 if ( !pThis->fPromisedWritable
623 && cbToWrite > 0
624 && rtPipeQueryInfo(pThis, &Info))
625 {
626 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
627 rc = VERR_BROKEN_PIPE;
628 else if ( cbToWrite >= Info.WriteQuotaAvailable
629 && Info.OutboundQuota != 0)
630 {
631 cbToWrite = Info.WriteQuotaAvailable;
632 if (!cbToWrite)
633 rc = VINF_TRY_AGAIN;
634 }
635 }
636 pThis->fPromisedWritable = false;
637
638 /* Do the bounce buffering. */
639 if ( pThis->cbBounceBufAlloc < cbToWrite
640 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
641 {
642 if (cbToWrite > RTPIPE_NT_SIZE)
643 cbToWrite = RTPIPE_NT_SIZE;
644 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
645 if (pv)
646 {
647 pThis->pbBounceBuf = (uint8_t *)pv;
648 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
649 }
650 else
651 rc = VERR_NO_MEMORY;
652 }
653 else if (cbToWrite > RTPIPE_NT_SIZE)
654 cbToWrite = RTPIPE_NT_SIZE;
655 if (RT_SUCCESS(rc) && cbToWrite)
656 {
657 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
658 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
659
660 /* Submit the write. */
661 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
662 DWORD cbWritten = 0;
663 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
664 &cbWritten, &pThis->Overlapped))
665 {
666 *pcbWritten = cbWritten;
667 rc = VINF_SUCCESS;
668 }
669 else if (GetLastError() == ERROR_IO_PENDING)
670 {
671 *pcbWritten = cbWritten;
672 pThis->fIOPending = true;
673 rc = VINF_SUCCESS;
674 }
675 else if (GetLastError() == ERROR_NO_DATA)
676 rc = VERR_BROKEN_PIPE;
677 else
678 rc = RTErrConvertFromWin32(GetLastError());
679 }
680 else if (RT_SUCCESS(rc))
681 *pcbWritten = 0;
682 }
683 else if (RT_SUCCESS(rc))
684 *pcbWritten = 0;
685
686 if (rc == VERR_BROKEN_PIPE)
687 pThis->fBrokenPipe = true;
688
689 pThis->cModeUsers--;
690 }
691 else
692 rc = VERR_WRONG_ORDER;
693 RTCritSectLeave(&pThis->CritSect);
694 }
695 return rc;
696}
697
698
699RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
700{
701 RTPIPEINTERNAL *pThis = hPipe;
702 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
703 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
704 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
705 AssertPtr(pvBuf);
706 AssertPtrNull(pcbWritten);
707
708 int rc = RTCritSectEnter(&pThis->CritSect);
709 if (RT_SUCCESS(rc))
710 {
711 /* No concurrent readers, sorry. */
712 if (pThis->cModeUsers == 0)
713 {
714 pThis->cModeUsers++;
715
716 /*
717 * If I/O is pending, wait for it to complete.
718 */
719 if (pThis->fIOPending)
720 {
721 rc = rtPipeWriteCheckCompletion(pThis);
722 while (rc == VINF_TRY_AGAIN)
723 {
724 Assert(pThis->fIOPending);
725 HANDLE hEvent = pThis->Overlapped.hEvent;
726 RTCritSectLeave(&pThis->CritSect);
727 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
728 RTCritSectEnter(&pThis->CritSect);
729 }
730 }
731 if (RT_SUCCESS(rc))
732 {
733 Assert(!pThis->fIOPending);
734 pThis->fPromisedWritable = false;
735
736 /*
737 * Try write everything.
738 * No bounce buffering, cModeUsers protects us.
739 */
740 size_t cbTotalWritten = 0;
741 while (cbToWrite > 0)
742 {
743 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
744 pThis->fIOPending = true;
745 RTCritSectLeave(&pThis->CritSect);
746
747 DWORD cbWritten = 0;
748 if (WriteFile(pThis->hPipe, pvBuf,
749 cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0,
750 &cbWritten, &pThis->Overlapped))
751 rc = VINF_SUCCESS;
752 else if (GetLastError() == ERROR_IO_PENDING)
753 {
754 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
755 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
756 rc = VINF_SUCCESS;
757 else
758 rc = RTErrConvertFromWin32(GetLastError());
759 }
760 else if (GetLastError() == ERROR_NO_DATA)
761 rc = VERR_BROKEN_PIPE;
762 else
763 rc = RTErrConvertFromWin32(GetLastError());
764
765 RTCritSectEnter(&pThis->CritSect);
766 pThis->fIOPending = false;
767 if (RT_FAILURE(rc))
768 break;
769
770 /* advance */
771 pvBuf = (char const *)pvBuf + cbWritten;
772 cbTotalWritten += cbWritten;
773 cbToWrite -= cbWritten;
774 }
775
776 if (pcbWritten)
777 {
778 *pcbWritten = cbTotalWritten;
779 if ( RT_FAILURE(rc)
780 && cbTotalWritten
781 && rc != VERR_INVALID_POINTER)
782 rc = VINF_SUCCESS;
783 }
784 }
785
786 if (rc == VERR_BROKEN_PIPE)
787 pThis->fBrokenPipe = true;
788
789 pThis->cModeUsers--;
790 }
791 else
792 rc = VERR_WRONG_ORDER;
793 RTCritSectLeave(&pThis->CritSect);
794 }
795 return rc;
796
797#if 1
798 return VERR_NOT_IMPLEMENTED;
799#else
800 int rc = rtPipeTryBlocking(pThis);
801 if (RT_SUCCESS(rc))
802 {
803 size_t cbTotalWritten = 0;
804 while (cbToWrite > 0)
805 {
806 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
807 if (cbWritten < 0)
808 {
809 rc = RTErrConvertFromErrno(errno);
810 break;
811 }
812
813 /* advance */
814 pvBuf = (char const *)pvBuf + cbWritten;
815 cbTotalWritten += cbWritten;
816 cbToWrite -= cbWritten;
817 }
818
819 if (pcbWritten)
820 {
821 *pcbWritten = cbTotalWritten;
822 if ( RT_FAILURE(rc)
823 && cbTotalWritten
824 && rc != VERR_INVALID_POINTER)
825 rc = VINF_SUCCESS;
826 }
827
828 ASMAtomicDecU32(&pThis->u32State);
829 }
830 return rc;
831#endif
832}
833
834
835RTDECL(int) RTPipeFlush(RTPIPE hPipe)
836{
837 RTPIPEINTERNAL *pThis = hPipe;
838 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
839 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
840 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
841
842 if (!FlushFileBuffers(pThis->hPipe))
843 {
844 int rc = RTErrConvertFromWin32(GetLastError());
845 if (rc == VERR_BROKEN_PIPE)
846 pThis->fBrokenPipe = true;
847 return rc;
848 }
849 return VINF_SUCCESS;
850}
851
852
853RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
854{
855 RTPIPEINTERNAL *pThis = hPipe;
856 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
857 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
858
859 uint64_t const StartMsTS = RTTimeMilliTS();
860
861 int rc = RTCritSectEnter(&pThis->CritSect);
862 if (RT_FAILURE(rc))
863 return rc;
864 for (unsigned iLoop = 0;; iLoop++)
865 {
866 HANDLE hWait = INVALID_HANDLE_VALUE;
867 if (pThis->fRead)
868 {
869 if (pThis->fIOPending)
870 hWait = pThis->Overlapped.hEvent;
871 else
872 {
873 /* Peek at the pipe buffer and see how many bytes it contains. */
874 DWORD cbAvailable;
875 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
876 && cbAvailable > 0)
877 {
878 rc = VINF_SUCCESS;
879 break;
880 }
881
882 /* Start a zero byte read operation that we can wait on. */
883 if (cMillies == 0)
884 {
885 rc = VERR_TIMEOUT;
886 break;
887 }
888 AssertBreakStmt(pThis->cModeUsers == 0, rc = VERR_INTERNAL_ERROR_5);
889 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
890 DWORD cbRead = 0;
891 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
892 {
893 rc = VINF_SUCCESS;
894 if (iLoop > 10)
895 RTThreadYield();
896 }
897 else if (GetLastError() == ERROR_IO_PENDING)
898 {
899 pThis->cModeUsers++;
900 pThis->fIOPending = true;
901 pThis->fZeroByteRead = true;
902 hWait = pThis->Overlapped.hEvent;
903 }
904 else
905 rc = RTErrConvertFromWin32(GetLastError());
906 }
907 }
908 else
909 {
910 if (pThis->fIOPending)
911 {
912 rc = rtPipeWriteCheckCompletion(pThis);
913 if (RT_FAILURE(rc))
914 break;
915 }
916 if (pThis->fIOPending)
917 hWait = pThis->Overlapped.hEvent;
918 else
919 {
920 FILE_PIPE_LOCAL_INFORMATION Info;
921 if (rtPipeQueryInfo(pThis, &Info))
922 {
923 /* Check for broken pipe. */
924 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
925 {
926 rc = VERR_BROKEN_PIPE;
927 break;
928 }
929 /* Check for available write buffer space. */
930 else if (Info.WriteQuotaAvailable > 0)
931 {
932 pThis->fPromisedWritable = false;
933 rc = VINF_SUCCESS;
934 break;
935 }
936 /* delayed buffer alloc or timeout: phony promise
937 later: See if we still can associate a semaphore with
938 the pipe, like on OS/2. */
939 else if ( Info.OutboundQuota == 0
940 || cMillies)
941 {
942 pThis->fPromisedWritable = true;
943 rc = VINF_SUCCESS;
944 break;
945 }
946 }
947 else
948 {
949 pThis->fPromisedWritable = true;
950 rc = VINF_SUCCESS;
951 break;
952 }
953 }
954 }
955 if (RT_FAILURE(rc))
956 break;
957
958 /*
959 * Check for timeout.
960 */
961 DWORD cMsMaxWait = INFINITE;
962 if ( cMillies != RT_INDEFINITE_WAIT
963 && ( hWait != INVALID_HANDLE_VALUE
964 || iLoop > 10)
965 )
966 {
967 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
968 if (cElapsed >= cMillies)
969 {
970 rc = VERR_TIMEOUT;
971 break;
972 }
973 cMsMaxWait = cMillies - (uint32_t)cElapsed;
974 }
975
976 /*
977 * Wait.
978 */
979 if (hWait != INVALID_HANDLE_VALUE)
980 {
981 RTCritSectLeave(&pThis->CritSect);
982
983 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
984 if (dwRc == WAIT_OBJECT_0)
985 rc = VINF_SUCCESS;
986 else if (dwRc == WAIT_TIMEOUT)
987 rc = VERR_TIMEOUT;
988 else if (dwRc == WAIT_ABANDONED)
989 rc = VERR_INVALID_HANDLE;
990 else
991 rc = RTErrConvertFromWin32(GetLastError());
992 if ( RT_FAILURE(rc)
993 && pThis->u32Magic != RTPIPE_MAGIC)
994 return rc;
995
996 RTCritSectEnter(&pThis->CritSect);
997 if (pThis->fZeroByteRead)
998 {
999 pThis->cModeUsers--;
1000 pThis->fIOPending = false;
1001 if (rc != VINF_SUCCESS)
1002 CancelIo(pThis->hPipe);
1003 DWORD cbRead = 0;
1004 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1005 }
1006 if (RT_FAILURE(rc))
1007 break;
1008 }
1009 }
1010
1011 if (rc == VERR_BROKEN_PIPE)
1012 pThis->fBrokenPipe = true;
1013
1014 RTCritSectLeave(&pThis->CritSect);
1015 return rc;
1016}
1017
1018
1019/**
1020 * Internal RTPollSetAdd helper that returns the handle that should be added to
1021 * the pollset.
1022 *
1023 * @returns Valid handle on success, INVALID_HANDLE_VALUE on failure.
1024 * @param hPipe The pipe handle.
1025 * @param fEvents The events we're polling for.
1026 * @param ph wher to put the primary handle.
1027 */
1028int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PHANDLE ph)
1029{
1030 RTPIPEINTERNAL *pThis = hPipe;
1031 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1032 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1033
1034 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1035 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1036
1037 /* Later: Try register an event handle with the pipe like on OS/2, there is
1038 a file control for doing this obviously intended for the OS/2 subsys.
1039 The question is whether this still exists on Vista and W7. */
1040 *ph = pThis->Overlapped.hEvent;
1041 return VINF_SUCCESS;
1042}
1043
1044
1045/**
1046 * Checks for pending events.
1047 *
1048 * @returns Event mask or 0.
1049 * @param pThis The pipe handle.
1050 * @param fEvents The desired events.
1051 */
1052static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1053{
1054 uint32_t fRetEvents = 0;
1055 if (pThis->fBrokenPipe)
1056 fRetEvents |= RTPOLL_EVT_ERROR;
1057 else if (pThis->fRead)
1058 {
1059 if (!pThis->fIOPending)
1060 {
1061 DWORD cbAvailable;
1062 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1063 {
1064 if ( (fEvents & RTPOLL_EVT_READ)
1065 && cbAvailable > 0)
1066 fRetEvents |= RTPOLL_EVT_READ;
1067 }
1068 else
1069 {
1070 if (GetLastError() == ERROR_BROKEN_PIPE)
1071 pThis->fBrokenPipe = true;
1072 fRetEvents |= RTPOLL_EVT_ERROR;
1073 }
1074 }
1075 }
1076 else
1077 {
1078 if (pThis->fIOPending)
1079 {
1080 rtPipeWriteCheckCompletion(pThis);
1081 if (pThis->fBrokenPipe)
1082 fRetEvents |= RTPOLL_EVT_ERROR;
1083 }
1084 if ( !pThis->fIOPending
1085 && !fRetEvents)
1086 {
1087 FILE_PIPE_LOCAL_INFORMATION Info;
1088 if (rtPipeQueryInfo(pThis, &Info))
1089 {
1090 /* Check for broken pipe. */
1091 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1092 {
1093 fRetEvents = RTPOLL_EVT_ERROR;
1094 pThis->fBrokenPipe = true;
1095 }
1096
1097 /* Check if there is available buffer space. */
1098 if ( !fRetEvents
1099 && ( Info.WriteQuotaAvailable > 0
1100 || Info.OutboundQuota == 0)
1101 )
1102 fRetEvents |= RTPOLL_EVT_WRITE;
1103 }
1104 else if ( !fRetEvents
1105 && (fEvents & RTPOLL_EVT_WRITE))
1106 fRetEvents |= RTPOLL_EVT_WRITE;
1107 }
1108 }
1109
1110 return fRetEvents;
1111}
1112
1113
1114/**
1115 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1116 * clear, starts whatever actions we've got running during the poll call.
1117 *
1118 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1119 * Event mask (in @a fEvents) and no actions if the handle is ready
1120 * already.
1121 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1122 * different poll set.
1123 *
1124 * @param hPipe The pipe handle.
1125 * @param hPollSet The poll set handle (for access checks).
1126 * @param fEvents The events we're polling for.
1127 * @param fFinalEntry Set if this is the final entry for this handle
1128 * in this poll set. This can be used for dealing
1129 * with duplicate entries.
1130 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1131 * we'll wait for an event to occur.
1132 */
1133uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1134{
1135 /** @todo All this polling code could be optimized to make fewer system
1136 * calls; like for instance the ResetEvent calls. */
1137 RTPIPEINTERNAL *pThis = hPipe;
1138 AssertPtrReturn(pThis, UINT32_MAX);
1139 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1140
1141 int rc = RTCritSectEnter(&pThis->CritSect);
1142 AssertRCReturn(rc, UINT32_MAX);
1143
1144 /* Check that this is the only current use of this pipe. */
1145 uint32_t fRetEvents;
1146 if ( ( pThis->cPolls == 0
1147 && pThis->cModeUsers == 0)
1148 || pThis->hPollSet == hPollSet
1149 )
1150 {
1151 /* Check what the current events are. */
1152 fRetEvents = rtPipePollCheck(pThis, fEvents);
1153 if ( !fRetEvents
1154 && !fNoWait)
1155 {
1156 /* Make sure the event semaphore has been reset. */
1157 if (!pThis->fIOPending)
1158 {
1159 rc = ResetEvent(pThis->Overlapped.hEvent);
1160 Assert(rc == TRUE);
1161 }
1162
1163 /* Kick off the zero byte read thing if applicable. */
1164 if ( !pThis->fIOPending
1165 && pThis->fRead
1166 && (fEvents & RTPOLL_EVT_READ)
1167 )
1168 {
1169 DWORD cbRead = 0;
1170 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1171 fRetEvents = rtPipePollCheck(pThis, fEvents);
1172 else if (GetLastError() == ERROR_IO_PENDING)
1173 {
1174 pThis->fIOPending = true;
1175 pThis->fZeroByteRead = true;
1176 }
1177 else
1178 fRetEvents = RTPOLL_EVT_ERROR;
1179 }
1180
1181 /* If we're still set for the waiting, record the poll set and
1182 mark the pipe used. */
1183 if (!fRetEvents)
1184 {
1185 pThis->cPolls++;
1186 pThis->cModeUsers++;
1187 pThis->hPollSet = hPollSet;
1188 }
1189 }
1190 }
1191 else
1192 {
1193 AssertFailed();
1194 fRetEvents = UINT32_MAX;
1195 }
1196
1197 RTCritSectLeave(&pThis->CritSect);
1198 return fRetEvents;
1199}
1200
1201
1202/**
1203 * Called after a WaitForMultipleObjects returned in order to check for pending
1204 * events and stop whatever actions that rtPipePollStart() initiated.
1205 *
1206 * @returns Event mask or 0.
1207 *
1208 * @param hPipe The pipe handle.
1209 * @param fEvents The events we're polling for.
1210 * @param fFinalEntry Set if this is the final entry for this handle
1211 * in this poll set. This can be used for dealing
1212 * with duplicate entries. Only keep in mind that
1213 * this method is called in reverse order, so the
1214 * first call will have this set (when the entire
1215 * set was processed).
1216 */
1217uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry)
1218{
1219 RTPIPEINTERNAL *pThis = hPipe;
1220 AssertPtrReturn(pThis, 0);
1221 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1222
1223 int rc = RTCritSectEnter(&pThis->CritSect);
1224 AssertRCReturn(rc, 0);
1225
1226 Assert(pThis->cPolls > 0);
1227 Assert(pThis->cModeUsers > 0);
1228
1229
1230 /* Cancel the zero byte read. */
1231 uint32_t fRetEvents = 0;
1232 if (pThis->fZeroByteRead)
1233 {
1234 CancelIo(pThis->hPipe);
1235 DWORD cbRead = 0;
1236 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1237 && GetLastError() != ERROR_OPERATION_ABORTED)
1238 fRetEvents = RTPOLL_EVT_ERROR;
1239
1240 pThis->fIOPending = false;
1241 pThis->fZeroByteRead = false;
1242 }
1243
1244 /* harvest events. */
1245 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1246
1247 /* update counters. */
1248 pThis->cPolls--;
1249 if (!pThis->cPolls)
1250 pThis->hPollSet = NIL_RTPOLLSET;
1251 pThis->cModeUsers--;
1252
1253 RTCritSectLeave(&pThis->CritSect);
1254 return fRetEvents;
1255}
1256
Note: See TracBrowser for help on using the repository browser.

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