VirtualBox

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

Last change on this file since 96124 was 95097, checked in by vboxsync, 3 years ago

IPRT/pipe/win: Adjusted RTPipeSelectOne to account for the RTPipeWrite[Blocking] behaviour on windows, making tstRTPipe stop failing. Documented this in the pipe.h and the testcase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.9 KB
Line 
1/* $Id: pipe-win.cpp 95097 2022-05-25 12:28:01Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2022 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/win/windows.h>
32
33#include <iprt/pipe.h>
34#include "internal/iprt.h"
35
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/critsect.h>
39#include <iprt/err.h>
40#include <iprt/log.h>
41#include <iprt/mem.h>
42#include <iprt/string.h>
43#include <iprt/poll.h>
44#include <iprt/process.h>
45#include <iprt/thread.h>
46#include <iprt/time.h>
47#include "internal/pipe.h"
48#include "internal/magics.h"
49#include "internal-r3-win.h"
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55/** The pipe buffer size we prefer. */
56#define RTPIPE_NT_SIZE _64K
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62typedef struct RTPIPEINTERNAL
63{
64 /** Magic value (RTPIPE_MAGIC). */
65 uint32_t u32Magic;
66 /** The pipe handle. */
67 HANDLE hPipe;
68 /** Set if this is the read end, clear if it's the write end. */
69 bool fRead;
70 /** RTPipeFromNative: Leave native handle open on RTPipeClose. */
71 bool fLeaveOpen;
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 /** Set if created inheritable. */
81 bool fCreatedInheritable;
82 /** Usage counter. */
83 uint32_t cUsers;
84 /** The overlapped I/O structure we use. */
85 OVERLAPPED Overlapped;
86 /** Bounce buffer for writes. */
87 uint8_t *pbBounceBuf;
88 /** Amount of used buffer space. */
89 size_t cbBounceBufUsed;
90 /** Amount of allocated buffer space. */
91 size_t cbBounceBufAlloc;
92 /** The handle of the poll set currently polling on this pipe.
93 * We can only have one poller at the time (lazy bird). */
94 RTPOLLSET hPollSet;
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 0x00000001U
136#define FILE_PIPE_LISTENING_STATE 0x00000002U
137#define FILE_PIPE_CONNECTED_STATE 0x00000003U
138#define FILE_PIPE_CLOSING_STATE 0x00000004U
139
140#define FILE_PIPE_INBOUND 0x00000000U
141#define FILE_PIPE_OUTBOUND 0x00000001U
142#define FILE_PIPE_FULL_DUPLEX 0x00000002U
143
144#define FILE_PIPE_CLIENT_END 0x00000000U
145#define FILE_PIPE_SERVER_END 0x00000001U
146
147extern "C" NTSYSAPI NTSTATUS WINAPI NtQueryInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, LONG, FILE_INFORMATION_CLASS);
148
149
150/**
151 * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
152 *
153 * @returns Success indicator (true/false).
154 * @param pThis The pipe.
155 * @param pInfo The info structure.
156 */
157static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
158{
159 IO_STATUS_BLOCK Ios;
160 RT_ZERO(Ios);
161 RT_ZERO(*pInfo);
162 NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
163 return rcNt >= 0;
164}
165
166
167RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
168{
169 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
170 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
171 AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
172
173 /*
174 * Create the read end of the pipe.
175 */
176 DWORD dwErr;
177 HANDLE hPipeR;
178 HANDLE hPipeW;
179 int rc;
180 for (;;)
181 {
182 static volatile uint32_t g_iNextPipe = 0;
183 char szName[128];
184 RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
185
186 SECURITY_ATTRIBUTES SecurityAttributes;
187 PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
188 if (fFlags & RTPIPE_C_INHERIT_READ)
189 {
190 SecurityAttributes.nLength = sizeof(SecurityAttributes);
191 SecurityAttributes.lpSecurityDescriptor = NULL;
192 SecurityAttributes.bInheritHandle = TRUE;
193 pSecurityAttributes = &SecurityAttributes;
194 }
195
196 DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
197#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
198 dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
199#endif
200
201 DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
202#ifdef PIPE_REJECT_REMOTE_CLIENTS
203 dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
204#endif
205
206 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
207 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
208#ifdef PIPE_REJECT_REMOTE_CLIENTS
209 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
210 {
211 dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
212 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
213 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
214 }
215#endif
216#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
217 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
218 {
219 dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
220 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
221 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
222 }
223#endif
224 if (hPipeR != INVALID_HANDLE_VALUE)
225 {
226 /*
227 * Connect to the pipe (the write end).
228 * We add FILE_READ_ATTRIBUTES here to make sure we can query the
229 * pipe state later on.
230 */
231 pSecurityAttributes = NULL;
232 if (fFlags & RTPIPE_C_INHERIT_WRITE)
233 {
234 SecurityAttributes.nLength = sizeof(SecurityAttributes);
235 SecurityAttributes.lpSecurityDescriptor = NULL;
236 SecurityAttributes.bInheritHandle = TRUE;
237 pSecurityAttributes = &SecurityAttributes;
238 }
239
240 hPipeW = CreateFileA(szName,
241 GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
242 0 /*dwShareMode*/,
243 pSecurityAttributes,
244 OPEN_EXISTING /* dwCreationDisposition */,
245 FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
246 NULL /*hTemplateFile*/);
247 if (hPipeW != INVALID_HANDLE_VALUE)
248 break;
249 dwErr = GetLastError();
250 CloseHandle(hPipeR);
251 }
252 else
253 dwErr = GetLastError();
254 if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
255 && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
256 return RTErrConvertFromWin32(dwErr);
257 /* else: try again with a new name */
258 }
259
260 /*
261 * Create the two handles.
262 */
263 RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
264 if (pThisR)
265 {
266 RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
267 if (pThisW)
268 {
269 rc = RTCritSectInit(&pThisR->CritSect);
270 if (RT_SUCCESS(rc))
271 {
272 rc = RTCritSectInit(&pThisW->CritSect);
273 if (RT_SUCCESS(rc))
274 {
275 pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
276 TRUE /*fInitialState*/, NULL /*pName*/);
277 if (pThisR->Overlapped.hEvent != NULL)
278 {
279 pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
280 TRUE /*fInitialState*/, NULL /*pName*/);
281 if (pThisW->Overlapped.hEvent != NULL)
282 {
283 pThisR->u32Magic = RTPIPE_MAGIC;
284 pThisW->u32Magic = RTPIPE_MAGIC;
285 pThisR->hPipe = hPipeR;
286 pThisW->hPipe = hPipeW;
287 pThisR->fRead = true;
288 pThisW->fRead = false;
289 pThisR->fLeaveOpen = false;
290 pThisW->fLeaveOpen = false;
291 //pThisR->fIOPending = false;
292 //pThisW->fIOPending = false;
293 //pThisR->fZeroByteRead = false;
294 //pThisW->fZeroByteRead = false;
295 //pThisR->fBrokenPipe = false;
296 //pThisW->fBrokenPipe = false;
297 //pThisW->fPromisedWritable = false;
298 //pThisR->fPromisedWritable = false;
299 pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE);
300 pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ);
301 //pThisR->cUsers = 0;
302 //pThisW->cUsers = 0;
303 //pThisR->pbBounceBuf = NULL;
304 //pThisW->pbBounceBuf = NULL;
305 //pThisR->cbBounceBufUsed = 0;
306 //pThisW->cbBounceBufUsed = 0;
307 //pThisR->cbBounceBufAlloc = 0;
308 //pThisW->cbBounceBufAlloc = 0;
309 pThisR->hPollSet = NIL_RTPOLLSET;
310 pThisW->hPollSet = NIL_RTPOLLSET;
311
312 *phPipeRead = pThisR;
313 *phPipeWrite = pThisW;
314 return VINF_SUCCESS;
315 }
316 CloseHandle(pThisR->Overlapped.hEvent);
317 }
318 RTCritSectDelete(&pThisW->CritSect);
319 }
320 RTCritSectDelete(&pThisR->CritSect);
321 }
322 RTMemFree(pThisW);
323 }
324 else
325 rc = VERR_NO_MEMORY;
326 RTMemFree(pThisR);
327 }
328 else
329 rc = VERR_NO_MEMORY;
330
331 CloseHandle(hPipeR);
332 CloseHandle(hPipeW);
333 return rc;
334}
335
336
337/**
338 * Common worker for handling I/O completion.
339 *
340 * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
341 *
342 * @returns IPRT status code.
343 * @param pThis The pipe instance handle.
344 */
345static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
346{
347 int rc;
348 DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
349 if (dwRc == WAIT_OBJECT_0)
350 {
351 DWORD cbWritten = 0;
352 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
353 {
354 for (;;)
355 {
356 if (cbWritten >= pThis->cbBounceBufUsed)
357 {
358 pThis->fIOPending = false;
359 rc = VINF_SUCCESS;
360 break;
361 }
362
363 /* resubmit the remainder of the buffer - can this actually happen? */
364 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
365 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
366 if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
367 &cbWritten, &pThis->Overlapped))
368 {
369 if (GetLastError() == ERROR_IO_PENDING)
370 rc = VINF_TRY_AGAIN;
371 else
372 {
373 pThis->fIOPending = false;
374 if (GetLastError() == ERROR_NO_DATA)
375 rc = VERR_BROKEN_PIPE;
376 else
377 rc = RTErrConvertFromWin32(GetLastError());
378 if (rc == VERR_BROKEN_PIPE)
379 pThis->fBrokenPipe = true;
380 }
381 break;
382 }
383 Assert(cbWritten > 0);
384 }
385 }
386 else
387 {
388 pThis->fIOPending = false;
389 rc = RTErrConvertFromWin32(GetLastError());
390 }
391 }
392 else if (dwRc == WAIT_TIMEOUT)
393 rc = VINF_TRY_AGAIN;
394 else
395 {
396 pThis->fIOPending = false;
397 if (dwRc == WAIT_ABANDONED)
398 rc = VERR_INVALID_HANDLE;
399 else
400 rc = RTErrConvertFromWin32(GetLastError());
401 }
402 return rc;
403}
404
405
406
407RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
408{
409 RTPIPEINTERNAL *pThis = hPipe;
410 if (pThis == NIL_RTPIPE)
411 return VINF_SUCCESS;
412 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
413 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
414
415 /*
416 * Do the cleanup.
417 */
418 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
419 RTCritSectEnter(&pThis->CritSect);
420 Assert(pThis->cUsers == 0);
421
422 if (!pThis->fRead && pThis->fIOPending)
423 rtPipeWriteCheckCompletion(pThis);
424
425 if (!fLeaveOpen && !pThis->fLeaveOpen)
426 CloseHandle(pThis->hPipe);
427 pThis->hPipe = INVALID_HANDLE_VALUE;
428
429 CloseHandle(pThis->Overlapped.hEvent);
430 pThis->Overlapped.hEvent = NULL;
431
432 RTMemFree(pThis->pbBounceBuf);
433 pThis->pbBounceBuf = NULL;
434
435 RTCritSectLeave(&pThis->CritSect);
436 RTCritSectDelete(&pThis->CritSect);
437
438 RTMemFree(pThis);
439
440 return VINF_SUCCESS;
441}
442
443
444RTDECL(int) RTPipeClose(RTPIPE hPipe)
445{
446 return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
447}
448
449
450RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
451{
452 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
453 AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
454 AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
455
456 /*
457 * Get and validate the pipe handle info.
458 */
459 HANDLE hNative = (HANDLE)hNativePipe;
460 AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
461
462 DWORD cMaxInstances;
463 DWORD fInfo;
464 if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
465 return RTErrConvertFromWin32(GetLastError());
466 /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
467 seems to hand us such pipes when capturing output (@bugref{9397}), so just
468 ignore skip this check:
469 AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
470 AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
471
472 DWORD cInstances;
473 DWORD fState;
474 if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
475 return RTErrConvertFromWin32(GetLastError());
476 AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
477 AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
478 AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
479
480 /*
481 * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
482 * and see if we need to duplicate it to make that call work.
483 */
484 RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
485 if (!pThis)
486 return VERR_NO_MEMORY;
487 int rc = RTCritSectInit(&pThis->CritSect);
488 if (RT_SUCCESS(rc))
489 {
490 pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
491 TRUE /*fInitialState*/, NULL /*pName*/);
492 if (pThis->Overlapped.hEvent != NULL)
493 {
494 pThis->u32Magic = RTPIPE_MAGIC;
495 pThis->hPipe = hNative;
496 pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
497 pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
498 //pThis->fIOPending = false;
499 //pThis->fZeroByteRead = false;
500 //pThis->fBrokenPipe = false;
501 //pThis->fPromisedWritable = false;
502 pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
503 //pThis->cUsers = 0;
504 //pThis->pbBounceBuf = NULL;
505 //pThis->cbBounceBufUsed = 0;
506 //pThis->cbBounceBufAlloc = 0;
507 pThis->hPollSet = NIL_RTPOLLSET;
508
509 HANDLE hNative2 = INVALID_HANDLE_VALUE;
510 FILE_PIPE_LOCAL_INFORMATION Info;
511 RT_ZERO(Info);
512 if ( g_enmWinVer != kRTWinOSType_NT310
513 && rtPipeQueryNtInfo(pThis, &Info))
514 rc = VINF_SUCCESS;
515 else
516 {
517 if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
518 GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
519 pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
520 !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
521 0 /*dwOptions*/))
522 {
523 pThis->hPipe = hNative2;
524 if (rtPipeQueryNtInfo(pThis, &Info))
525 {
526 pThis->fLeaveOpen = false;
527 rc = VINF_SUCCESS;
528 }
529 else
530 {
531 rc = VERR_ACCESS_DENIED;
532 CloseHandle(hNative2);
533 }
534 }
535 else
536 hNative2 = INVALID_HANDLE_VALUE;
537 }
538 if (RT_SUCCESS(rc))
539 {
540 /*
541 * Verify the pipe state and correct the inheritability.
542 */
543 AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
544 || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
545 || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
546 VERR_INVALID_HANDLE);
547 AssertStmt( Info.NamedPipeConfiguration
548 == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
549 ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
550 : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
551 || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
552 VERR_INVALID_HANDLE);
553 if ( RT_SUCCESS(rc)
554 && hNative2 == INVALID_HANDLE_VALUE
555 && !SetHandleInformation(hNative,
556 HANDLE_FLAG_INHERIT /*dwMask*/,
557 fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
558 {
559 rc = RTErrConvertFromWin32(GetLastError());
560 AssertMsgFailed(("%Rrc\n", rc));
561 }
562 if (RT_SUCCESS(rc))
563 {
564 /*
565 * Ok, we're good! If we replaced the handle, make sure it's not a standard
566 * handle if we think we need to close it.
567 */
568 if (hNative2 != INVALID_HANDLE_VALUE)
569 {
570 if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
571 && hNative != GetStdHandle(STD_INPUT_HANDLE)
572 && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
573 && hNative != GetStdHandle(STD_ERROR_HANDLE) )
574 CloseHandle(hNative);
575 }
576 *phPipe = pThis;
577 return VINF_SUCCESS;
578 }
579 }
580
581 /* Bail out. */
582 if (hNative2 != INVALID_HANDLE_VALUE)
583 CloseHandle(hNative2);
584 CloseHandle(pThis->Overlapped.hEvent);
585 }
586 RTCritSectDelete(&pThis->CritSect);
587 }
588 RTMemFree(pThis);
589 return rc;
590}
591
592
593RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
594{
595 RTPIPEINTERNAL *pThis = hPipe;
596 AssertPtrReturn(pThis, -1);
597 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
598
599 return (RTHCINTPTR)pThis->hPipe;
600}
601
602
603RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
604{
605 RTPIPEINTERNAL *pThis = hPipe;
606 AssertPtrReturn(pThis, false);
607 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
608
609 return pThis->fCreatedInheritable;
610}
611
612
613RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
614{
615 RTPIPEINTERNAL *pThis = hPipe;
616 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
617 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
618 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
619 AssertPtr(pcbRead);
620 AssertPtr(pvBuf);
621
622 int rc = RTCritSectEnter(&pThis->CritSect);
623 if (RT_SUCCESS(rc))
624 {
625 /* No concurrent readers, sorry. */
626 if (pThis->cUsers == 0)
627 {
628 pThis->cUsers++;
629
630 /*
631 * Kick off a an overlapped read. It should return immediately if
632 * there are bytes in the buffer. If not, we'll cancel it and see
633 * what we get back.
634 */
635 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
636 DWORD cbRead = 0;
637 if ( cbToRead == 0
638 || ReadFile(pThis->hPipe, pvBuf,
639 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
640 &cbRead, &pThis->Overlapped))
641 {
642 *pcbRead = cbRead;
643 rc = VINF_SUCCESS;
644 }
645 else if (GetLastError() == ERROR_IO_PENDING)
646 {
647 pThis->fIOPending = true;
648 RTCritSectLeave(&pThis->CritSect);
649
650 if (!CancelIo(pThis->hPipe))
651 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
652 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
653 {
654 *pcbRead = cbRead;
655 rc = VINF_SUCCESS;
656 }
657 else if (GetLastError() == ERROR_OPERATION_ABORTED)
658 {
659 *pcbRead = 0;
660 rc = VINF_TRY_AGAIN;
661 }
662 else
663 rc = RTErrConvertFromWin32(GetLastError());
664
665 RTCritSectEnter(&pThis->CritSect);
666 pThis->fIOPending = false;
667 }
668 else
669 rc = RTErrConvertFromWin32(GetLastError());
670 if (rc == VERR_BROKEN_PIPE)
671 pThis->fBrokenPipe = true;
672
673 pThis->cUsers--;
674 }
675 else
676 rc = VERR_WRONG_ORDER;
677 RTCritSectLeave(&pThis->CritSect);
678 }
679 return rc;
680}
681
682
683RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
684{
685 RTPIPEINTERNAL *pThis = hPipe;
686 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
687 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
688 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
689 AssertPtr(pvBuf);
690
691 int rc = RTCritSectEnter(&pThis->CritSect);
692 if (RT_SUCCESS(rc))
693 {
694 /* No concurrent readers, sorry. */
695 if (pThis->cUsers == 0)
696 {
697 pThis->cUsers++;
698
699 size_t cbTotalRead = 0;
700 while (cbToRead > 0)
701 {
702 /*
703 * Kick of a an overlapped read. It should return immediately if
704 * there is bytes in the buffer. If not, we'll cancel it and see
705 * what we get back.
706 */
707 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
708 DWORD cbRead = 0;
709 pThis->fIOPending = true;
710 RTCritSectLeave(&pThis->CritSect);
711
712 if (ReadFile(pThis->hPipe, pvBuf,
713 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
714 &cbRead, &pThis->Overlapped))
715 rc = VINF_SUCCESS;
716 else if (GetLastError() == ERROR_IO_PENDING)
717 {
718 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
719 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
720 rc = VINF_SUCCESS;
721 else
722 rc = RTErrConvertFromWin32(GetLastError());
723 }
724 else
725 rc = RTErrConvertFromWin32(GetLastError());
726
727 RTCritSectEnter(&pThis->CritSect);
728 pThis->fIOPending = false;
729 if (RT_FAILURE(rc))
730 break;
731
732 /* advance */
733 cbToRead -= cbRead;
734 cbTotalRead += cbRead;
735 pvBuf = (uint8_t *)pvBuf + cbRead;
736 }
737
738 if (rc == VERR_BROKEN_PIPE)
739 pThis->fBrokenPipe = true;
740
741 if (pcbRead)
742 {
743 *pcbRead = cbTotalRead;
744 if ( RT_FAILURE(rc)
745 && cbTotalRead
746 && rc != VERR_INVALID_POINTER)
747 rc = VINF_SUCCESS;
748 }
749
750 pThis->cUsers--;
751 }
752 else
753 rc = VERR_WRONG_ORDER;
754 RTCritSectLeave(&pThis->CritSect);
755 }
756 return rc;
757}
758
759
760RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
761{
762 RTPIPEINTERNAL *pThis = hPipe;
763 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
764 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
765 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
766 AssertPtr(pcbWritten);
767 AssertPtr(pvBuf);
768
769 int rc = RTCritSectEnter(&pThis->CritSect);
770 if (RT_SUCCESS(rc))
771 {
772 /* No concurrent writers, sorry. */
773 if (pThis->cUsers == 0)
774 {
775 pThis->cUsers++;
776
777 /* If I/O is pending, check if it has completed. */
778 if (pThis->fIOPending)
779 rc = rtPipeWriteCheckCompletion(pThis);
780 else
781 rc = VINF_SUCCESS;
782 if (rc == VINF_SUCCESS)
783 {
784 Assert(!pThis->fIOPending);
785
786 /* Adjust the number of bytes to write to fit into the current
787 buffer quota, unless we've promised stuff in RTPipeSelectOne.
788 WriteQuotaAvailable better not be zero when it shouldn't!! */
789 FILE_PIPE_LOCAL_INFORMATION Info;
790 if ( !pThis->fPromisedWritable
791 && cbToWrite > 0
792 && rtPipeQueryNtInfo(pThis, &Info))
793 {
794 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
795 rc = VERR_BROKEN_PIPE;
796 /** @todo fixme: To get the pipe writing support to work the
797 * block below needs to be commented out until a
798 * way is found to address the problem of the incorrectly
799 * set field Info.WriteQuotaAvailable.
800 * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
801 * possibely what lead to the misunderstanding here wrt to
802 * WriteQuotaAvailable updating. */
803#if 0
804 else if ( cbToWrite >= Info.WriteQuotaAvailable
805 && Info.OutboundQuota != 0
806 && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
807 )
808 {
809 cbToWrite = Info.WriteQuotaAvailable;
810 if (!cbToWrite)
811 rc = VINF_TRY_AGAIN;
812 }
813#endif
814 }
815 pThis->fPromisedWritable = false;
816
817 /* Do the bounce buffering. */
818 if ( pThis->cbBounceBufAlloc < cbToWrite
819 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
820 {
821 if (cbToWrite > RTPIPE_NT_SIZE)
822 cbToWrite = RTPIPE_NT_SIZE;
823 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
824 if (pv)
825 {
826 pThis->pbBounceBuf = (uint8_t *)pv;
827 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
828 }
829 else
830 rc = VERR_NO_MEMORY;
831 }
832 else if (cbToWrite > RTPIPE_NT_SIZE)
833 cbToWrite = RTPIPE_NT_SIZE;
834 if (RT_SUCCESS(rc) && cbToWrite)
835 {
836 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
837 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
838
839 /* Submit the write. */
840 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
841 DWORD cbWritten = 0;
842 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
843 &cbWritten, &pThis->Overlapped))
844 {
845 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
846 rc = VINF_SUCCESS;
847 }
848 else if (GetLastError() == ERROR_IO_PENDING)
849 {
850 *pcbWritten = cbToWrite;
851 pThis->fIOPending = true;
852 rc = VINF_SUCCESS;
853 }
854 else if (GetLastError() == ERROR_NO_DATA)
855 rc = VERR_BROKEN_PIPE;
856 else
857 rc = RTErrConvertFromWin32(GetLastError());
858 }
859 else if (RT_SUCCESS(rc))
860 *pcbWritten = 0;
861 }
862 else if (RT_SUCCESS(rc))
863 *pcbWritten = 0;
864
865 if (rc == VERR_BROKEN_PIPE)
866 pThis->fBrokenPipe = true;
867
868 pThis->cUsers--;
869 }
870 else
871 rc = VERR_WRONG_ORDER;
872 RTCritSectLeave(&pThis->CritSect);
873 }
874 return rc;
875}
876
877
878RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
879{
880 RTPIPEINTERNAL *pThis = hPipe;
881 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
882 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
883 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
884 AssertPtr(pvBuf);
885 AssertPtrNull(pcbWritten);
886
887 int rc = RTCritSectEnter(&pThis->CritSect);
888 if (RT_SUCCESS(rc))
889 {
890 /* No concurrent writers, sorry. */
891 if (pThis->cUsers == 0)
892 {
893 pThis->cUsers++;
894
895 /*
896 * If I/O is pending, wait for it to complete.
897 */
898 if (pThis->fIOPending)
899 {
900 rc = rtPipeWriteCheckCompletion(pThis);
901 while (rc == VINF_TRY_AGAIN)
902 {
903 Assert(pThis->fIOPending);
904 HANDLE hEvent = pThis->Overlapped.hEvent;
905 RTCritSectLeave(&pThis->CritSect);
906 WaitForSingleObject(hEvent, INFINITE);
907 RTCritSectEnter(&pThis->CritSect);
908 }
909 }
910 if (RT_SUCCESS(rc))
911 {
912 Assert(!pThis->fIOPending);
913 pThis->fPromisedWritable = false;
914
915 /*
916 * Try write everything.
917 * No bounce buffering, cUsers protects us.
918 */
919 size_t cbTotalWritten = 0;
920 while (cbToWrite > 0)
921 {
922 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
923 pThis->fIOPending = true;
924 RTCritSectLeave(&pThis->CritSect);
925
926 DWORD cbWritten = 0;
927 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
928 if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
929 rc = VINF_SUCCESS;
930 else if (GetLastError() == ERROR_IO_PENDING)
931 {
932 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
933 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
934 rc = VINF_SUCCESS;
935 else
936 rc = RTErrConvertFromWin32(GetLastError());
937 }
938 else if (GetLastError() == ERROR_NO_DATA)
939 rc = VERR_BROKEN_PIPE;
940 else
941 rc = RTErrConvertFromWin32(GetLastError());
942
943 RTCritSectEnter(&pThis->CritSect);
944 pThis->fIOPending = false;
945 if (RT_FAILURE(rc))
946 break;
947
948 /* advance */
949 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
950 cbWritten = cbToWriteInThisIteration;
951 pvBuf = (char const *)pvBuf + cbWritten;
952 cbTotalWritten += cbWritten;
953 cbToWrite -= cbWritten;
954 }
955
956 if (pcbWritten)
957 {
958 *pcbWritten = cbTotalWritten;
959 if ( RT_FAILURE(rc)
960 && cbTotalWritten
961 && rc != VERR_INVALID_POINTER)
962 rc = VINF_SUCCESS;
963 }
964 }
965
966 if (rc == VERR_BROKEN_PIPE)
967 pThis->fBrokenPipe = true;
968
969 pThis->cUsers--;
970 }
971 else
972 rc = VERR_WRONG_ORDER;
973 RTCritSectLeave(&pThis->CritSect);
974 }
975 return rc;
976
977#if 0 /** @todo r=bird: What's this? */
978 int rc = rtPipeTryBlocking(pThis);
979 if (RT_SUCCESS(rc))
980 {
981 size_t cbTotalWritten = 0;
982 while (cbToWrite > 0)
983 {
984 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
985 if (cbWritten < 0)
986 {
987 rc = RTErrConvertFromErrno(errno);
988 break;
989 }
990
991 /* advance */
992 pvBuf = (char const *)pvBuf + cbWritten;
993 cbTotalWritten += cbWritten;
994 cbToWrite -= cbWritten;
995 }
996
997 if (pcbWritten)
998 {
999 *pcbWritten = cbTotalWritten;
1000 if ( RT_FAILURE(rc)
1001 && cbTotalWritten
1002 && rc != VERR_INVALID_POINTER)
1003 rc = VINF_SUCCESS;
1004 }
1005
1006 ASMAtomicDecU32(&pThis->u32State);
1007 }
1008 return rc;
1009#endif
1010}
1011
1012
1013RTDECL(int) RTPipeFlush(RTPIPE hPipe)
1014{
1015 RTPIPEINTERNAL *pThis = hPipe;
1016 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1017 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1018 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
1019
1020 if (!FlushFileBuffers(pThis->hPipe))
1021 {
1022 int rc = RTErrConvertFromWin32(GetLastError());
1023 if (rc == VERR_BROKEN_PIPE)
1024 pThis->fBrokenPipe = true;
1025 return rc;
1026 }
1027 return VINF_SUCCESS;
1028}
1029
1030
1031RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
1032{
1033 RTPIPEINTERNAL *pThis = hPipe;
1034 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1035 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1036
1037 uint64_t const StartMsTS = RTTimeMilliTS();
1038
1039 int rc = RTCritSectEnter(&pThis->CritSect);
1040 if (RT_FAILURE(rc))
1041 return rc;
1042 for (unsigned iLoop = 0;; iLoop++)
1043 {
1044 HANDLE hWait = INVALID_HANDLE_VALUE;
1045 if (pThis->fRead)
1046 {
1047 if (pThis->fIOPending)
1048 hWait = pThis->Overlapped.hEvent;
1049 else
1050 {
1051 /* Peek at the pipe buffer and see how many bytes it contains. */
1052 DWORD cbAvailable;
1053 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
1054 && cbAvailable > 0)
1055 {
1056 rc = VINF_SUCCESS;
1057 break;
1058 }
1059
1060 /* Start a zero byte read operation that we can wait on. */
1061 if (cMillies == 0)
1062 {
1063 rc = VERR_TIMEOUT;
1064 break;
1065 }
1066 AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
1067 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
1068 DWORD cbRead = 0;
1069 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1070 {
1071 rc = VINF_SUCCESS;
1072 if (iLoop > 10)
1073 RTThreadYield();
1074 }
1075 else if (GetLastError() == ERROR_IO_PENDING)
1076 {
1077 pThis->cUsers++;
1078 pThis->fIOPending = true;
1079 pThis->fZeroByteRead = true;
1080 hWait = pThis->Overlapped.hEvent;
1081 }
1082 else
1083 rc = RTErrConvertFromWin32(GetLastError());
1084 }
1085 }
1086 else
1087 {
1088 if (pThis->fIOPending)
1089 {
1090 rc = rtPipeWriteCheckCompletion(pThis);
1091 if (RT_FAILURE(rc))
1092 break;
1093 }
1094 if (pThis->fIOPending)
1095 hWait = pThis->Overlapped.hEvent;
1096 else
1097 {
1098 FILE_PIPE_LOCAL_INFORMATION Info;
1099#if 1
1100 /* We can always write one bounce buffer full of data regardless of
1101 the pipe buffer state. We must of course take this into account,
1102 or code like "Full write buffer" test in tstRTPipe gets confused. */
1103 rc = VINF_SUCCESS;
1104 if (rtPipeQueryNtInfo(pThis, &Info))
1105 {
1106 /* Check for broken pipe. */
1107 if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
1108 pThis->fPromisedWritable = true;
1109 else
1110 rc = VERR_BROKEN_PIPE;
1111 }
1112 else
1113 pThis->fPromisedWritable = true;
1114 break;
1115
1116#else /* old code: */
1117 if (rtPipeQueryNtInfo(pThis, &Info))
1118 {
1119 /* Check for broken pipe. */
1120 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1121 {
1122 rc = VERR_BROKEN_PIPE;
1123 break;
1124 }
1125
1126 /* Check for available write buffer space. */
1127 if (Info.WriteQuotaAvailable > 0)
1128 {
1129 pThis->fPromisedWritable = false;
1130 rc = VINF_SUCCESS;
1131 break;
1132 }
1133
1134 /* delayed buffer alloc or timeout: phony promise
1135 later: See if we still can associate a semaphore with
1136 the pipe, like on OS/2. */
1137 if (Info.OutboundQuota == 0 || cMillies)
1138 {
1139 pThis->fPromisedWritable = true;
1140 rc = VINF_SUCCESS;
1141 break;
1142 }
1143 }
1144 else
1145 {
1146 pThis->fPromisedWritable = true;
1147 rc = VINF_SUCCESS;
1148 break;
1149 }
1150#endif
1151 }
1152 }
1153 if (RT_FAILURE(rc))
1154 break;
1155
1156 /*
1157 * Check for timeout.
1158 */
1159 DWORD cMsMaxWait = INFINITE;
1160 if ( cMillies != RT_INDEFINITE_WAIT
1161 && ( hWait != INVALID_HANDLE_VALUE
1162 || iLoop > 10)
1163 )
1164 {
1165 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
1166 if (cElapsed >= cMillies)
1167 {
1168 rc = VERR_TIMEOUT;
1169 break;
1170 }
1171 cMsMaxWait = cMillies - (uint32_t)cElapsed;
1172 }
1173
1174 /*
1175 * Wait.
1176 */
1177 if (hWait != INVALID_HANDLE_VALUE)
1178 {
1179 RTCritSectLeave(&pThis->CritSect);
1180
1181 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
1182 if (dwRc == WAIT_OBJECT_0)
1183 rc = VINF_SUCCESS;
1184 else if (dwRc == WAIT_TIMEOUT)
1185 rc = VERR_TIMEOUT;
1186 else if (dwRc == WAIT_ABANDONED)
1187 rc = VERR_INVALID_HANDLE;
1188 else
1189 rc = RTErrConvertFromWin32(GetLastError());
1190 if ( RT_FAILURE(rc)
1191 && pThis->u32Magic != RTPIPE_MAGIC)
1192 return rc;
1193
1194 RTCritSectEnter(&pThis->CritSect);
1195 if (pThis->fZeroByteRead)
1196 {
1197 pThis->cUsers--;
1198 pThis->fIOPending = false;
1199 if (rc != VINF_SUCCESS)
1200 CancelIo(pThis->hPipe);
1201 DWORD cbRead = 0;
1202 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1203 }
1204 if (RT_FAILURE(rc))
1205 break;
1206 }
1207 }
1208
1209 if (rc == VERR_BROKEN_PIPE)
1210 pThis->fBrokenPipe = true;
1211
1212 RTCritSectLeave(&pThis->CritSect);
1213 return rc;
1214}
1215
1216
1217RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
1218{
1219 RTPIPEINTERNAL *pThis = hPipe;
1220 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1221 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1222 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
1223 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
1224
1225 int rc = RTCritSectEnter(&pThis->CritSect);
1226 if (RT_FAILURE(rc))
1227 return rc;
1228
1229 /** @todo The file size should give the same info and be slightly faster... */
1230 DWORD cbAvailable = 0;
1231 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1232 {
1233#if ARCH_BITS == 32
1234 /*
1235 * Kludge!
1236 *
1237 * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
1238 * by the read position in the current message/buffer, so it could
1239 * potentially be too high. This may cause the caller to try read more
1240 * data than what's actually available, which may cause the read to
1241 * block when the caller thought it wouldn't.
1242 *
1243 * To get an accurate readable size, we have to provide an output
1244 * buffer and see how much we actually get back in it, as the data
1245 * peeking works correctly (as you would expect).
1246 */
1247 if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
1248 { /* No data available or kernel shouldn't be affected. */ }
1249 else
1250 {
1251 for (unsigned i = 0; ; i++)
1252 {
1253 uint8_t abBufStack[_16K];
1254 void *pvBufFree = NULL;
1255 void *pvBuf;
1256 DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
1257 if (cbBuf <= sizeof(abBufStack))
1258 {
1259 pvBuf = abBufStack;
1260 /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
1261 }
1262 else
1263 {
1264 pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
1265 if (!pvBuf)
1266 {
1267 rc = VERR_NO_TMP_MEMORY;
1268 cbAvailable = 1;
1269 break;
1270 }
1271 }
1272
1273 DWORD cbAvailable2 = 0;
1274 DWORD cbRead = 0;
1275 BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
1276 Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
1277 i, cbAvailable, cbRead, cbAvailable2, cbBuf));
1278
1279 RTMemTmpFree(pvBufFree);
1280
1281 if (fRc)
1282 {
1283 if (cbAvailable2 <= cbBuf || i >= 10)
1284 cbAvailable = cbRead;
1285 else
1286 {
1287 cbAvailable = cbAvailable2;
1288 continue;
1289 }
1290 }
1291 else
1292 {
1293 rc = RTErrConvertFromWin32(GetLastError());
1294 cbAvailable = 1;
1295 }
1296 break;
1297 }
1298 }
1299#endif
1300 *pcbReadable = cbAvailable;
1301 }
1302 else
1303 rc = RTErrConvertFromWin32(GetLastError());
1304
1305 RTCritSectLeave(&pThis->CritSect);
1306 return rc;
1307}
1308
1309
1310RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1311{
1312 RTPIPEINTERNAL *pThis = hPipe;
1313 AssertPtrReturn(pThis, 0);
1314 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1315
1316 int rc = RTCritSectEnter(&pThis->CritSect);
1317 AssertRCReturn(rc, 0);
1318
1319 rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
1320
1321 FILE_PIPE_LOCAL_INFORMATION Info;
1322 if (rtPipeQueryNtInfo(pThis, &Info))
1323 {
1324 pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
1325 pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
1326 }
1327
1328 RTCritSectLeave(&pThis->CritSect);
1329 return VINF_SUCCESS;
1330}
1331
1332
1333int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
1334{
1335 RTPIPEINTERNAL *pThis = hPipe;
1336 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1337 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1338
1339 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1340 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1341
1342 /* Later: Try register an event handle with the pipe like on OS/2, there is
1343 a file control for doing this obviously intended for the OS/2 subsys.
1344 The question is whether this still exists on Vista and W7. */
1345 *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
1346 return VINF_SUCCESS;
1347}
1348
1349
1350/**
1351 * Checks for pending events.
1352 *
1353 * @returns Event mask or 0.
1354 * @param pThis The pipe handle.
1355 * @param fEvents The desired events.
1356 */
1357static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1358{
1359 uint32_t fRetEvents = 0;
1360 if (pThis->fBrokenPipe)
1361 fRetEvents |= RTPOLL_EVT_ERROR;
1362 else if (pThis->fRead)
1363 {
1364 if (!pThis->fIOPending)
1365 {
1366 DWORD cbAvailable;
1367 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1368 {
1369 if ( (fEvents & RTPOLL_EVT_READ)
1370 && cbAvailable > 0)
1371 fRetEvents |= RTPOLL_EVT_READ;
1372 }
1373 else
1374 {
1375 if (GetLastError() == ERROR_BROKEN_PIPE)
1376 pThis->fBrokenPipe = true;
1377 fRetEvents |= RTPOLL_EVT_ERROR;
1378 }
1379 }
1380 }
1381 else
1382 {
1383 if (pThis->fIOPending)
1384 {
1385 rtPipeWriteCheckCompletion(pThis);
1386 if (pThis->fBrokenPipe)
1387 fRetEvents |= RTPOLL_EVT_ERROR;
1388 }
1389 if ( !pThis->fIOPending
1390 && !fRetEvents)
1391 {
1392 FILE_PIPE_LOCAL_INFORMATION Info;
1393 if (rtPipeQueryNtInfo(pThis, &Info))
1394 {
1395 /* Check for broken pipe. */
1396 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1397 {
1398 fRetEvents = RTPOLL_EVT_ERROR;
1399 pThis->fBrokenPipe = true;
1400 }
1401
1402 /* Check if there is available buffer space. */
1403 if ( !fRetEvents
1404 && (fEvents & RTPOLL_EVT_WRITE)
1405 && ( Info.WriteQuotaAvailable > 0
1406 || Info.OutboundQuota == 0)
1407 )
1408 fRetEvents |= RTPOLL_EVT_WRITE;
1409 }
1410 else if (fEvents & RTPOLL_EVT_WRITE)
1411 fRetEvents |= RTPOLL_EVT_WRITE;
1412 }
1413 }
1414
1415 return fRetEvents;
1416}
1417
1418
1419/**
1420 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1421 * clear, starts whatever actions we've got running during the poll call.
1422 *
1423 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1424 * Event mask (in @a fEvents) and no actions if the handle is ready
1425 * already.
1426 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1427 * different poll set.
1428 *
1429 * @param hPipe The pipe handle.
1430 * @param hPollSet The poll set handle (for access checks).
1431 * @param fEvents The events we're polling for.
1432 * @param fFinalEntry Set if this is the final entry for this handle
1433 * in this poll set. This can be used for dealing
1434 * with duplicate entries.
1435 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1436 * we'll wait for an event to occur.
1437 */
1438uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1439{
1440 /** @todo All this polling code could be optimized to make fewer system
1441 * calls; like for instance the ResetEvent calls. */
1442 RTPIPEINTERNAL *pThis = hPipe;
1443 AssertPtrReturn(pThis, UINT32_MAX);
1444 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1445 RT_NOREF_PV(fFinalEntry);
1446
1447 int rc = RTCritSectEnter(&pThis->CritSect);
1448 AssertRCReturn(rc, UINT32_MAX);
1449
1450 /* Check that this is the only current use of this pipe. */
1451 uint32_t fRetEvents;
1452 if ( pThis->cUsers == 0
1453 || pThis->hPollSet == hPollSet)
1454 {
1455 /* Check what the current events are. */
1456 fRetEvents = rtPipePollCheck(pThis, fEvents);
1457 if ( !fRetEvents
1458 && !fNoWait)
1459 {
1460 /* Make sure the event semaphore has been reset. */
1461 if (!pThis->fIOPending)
1462 {
1463 rc = ResetEvent(pThis->Overlapped.hEvent);
1464 Assert(rc == TRUE);
1465 }
1466
1467 /* Kick off the zero byte read thing if applicable. */
1468 if ( !pThis->fIOPending
1469 && pThis->fRead
1470 && (fEvents & RTPOLL_EVT_READ)
1471 )
1472 {
1473 DWORD cbRead = 0;
1474 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1475 fRetEvents = rtPipePollCheck(pThis, fEvents);
1476 else if (GetLastError() == ERROR_IO_PENDING)
1477 {
1478 pThis->fIOPending = true;
1479 pThis->fZeroByteRead = true;
1480 }
1481 else
1482 fRetEvents = RTPOLL_EVT_ERROR;
1483 }
1484
1485 /* If we're still set for the waiting, record the poll set and
1486 mark the pipe used. */
1487 if (!fRetEvents)
1488 {
1489 pThis->cUsers++;
1490 pThis->hPollSet = hPollSet;
1491 }
1492 }
1493 }
1494 else
1495 {
1496 AssertFailed();
1497 fRetEvents = UINT32_MAX;
1498 }
1499
1500 RTCritSectLeave(&pThis->CritSect);
1501 return fRetEvents;
1502}
1503
1504
1505/**
1506 * Called after a WaitForMultipleObjects returned in order to check for pending
1507 * events and stop whatever actions that rtPipePollStart() initiated.
1508 *
1509 * @returns Event mask or 0.
1510 *
1511 * @param hPipe The pipe handle.
1512 * @param fEvents The events we're polling for.
1513 * @param fFinalEntry Set if this is the final entry for this handle
1514 * in this poll set. This can be used for dealing
1515 * with duplicate entries. Only keep in mind that
1516 * this method is called in reverse order, so the
1517 * first call will have this set (when the entire
1518 * set was processed).
1519 * @param fHarvestEvents Set if we should check for pending events.
1520 */
1521uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
1522{
1523 RTPIPEINTERNAL *pThis = hPipe;
1524 AssertPtrReturn(pThis, 0);
1525 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1526 RT_NOREF_PV(fFinalEntry);
1527 RT_NOREF_PV(fHarvestEvents);
1528
1529 int rc = RTCritSectEnter(&pThis->CritSect);
1530 AssertRCReturn(rc, 0);
1531
1532 Assert(pThis->cUsers > 0);
1533
1534
1535 /* Cancel the zero byte read. */
1536 uint32_t fRetEvents = 0;
1537 if (pThis->fZeroByteRead)
1538 {
1539 CancelIo(pThis->hPipe);
1540 DWORD cbRead = 0;
1541 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1542 && GetLastError() != ERROR_OPERATION_ABORTED)
1543 fRetEvents = RTPOLL_EVT_ERROR;
1544
1545 pThis->fIOPending = false;
1546 pThis->fZeroByteRead = false;
1547 }
1548
1549 /* harvest events. */
1550 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1551
1552 /* update counters. */
1553 pThis->cUsers--;
1554 /** @todo This isn't sane, or is it? See OS/2 impl. */
1555 if (!pThis->cUsers)
1556 pThis->hPollSet = NIL_RTPOLLSET;
1557
1558 RTCritSectLeave(&pThis->CritSect);
1559 return fRetEvents;
1560}
1561
Note: See TracBrowser for help on using the repository browser.

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