VirtualBox

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

Last change on this file since 95096 was 95096, 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.8 KB
Line 
1/* $Id: pipe-win.cpp 95096 2022-05-25 12:26: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. */
801#if 0
802 else if ( cbToWrite >= Info.WriteQuotaAvailable
803 && Info.OutboundQuota != 0
804 && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
805 )
806 {
807 cbToWrite = Info.WriteQuotaAvailable;
808 if (!cbToWrite)
809 rc = VINF_TRY_AGAIN;
810 }
811#endif
812 }
813 pThis->fPromisedWritable = false;
814
815 /* Do the bounce buffering. */
816 if ( pThis->cbBounceBufAlloc < cbToWrite
817 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
818 {
819 if (cbToWrite > RTPIPE_NT_SIZE)
820 cbToWrite = RTPIPE_NT_SIZE;
821 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
822 if (pv)
823 {
824 pThis->pbBounceBuf = (uint8_t *)pv;
825 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
826 }
827 else
828 rc = VERR_NO_MEMORY;
829 }
830 else if (cbToWrite > RTPIPE_NT_SIZE)
831 cbToWrite = RTPIPE_NT_SIZE;
832 if (RT_SUCCESS(rc) && cbToWrite)
833 {
834 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
835 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
836
837 /* Submit the write. */
838 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
839 DWORD cbWritten = 0;
840 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
841 &cbWritten, &pThis->Overlapped))
842 {
843 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
844 rc = VINF_SUCCESS;
845 }
846 else if (GetLastError() == ERROR_IO_PENDING)
847 {
848 *pcbWritten = cbToWrite;
849 pThis->fIOPending = true;
850 rc = VINF_SUCCESS;
851 }
852 else if (GetLastError() == ERROR_NO_DATA)
853 rc = VERR_BROKEN_PIPE;
854 else
855 rc = RTErrConvertFromWin32(GetLastError());
856 }
857 else if (RT_SUCCESS(rc))
858 *pcbWritten = 0;
859 }
860 else if (RT_SUCCESS(rc))
861 *pcbWritten = 0;
862
863 if (rc == VERR_BROKEN_PIPE)
864 pThis->fBrokenPipe = true;
865
866 pThis->cUsers--;
867 }
868 else
869 rc = VERR_WRONG_ORDER;
870 RTCritSectLeave(&pThis->CritSect);
871 }
872 return rc;
873}
874
875
876RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
877{
878 RTPIPEINTERNAL *pThis = hPipe;
879 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
880 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
881 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
882 AssertPtr(pvBuf);
883 AssertPtrNull(pcbWritten);
884
885 int rc = RTCritSectEnter(&pThis->CritSect);
886 if (RT_SUCCESS(rc))
887 {
888 /* No concurrent writers, sorry. */
889 if (pThis->cUsers == 0)
890 {
891 pThis->cUsers++;
892
893 /*
894 * If I/O is pending, wait for it to complete.
895 */
896 if (pThis->fIOPending)
897 {
898 rc = rtPipeWriteCheckCompletion(pThis);
899 while (rc == VINF_TRY_AGAIN)
900 {
901 Assert(pThis->fIOPending);
902 HANDLE hEvent = pThis->Overlapped.hEvent;
903 RTCritSectLeave(&pThis->CritSect);
904 WaitForSingleObject(hEvent, INFINITE);
905 RTCritSectEnter(&pThis->CritSect);
906 }
907 }
908 if (RT_SUCCESS(rc))
909 {
910 Assert(!pThis->fIOPending);
911 pThis->fPromisedWritable = false;
912
913 /*
914 * Try write everything.
915 * No bounce buffering, cUsers protects us.
916 */
917 size_t cbTotalWritten = 0;
918 while (cbToWrite > 0)
919 {
920 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
921 pThis->fIOPending = true;
922 RTCritSectLeave(&pThis->CritSect);
923
924 DWORD cbWritten = 0;
925 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
926 if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
927 rc = VINF_SUCCESS;
928 else if (GetLastError() == ERROR_IO_PENDING)
929 {
930 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
931 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
932 rc = VINF_SUCCESS;
933 else
934 rc = RTErrConvertFromWin32(GetLastError());
935 }
936 else if (GetLastError() == ERROR_NO_DATA)
937 rc = VERR_BROKEN_PIPE;
938 else
939 rc = RTErrConvertFromWin32(GetLastError());
940
941 RTCritSectEnter(&pThis->CritSect);
942 pThis->fIOPending = false;
943 if (RT_FAILURE(rc))
944 break;
945
946 /* advance */
947 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
948 cbWritten = cbToWriteInThisIteration;
949 pvBuf = (char const *)pvBuf + cbWritten;
950 cbTotalWritten += cbWritten;
951 cbToWrite -= cbWritten;
952 }
953
954 if (pcbWritten)
955 {
956 *pcbWritten = cbTotalWritten;
957 if ( RT_FAILURE(rc)
958 && cbTotalWritten
959 && rc != VERR_INVALID_POINTER)
960 rc = VINF_SUCCESS;
961 }
962 }
963
964 if (rc == VERR_BROKEN_PIPE)
965 pThis->fBrokenPipe = true;
966
967 pThis->cUsers--;
968 }
969 else
970 rc = VERR_WRONG_ORDER;
971 RTCritSectLeave(&pThis->CritSect);
972 }
973 return rc;
974
975#if 0 /** @todo r=bird: What's this? */
976 int rc = rtPipeTryBlocking(pThis);
977 if (RT_SUCCESS(rc))
978 {
979 size_t cbTotalWritten = 0;
980 while (cbToWrite > 0)
981 {
982 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
983 if (cbWritten < 0)
984 {
985 rc = RTErrConvertFromErrno(errno);
986 break;
987 }
988
989 /* advance */
990 pvBuf = (char const *)pvBuf + cbWritten;
991 cbTotalWritten += cbWritten;
992 cbToWrite -= cbWritten;
993 }
994
995 if (pcbWritten)
996 {
997 *pcbWritten = cbTotalWritten;
998 if ( RT_FAILURE(rc)
999 && cbTotalWritten
1000 && rc != VERR_INVALID_POINTER)
1001 rc = VINF_SUCCESS;
1002 }
1003
1004 ASMAtomicDecU32(&pThis->u32State);
1005 }
1006 return rc;
1007#endif
1008}
1009
1010
1011RTDECL(int) RTPipeFlush(RTPIPE hPipe)
1012{
1013 RTPIPEINTERNAL *pThis = hPipe;
1014 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1015 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1016 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
1017
1018 if (!FlushFileBuffers(pThis->hPipe))
1019 {
1020 int rc = RTErrConvertFromWin32(GetLastError());
1021 if (rc == VERR_BROKEN_PIPE)
1022 pThis->fBrokenPipe = true;
1023 return rc;
1024 }
1025 return VINF_SUCCESS;
1026}
1027
1028
1029RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
1030{
1031 RTPIPEINTERNAL *pThis = hPipe;
1032 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1033 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1034
1035 uint64_t const StartMsTS = RTTimeMilliTS();
1036
1037 int rc = RTCritSectEnter(&pThis->CritSect);
1038 if (RT_FAILURE(rc))
1039 return rc;
1040 for (unsigned iLoop = 0;; iLoop++)
1041 {
1042 HANDLE hWait = INVALID_HANDLE_VALUE;
1043 if (pThis->fRead)
1044 {
1045 if (pThis->fIOPending)
1046 hWait = pThis->Overlapped.hEvent;
1047 else
1048 {
1049 /* Peek at the pipe buffer and see how many bytes it contains. */
1050 DWORD cbAvailable;
1051 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
1052 && cbAvailable > 0)
1053 {
1054 rc = VINF_SUCCESS;
1055 break;
1056 }
1057
1058 /* Start a zero byte read operation that we can wait on. */
1059 if (cMillies == 0)
1060 {
1061 rc = VERR_TIMEOUT;
1062 break;
1063 }
1064 AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
1065 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
1066 DWORD cbRead = 0;
1067 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1068 {
1069 rc = VINF_SUCCESS;
1070 if (iLoop > 10)
1071 RTThreadYield();
1072 }
1073 else if (GetLastError() == ERROR_IO_PENDING)
1074 {
1075 pThis->cUsers++;
1076 pThis->fIOPending = true;
1077 pThis->fZeroByteRead = true;
1078 hWait = pThis->Overlapped.hEvent;
1079 }
1080 else
1081 rc = RTErrConvertFromWin32(GetLastError());
1082 }
1083 }
1084 else
1085 {
1086 if (pThis->fIOPending)
1087 {
1088 rc = rtPipeWriteCheckCompletion(pThis);
1089 if (RT_FAILURE(rc))
1090 break;
1091 }
1092 if (pThis->fIOPending)
1093 hWait = pThis->Overlapped.hEvent;
1094 else
1095 {
1096 FILE_PIPE_LOCAL_INFORMATION Info;
1097#if 1
1098 /* We can always write one bounce buffer full of data regardless of
1099 the pipe buffer state. We must of course take this into account,
1100 or code like "Full write buffer" test in tstRTPipe gets confused. */
1101 rc = VINF_SUCCESS;
1102 if (rtPipeQueryNtInfo(pThis, &Info))
1103 {
1104 /* Check for broken pipe. */
1105 if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
1106 pThis->fPromisedWritable = true;
1107 else
1108 rc = VERR_BROKEN_PIPE;
1109 }
1110 else
1111 pThis->fPromisedWritable = true;
1112 break;
1113
1114#else /* old code: */
1115 if (rtPipeQueryNtInfo(pThis, &Info))
1116 {
1117 /* Check for broken pipe. */
1118 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1119 {
1120 rc = VERR_BROKEN_PIPE;
1121 break;
1122 }
1123
1124 /* Check for available write buffer space. */
1125 if (Info.WriteQuotaAvailable > 0)
1126 {
1127 pThis->fPromisedWritable = false;
1128 rc = VINF_SUCCESS;
1129 break;
1130 }
1131
1132 /* delayed buffer alloc or timeout: phony promise
1133 later: See if we still can associate a semaphore with
1134 the pipe, like on OS/2. */
1135 if (Info.OutboundQuota == 0 || cMillies)
1136 {
1137 pThis->fPromisedWritable = true;
1138 rc = VINF_SUCCESS;
1139 break;
1140 }
1141 }
1142 else
1143 {
1144 pThis->fPromisedWritable = true;
1145 rc = VINF_SUCCESS;
1146 break;
1147 }
1148#endif
1149 }
1150 }
1151 if (RT_FAILURE(rc))
1152 break;
1153
1154 /*
1155 * Check for timeout.
1156 */
1157 DWORD cMsMaxWait = INFINITE;
1158 if ( cMillies != RT_INDEFINITE_WAIT
1159 && ( hWait != INVALID_HANDLE_VALUE
1160 || iLoop > 10)
1161 )
1162 {
1163 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
1164 if (cElapsed >= cMillies)
1165 {
1166 rc = VERR_TIMEOUT;
1167 break;
1168 }
1169 cMsMaxWait = cMillies - (uint32_t)cElapsed;
1170 }
1171
1172 /*
1173 * Wait.
1174 */
1175 if (hWait != INVALID_HANDLE_VALUE)
1176 {
1177 RTCritSectLeave(&pThis->CritSect);
1178
1179 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
1180 if (dwRc == WAIT_OBJECT_0)
1181 rc = VINF_SUCCESS;
1182 else if (dwRc == WAIT_TIMEOUT)
1183 rc = VERR_TIMEOUT;
1184 else if (dwRc == WAIT_ABANDONED)
1185 rc = VERR_INVALID_HANDLE;
1186 else
1187 rc = RTErrConvertFromWin32(GetLastError());
1188 if ( RT_FAILURE(rc)
1189 && pThis->u32Magic != RTPIPE_MAGIC)
1190 return rc;
1191
1192 RTCritSectEnter(&pThis->CritSect);
1193 if (pThis->fZeroByteRead)
1194 {
1195 pThis->cUsers--;
1196 pThis->fIOPending = false;
1197 if (rc != VINF_SUCCESS)
1198 CancelIo(pThis->hPipe);
1199 DWORD cbRead = 0;
1200 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1201 }
1202 if (RT_FAILURE(rc))
1203 break;
1204 }
1205 }
1206
1207 if (rc == VERR_BROKEN_PIPE)
1208 pThis->fBrokenPipe = true;
1209
1210 RTCritSectLeave(&pThis->CritSect);
1211 return rc;
1212}
1213
1214
1215RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
1216{
1217 RTPIPEINTERNAL *pThis = hPipe;
1218 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1219 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1220 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
1221 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
1222
1223 int rc = RTCritSectEnter(&pThis->CritSect);
1224 if (RT_FAILURE(rc))
1225 return rc;
1226
1227 /** @todo The file size should give the same info and be slightly faster... */
1228 DWORD cbAvailable = 0;
1229 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1230 {
1231#if ARCH_BITS == 32
1232 /*
1233 * Kludge!
1234 *
1235 * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
1236 * by the read position in the current message/buffer, so it could
1237 * potentially be too high. This may cause the caller to try read more
1238 * data than what's actually available, which may cause the read to
1239 * block when the caller thought it wouldn't.
1240 *
1241 * To get an accurate readable size, we have to provide an output
1242 * buffer and see how much we actually get back in it, as the data
1243 * peeking works correctly (as you would expect).
1244 */
1245 if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
1246 { /* No data available or kernel shouldn't be affected. */ }
1247 else
1248 {
1249 for (unsigned i = 0; ; i++)
1250 {
1251 uint8_t abBufStack[_16K];
1252 void *pvBufFree = NULL;
1253 void *pvBuf;
1254 DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
1255 if (cbBuf <= sizeof(abBufStack))
1256 {
1257 pvBuf = abBufStack;
1258 /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
1259 }
1260 else
1261 {
1262 pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
1263 if (!pvBuf)
1264 {
1265 rc = VERR_NO_TMP_MEMORY;
1266 cbAvailable = 1;
1267 break;
1268 }
1269 }
1270
1271 DWORD cbAvailable2 = 0;
1272 DWORD cbRead = 0;
1273 BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
1274 Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
1275 i, cbAvailable, cbRead, cbAvailable2, cbBuf));
1276
1277 RTMemTmpFree(pvBufFree);
1278
1279 if (fRc)
1280 {
1281 if (cbAvailable2 <= cbBuf || i >= 10)
1282 cbAvailable = cbRead;
1283 else
1284 {
1285 cbAvailable = cbAvailable2;
1286 continue;
1287 }
1288 }
1289 else
1290 {
1291 rc = RTErrConvertFromWin32(GetLastError());
1292 cbAvailable = 1;
1293 }
1294 break;
1295 }
1296 }
1297#endif
1298 *pcbReadable = cbAvailable;
1299 }
1300 else
1301 rc = RTErrConvertFromWin32(GetLastError());
1302
1303 RTCritSectLeave(&pThis->CritSect);
1304 return rc;
1305}
1306
1307
1308RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1309{
1310 RTPIPEINTERNAL *pThis = hPipe;
1311 AssertPtrReturn(pThis, 0);
1312 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1313
1314 int rc = RTCritSectEnter(&pThis->CritSect);
1315 AssertRCReturn(rc, 0);
1316
1317 rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
1318
1319 FILE_PIPE_LOCAL_INFORMATION Info;
1320 if (rtPipeQueryNtInfo(pThis, &Info))
1321 {
1322 pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
1323 pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
1324 }
1325
1326 RTCritSectLeave(&pThis->CritSect);
1327 return VINF_SUCCESS;
1328}
1329
1330
1331int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
1332{
1333 RTPIPEINTERNAL *pThis = hPipe;
1334 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1335 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1336
1337 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1338 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1339
1340 /* Later: Try register an event handle with the pipe like on OS/2, there is
1341 a file control for doing this obviously intended for the OS/2 subsys.
1342 The question is whether this still exists on Vista and W7. */
1343 *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
1344 return VINF_SUCCESS;
1345}
1346
1347
1348/**
1349 * Checks for pending events.
1350 *
1351 * @returns Event mask or 0.
1352 * @param pThis The pipe handle.
1353 * @param fEvents The desired events.
1354 */
1355static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1356{
1357 uint32_t fRetEvents = 0;
1358 if (pThis->fBrokenPipe)
1359 fRetEvents |= RTPOLL_EVT_ERROR;
1360 else if (pThis->fRead)
1361 {
1362 if (!pThis->fIOPending)
1363 {
1364 DWORD cbAvailable;
1365 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1366 {
1367 if ( (fEvents & RTPOLL_EVT_READ)
1368 && cbAvailable > 0)
1369 fRetEvents |= RTPOLL_EVT_READ;
1370 }
1371 else
1372 {
1373 if (GetLastError() == ERROR_BROKEN_PIPE)
1374 pThis->fBrokenPipe = true;
1375 fRetEvents |= RTPOLL_EVT_ERROR;
1376 }
1377 }
1378 }
1379 else
1380 {
1381 if (pThis->fIOPending)
1382 {
1383 rtPipeWriteCheckCompletion(pThis);
1384 if (pThis->fBrokenPipe)
1385 fRetEvents |= RTPOLL_EVT_ERROR;
1386 }
1387 if ( !pThis->fIOPending
1388 && !fRetEvents)
1389 {
1390 FILE_PIPE_LOCAL_INFORMATION Info;
1391 if (rtPipeQueryNtInfo(pThis, &Info))
1392 {
1393 /* Check for broken pipe. */
1394 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1395 {
1396 fRetEvents = RTPOLL_EVT_ERROR;
1397 pThis->fBrokenPipe = true;
1398 }
1399
1400 /* Check if there is available buffer space. */
1401 if ( !fRetEvents
1402 && (fEvents & RTPOLL_EVT_WRITE)
1403 && ( Info.WriteQuotaAvailable > 0
1404 || Info.OutboundQuota == 0)
1405 )
1406 fRetEvents |= RTPOLL_EVT_WRITE;
1407 }
1408 else if (fEvents & RTPOLL_EVT_WRITE)
1409 fRetEvents |= RTPOLL_EVT_WRITE;
1410 }
1411 }
1412
1413 return fRetEvents;
1414}
1415
1416
1417/**
1418 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1419 * clear, starts whatever actions we've got running during the poll call.
1420 *
1421 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1422 * Event mask (in @a fEvents) and no actions if the handle is ready
1423 * already.
1424 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1425 * different poll set.
1426 *
1427 * @param hPipe The pipe handle.
1428 * @param hPollSet The poll set handle (for access checks).
1429 * @param fEvents The events we're polling for.
1430 * @param fFinalEntry Set if this is the final entry for this handle
1431 * in this poll set. This can be used for dealing
1432 * with duplicate entries.
1433 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1434 * we'll wait for an event to occur.
1435 */
1436uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1437{
1438 /** @todo All this polling code could be optimized to make fewer system
1439 * calls; like for instance the ResetEvent calls. */
1440 RTPIPEINTERNAL *pThis = hPipe;
1441 AssertPtrReturn(pThis, UINT32_MAX);
1442 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1443 RT_NOREF_PV(fFinalEntry);
1444
1445 int rc = RTCritSectEnter(&pThis->CritSect);
1446 AssertRCReturn(rc, UINT32_MAX);
1447
1448 /* Check that this is the only current use of this pipe. */
1449 uint32_t fRetEvents;
1450 if ( pThis->cUsers == 0
1451 || pThis->hPollSet == hPollSet)
1452 {
1453 /* Check what the current events are. */
1454 fRetEvents = rtPipePollCheck(pThis, fEvents);
1455 if ( !fRetEvents
1456 && !fNoWait)
1457 {
1458 /* Make sure the event semaphore has been reset. */
1459 if (!pThis->fIOPending)
1460 {
1461 rc = ResetEvent(pThis->Overlapped.hEvent);
1462 Assert(rc == TRUE);
1463 }
1464
1465 /* Kick off the zero byte read thing if applicable. */
1466 if ( !pThis->fIOPending
1467 && pThis->fRead
1468 && (fEvents & RTPOLL_EVT_READ)
1469 )
1470 {
1471 DWORD cbRead = 0;
1472 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1473 fRetEvents = rtPipePollCheck(pThis, fEvents);
1474 else if (GetLastError() == ERROR_IO_PENDING)
1475 {
1476 pThis->fIOPending = true;
1477 pThis->fZeroByteRead = true;
1478 }
1479 else
1480 fRetEvents = RTPOLL_EVT_ERROR;
1481 }
1482
1483 /* If we're still set for the waiting, record the poll set and
1484 mark the pipe used. */
1485 if (!fRetEvents)
1486 {
1487 pThis->cUsers++;
1488 pThis->hPollSet = hPollSet;
1489 }
1490 }
1491 }
1492 else
1493 {
1494 AssertFailed();
1495 fRetEvents = UINT32_MAX;
1496 }
1497
1498 RTCritSectLeave(&pThis->CritSect);
1499 return fRetEvents;
1500}
1501
1502
1503/**
1504 * Called after a WaitForMultipleObjects returned in order to check for pending
1505 * events and stop whatever actions that rtPipePollStart() initiated.
1506 *
1507 * @returns Event mask or 0.
1508 *
1509 * @param hPipe The pipe handle.
1510 * @param fEvents The events we're polling for.
1511 * @param fFinalEntry Set if this is the final entry for this handle
1512 * in this poll set. This can be used for dealing
1513 * with duplicate entries. Only keep in mind that
1514 * this method is called in reverse order, so the
1515 * first call will have this set (when the entire
1516 * set was processed).
1517 * @param fHarvestEvents Set if we should check for pending events.
1518 */
1519uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
1520{
1521 RTPIPEINTERNAL *pThis = hPipe;
1522 AssertPtrReturn(pThis, 0);
1523 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1524 RT_NOREF_PV(fFinalEntry);
1525 RT_NOREF_PV(fHarvestEvents);
1526
1527 int rc = RTCritSectEnter(&pThis->CritSect);
1528 AssertRCReturn(rc, 0);
1529
1530 Assert(pThis->cUsers > 0);
1531
1532
1533 /* Cancel the zero byte read. */
1534 uint32_t fRetEvents = 0;
1535 if (pThis->fZeroByteRead)
1536 {
1537 CancelIo(pThis->hPipe);
1538 DWORD cbRead = 0;
1539 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1540 && GetLastError() != ERROR_OPERATION_ABORTED)
1541 fRetEvents = RTPOLL_EVT_ERROR;
1542
1543 pThis->fIOPending = false;
1544 pThis->fZeroByteRead = false;
1545 }
1546
1547 /* harvest events. */
1548 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1549
1550 /* update counters. */
1551 pThis->cUsers--;
1552 /** @todo This isn't sane, or is it? See OS/2 impl. */
1553 if (!pThis->cUsers)
1554 pThis->hPollSet = NIL_RTPOLLSET;
1555
1556 RTCritSectLeave(&pThis->CritSect);
1557 return fRetEvents;
1558}
1559
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