pipe-os2.cpp revision 9f7c2715c84963f7a53bdbd1b9e9dadc533e6138
/* $Id$ */
/** @file
* IPRT - Anonymous Pipes, OS/2 Implementation.
*/
/*
* Copyright (C) 2010-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define INCL_ERRORS
#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <iprt/critsect.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The pipe buffer size we prefer. */
#define RTPIPE_OS2_SIZE _32K
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct RTPIPEINTERNAL
{
/** Magic value (RTPIPE_MAGIC). */
/** The pipe handle. */
/** Set if this is the read end, clear if it's the write end. */
bool fRead;
/** Whether the pipe is in blocking or non-blocking mode. */
bool fBlocking;
/** Set if the pipe is broken. */
bool fBrokenPipe;
/** Usage counter. */
/** The event semaphore associated with the pipe. */
/** The handle of the poll set currently polling on this pipe.
* We can only have one poller at the time (lazy bird). */
/** Critical section protecting the above members.
/**
* Ensures that the pipe has a semaphore associated with it.
*
* @returns VBox status code.
* @param pThis The pipe.
*/
{
return VINF_SUCCESS;
{
{
return VINF_SUCCESS;
}
}
return RTErrConvertFromOS2(orc);
}
{
/*
* Try create and connect a pipe pair.
*/
int rc;
for (;;)
{
static volatile uint32_t g_iNextPipe = 0;
char szName[128];
RTStrPrintf(szName, sizeof(szName), "\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
/*
* Create the read end of the pipe.
*/
if (fFlags & RTPIPE_C_INHERIT_READ)
fOpenMode |= NP_INHERIT;
else
orc = DosCreateNPipe((PSZ)szName, &hPipeR, fOpenMode, fPipeMode, RTPIPE_OS2_SIZE, RTPIPE_OS2_SIZE, NP_DEFAULT_WAIT);
{
{
/*
* Connect to the pipe (the write end), attach sem below.
*/
if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
break;
}
}
return RTErrConvertFromOS2(orc);
/* else: try again with a new name */
}
/*
* Create the two handles.
*/
if (pThisR)
{
if (pThisW)
{
/* Crit sects. */
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/* Initialize the structures. */
//pThisR->fBrokenPipe = false;
//pThisW->fBrokenPipe = false;
//pThisR->cUsers = 0;
//pThisW->cUsers = 0;
*phPipeRead = pThisR;
*phPipeWrite = pThisW;
return VINF_SUCCESS;
}
}
}
else
rc = VERR_NO_MEMORY;
}
else
rc = VERR_NO_MEMORY;
/* Don't call DosDisConnectNPipe! */
return rc;
}
{
if (pThis == NIL_RTPIPE)
return VINF_SUCCESS;
/*
* Do the cleanup.
*/
AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
/* Don't call DosDisConnectNPipe! */
{
}
return VINF_SUCCESS;
}
{
/*
* Get and validate the pipe handle info.
*/
#if 0
union
{
} Buf;
{
/* Sorry, anonymous pips are not supported. */
return VERR_INVALID_HANDLE;
}
#endif
ULONG fPipeState = 0;
{
/* Sorry, anonymous pips are not supported. */
return VERR_INVALID_HANDLE;
}
ULONG fFileState = 0;
AssertMsgReturn( (fFileState & 0x3) == (fFlags & RTPIPE_N_READ ? OPEN_ACCESS_READONLY : OPEN_ACCESS_WRITEONLY)
/*
* Looks kind of OK. Fix the inherit flag.
*/
orc = DosSetFHState(hNative, (fFileState & (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE))
/*
* Create a handle so we can try rtPipeQueryInfo on it
* and see if we need to duplicate it to make that call work.
*/
if (!pThis)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
//pThis->fBrokenPipe = false;
//pThis->cUsers = 0;
return VINF_SUCCESS;
//RTCritSectDelete(&pThis->CritSect);
}
return rc;
}
{
}
/**
* Prepare blocking mode.
*
* @returns IPRT status code.
* @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
* attempted.
*
* @param pThis The pipe handle.
*
* @remarks Caller owns the critical section.
*/
{
{
return VERR_WRONG_ORDER;
{
return RTErrConvertFromOS2(orc);
pThis->fBrokenPipe = true;
}
}
return VINF_SUCCESS;
}
/**
* Prepare non-blocking mode.
*
* @returns IPRT status code.
* @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
* attempted.
*
* @param pThis The pipe handle.
*/
{
{
return VERR_WRONG_ORDER;
{
return RTErrConvertFromOS2(orc);
pThis->fBrokenPipe = true;
}
}
return VINF_SUCCESS;
}
/**
* Checks if the read pipe has been broken.
*
* @returns true if broken, false if no.
* @param pThis The pipe handle (read).
*/
{
#if 0
/*
* Query it via the semaphore. Not sure how fast this is...
*/
{
return true;
return false;
}
/*
* Fall back / alternative method.
*/
#endif
{
if (orc != ERROR_PIPE_BUSY)
return false;
}
return ulState != NP_STATE_CONNECTED;
}
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
{
else
}
else if (orc == ERROR_NO_DATA)
{
*pcbRead = 0;
rc = VINF_TRY_AGAIN;
}
else
if (rc == VERR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
else
}
return rc;
}
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
size_t cbTotalRead = 0;
while (cbToRead > 0)
{
{
break;
}
{
break;
}
/* advance */
cbTotalRead += cbActual;
}
if (pcbRead)
{
*pcbRead = cbTotalRead;
if ( RT_FAILURE(rc)
&& cbTotalRead)
rc = VINF_SUCCESS;
}
if (rc == VERR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
else
}
return rc;
}
/**
* Gets the available write buffer size of the pipe.
*
* @returns Number of bytes, 1 on failure.
* @param pThis The pipe handle.
*/
{
#if 0 /* Not sure which is more efficient, neither are really optimal, I fear. */
/*
* Query via semaphore state.
* This will walk the list of active named pipes...
*/
/** @todo Check how hev and hpipe are associated, if complicated, use the
* alternative method below. */
{
return 0;
}
#else
/*
* Query via the pipe info.
* This will have to lookup and store the pipe name.
*/
union
{
} Buf;
#endif
return 1;
}
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (cbToWrite > 0)
{
{
/* Retry with the request adjusted to the available buffer space. */
}
{
*pcbWritten = cbActual;
if (cbActual == 0)
rc = VINF_TRY_AGAIN;
}
else
{
if (rc == VERR_PIPE_NOT_CONNECTED)
}
}
else
*pcbWritten = 0;
if (rc == VERR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
else
}
return rc;
}
RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
size_t cbTotalWritten = 0;
while (cbToWrite > 0)
{
{
if (rc == VERR_PIPE_NOT_CONNECTED)
break;
}
}
if (pcbWritten)
{
if ( RT_FAILURE(rc)
&& cbTotalWritten)
rc = VINF_SUCCESS;
}
if (rc == VERR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
else
}
return rc;
}
{
{
if (rc == VERR_BROKEN_PIPE)
{
pThis->fBrokenPipe = true;
}
return rc;
}
return VINF_SUCCESS;
}
{
if (RT_FAILURE(rc))
return rc;
if (RT_SUCCESS(rc))
{
{
/*
* Check the handle state.
*/
{
break;
}
int i = 0;
i++;
else
i++;
break;
Assert(aStates[i].fStatus == NPSS_WSPACE || aStates[i].fStatus == NPSS_RDATA || aStates[i].fStatus == NPSS_EOI);
break;
/*
* Check for timeout.
*/
if (cMillies != RT_INDEFINITE_WAIT)
{
{
rc = VERR_TIMEOUT;
break;
}
}
/*
* Wait.
*/
{
break;
}
}
if (rc == VERR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
return rc;
}
{
if (RT_FAILURE(rc))
return rc;
{
else
}
else
return rc;
}
#if 0
/**
* Internal RTPollSetAdd helper that returns the handle that should be added to
* the pollset.
*
* @returns Valid handle on success, INVALID_HANDLE_VALUE on failure.
* @param hPipe The pipe handle.
* @param fEvents The events we're polling for.
* @param ph where to put the primary handle.
*/
{
/* Later: Try register an event handle with the pipe like on OS/2, there is
a file control for doing this obviously intended for the OS/2 subsys.
The question is whether this still exists on Vista and W7. */
return VINF_SUCCESS;
}
/**
* Checks for pending events.
*
* @returns Event mask or 0.
* @param pThis The pipe handle.
* @param fEvents The desired events.
*/
{
uint32_t fRetEvents = 0;
if (pThis->fBrokenPipe)
{
if (!pThis->fIOPending)
{
{
if ( (fEvents & RTPOLL_EVT_READ)
&& cbAvailable > 0)
}
else
{
if (GetLastError() == ERROR_BROKEN_PIPE)
pThis->fBrokenPipe = true;
}
}
}
else
{
if (pThis->fIOPending)
{
if (pThis->fBrokenPipe)
}
if ( !pThis->fIOPending
&& !fRetEvents)
{
{
/* Check for broken pipe. */
{
pThis->fBrokenPipe = true;
}
/* Check if there is available buffer space. */
if ( !fRetEvents
&& (fEvents & RTPOLL_EVT_WRITE)
&& ( Info.WriteQuotaAvailable > 0
|| Info.OutboundQuota == 0)
)
}
else if (fEvents & RTPOLL_EVT_WRITE)
}
}
return fRetEvents;
}
/**
* Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
* clear, starts whatever actions we've got running during the poll call.
*
* @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
* Event mask (in @a fEvents) and no actions if the handle is ready
* already.
* UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
* different poll set.
*
* @param hPipe The pipe handle.
* @param hPollSet The poll set handle (for access checks).
* @param fEvents The events we're polling for.
* @param fFinalEntry Set if this is the final entry for this handle
* in this poll set. This can be used for dealing
* with duplicate entries.
* @param fNoWait Set if it's a zero-wait poll call. Clear if
* we'll wait for an event to occur.
*/
uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
{
/** @todo All this polling code could be optimized to make fewer system
* calls; like for instance the ResetEvent calls. */
/* Check that this is the only current use of this pipe. */
{
/* Check what the current events are. */
if ( !fRetEvents
&& !fNoWait)
{
/* Make sure the event semaphore has been reset. */
if (!pThis->fIOPending)
{
}
/* Kick off the zero byte read thing if applicable. */
if ( !pThis->fIOPending
&& (fEvents & RTPOLL_EVT_READ)
)
{
else if (GetLastError() == ERROR_IO_PENDING)
{
pThis->fIOPending = true;
pThis->fZeroByteRead = true;
}
else
}
/* If we're still set for the waiting, record the poll set and
mark the pipe used. */
if (!fRetEvents)
{
}
}
}
else
{
AssertFailed();
}
return fRetEvents;
}
/**
* Called after a WaitForMultipleObjects returned in order to check for pending
* events and stop whatever actions that rtPipePollStart() initiated.
*
* @returns Event mask or 0.
*
* @param hPipe The pipe handle.
* @param fEvents The events we're polling for.
* @param fFinalEntry Set if this is the final entry for this handle
* in this poll set. This can be used for dealing
* with duplicate entries. Only keep in mind that
* this method is called in reverse order, so the
* first call will have this set (when the entire
* set was processed).
* @param fHarvestEvents Set if we should check for pending events.
*/
{
AssertPtrReturn(pThis, 0);
AssertRCReturn(rc, 0);
/* Cancel the zero byte read. */
uint32_t fRetEvents = 0;
if (pThis->fZeroByteRead)
{
&& GetLastError() != ERROR_OPERATION_ABORTED)
pThis->fIOPending = false;
pThis->fZeroByteRead = false;
}
/* harvest events. */
/* update counters. */
return fRetEvents;
}
#endif