summaryrefslogtreecommitdiff
path: root/lib/server/WinNamedPipeStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/server/WinNamedPipeStream.cpp')
-rw-r--r--lib/server/WinNamedPipeStream.cpp555
1 files changed, 267 insertions, 288 deletions
diff --git a/lib/server/WinNamedPipeStream.cpp b/lib/server/WinNamedPipeStream.cpp
index 1179516e..448a3c9d 100644
--- a/lib/server/WinNamedPipeStream.cpp
+++ b/lib/server/WinNamedPipeStream.cpp
@@ -19,10 +19,12 @@
#include <errno.h>
#include <windows.h>
-#include "WinNamedPipeStream.h"
-#include "ServerException.h"
+#include "autogen_ConnectionException.h"
+#include "autogen_ServerException.h"
+#include "BoxTime.h"
#include "CommonException.h"
#include "Socket.h"
+#include "WinNamedPipeStream.h"
#include "MemLeakFindOn.h"
@@ -37,13 +39,14 @@ std::string WinNamedPipeStream::sPipeNamePrefix = "\\\\.\\pipe\\";
//
// --------------------------------------------------------------------------
WinNamedPipeStream::WinNamedPipeStream()
- : mSocketHandle(INVALID_HANDLE_VALUE),
- mReadableEvent(INVALID_HANDLE_VALUE),
- mBytesInBuffer(0),
- mReadClosed(false),
- mWriteClosed(false),
- mIsServer(false),
- mIsConnected(false)
+: mSocketHandle(INVALID_HANDLE_VALUE),
+ mReadableEvent(INVALID_HANDLE_VALUE),
+ mBytesInBuffer(0),
+ mReadClosed(false),
+ mWriteClosed(false),
+ mIsServer(false),
+ mIsConnected(false),
+ mNeedAnotherRead(false)
{ }
// --------------------------------------------------------------------------
@@ -55,14 +58,21 @@ WinNamedPipeStream::WinNamedPipeStream()
//
// --------------------------------------------------------------------------
WinNamedPipeStream::WinNamedPipeStream(HANDLE hNamedPipe)
- : mSocketHandle(hNamedPipe),
- mReadableEvent(INVALID_HANDLE_VALUE),
- mBytesInBuffer(0),
- mReadClosed(false),
- mWriteClosed(false),
- mIsServer(true),
- mIsConnected(true)
+: mSocketHandle(hNamedPipe),
+ mReadableEvent(INVALID_HANDLE_VALUE),
+ mBytesInBuffer(0),
+ mReadClosed(false),
+ mWriteClosed(false),
+ mIsServer(true),
+ mIsConnected(true),
+ mNeedAnotherRead(false)
{
+ StartFirstRead();
+}
+
+// Start the first overlapped read
+void WinNamedPipeStream::StartFirstRead()
+{
// create the Readable event
mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -74,23 +84,50 @@ WinNamedPipeStream::WinNamedPipeStream(HANDLE hNamedPipe)
THROW_EXCEPTION(CommonException, Internal)
}
- // initialise the OVERLAPPED structure
+ StartOverlappedRead();
+}
+
+void WinNamedPipeStream::StartOverlappedRead()
+{
+ // We should only do this when the buffer is empty. We don't want
+ // to start an overlapped read anywhere else than the start of the
+ // buffer, because it could complete at any time and we don't want
+ // to mess about with interrupting the read already in progress.
+ ASSERT(mBytesInBuffer == 0);
+
+ // Initialise the OVERLAPPED structure
memset(&mReadOverlap, 0, sizeof(mReadOverlap));
mReadOverlap.hEvent = mReadableEvent;
- // start the first overlapped read
if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
NULL, &mReadOverlap))
{
DWORD err = GetLastError();
-
- if (err != ERROR_IO_PENDING)
+ if (err == ERROR_IO_PENDING)
+ {
+ // Don't reset yet, there might be data
+ // in the buffer waiting to be read,
+ // will check below.
+ // ResetEvent(mReadableEvent);
+ }
+ else if (err == ERROR_HANDLE_EOF)
+ {
+ BOX_INFO("Control client disconnected");
+ mReadClosed = true;
+ }
+ else if (err == ERROR_BROKEN_PIPE ||
+ err == ERROR_PIPE_NOT_CONNECTED)
+ {
+ BOX_NOTICE("Control client disconnected");
+ mReadClosed = true;
+ mIsConnected = false;
+ }
+ else
{
- BOX_ERROR("Failed to start overlapped read: " <<
- GetErrorMessage(err));
Close();
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketReadError)
+ THROW_WIN_ERROR_NUMBER("Failed to start overlapped "
+ "read", err, ConnectionException,
+ SocketReadError)
}
}
}
@@ -105,6 +142,12 @@ WinNamedPipeStream::WinNamedPipeStream(HANDLE hNamedPipe)
// --------------------------------------------------------------------------
WinNamedPipeStream::~WinNamedPipeStream()
{
+ for(std::list<WriteInProgress*>::iterator i = mWritesInProgress.begin();
+ i != mWritesInProgress.end(); i++)
+ {
+ delete *i;
+ }
+
if (mSocketHandle != INVALID_HANDLE_VALUE)
{
try
@@ -157,36 +200,7 @@ void WinNamedPipeStream::Accept()
mIsServer = true; // must flush and disconnect before closing
mIsConnected = true;
- // create the Readable event
- mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-
- if (mReadableEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to create the Readable event: " <<
- GetErrorMessage(GetLastError()));
- Close();
- THROW_EXCEPTION(CommonException, Internal)
- }
-
- // initialise the OVERLAPPED structure
- memset(&mReadOverlap, 0, sizeof(mReadOverlap));
- mReadOverlap.hEvent = mReadableEvent;
-
- // start the first overlapped read
- if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
- NULL, &mReadOverlap))
- {
- DWORD err = GetLastError();
-
- if (err != ERROR_IO_PENDING)
- {
- BOX_ERROR("Failed to start overlapped read: " <<
- GetErrorMessage(err));
- Close();
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketReadError)
- }
- }
+ StartFirstRead();
}
*/
@@ -214,7 +228,7 @@ void WinNamedPipeStream::Connect(const std::string& rName)
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING,
- 0, // default attributes
+ 0, // FILE_FLAG_OVERLAPPED, // dwFlagsAndAttributes
NULL); // no template file
if (mSocketHandle == INVALID_HANDLE_VALUE)
@@ -237,6 +251,86 @@ void WinNamedPipeStream::Connect(const std::string& rName)
mWriteClosed = false;
mIsServer = false; // just close the socket
mIsConnected = true;
+
+ StartFirstRead();
+}
+
+// Returns true if the operation is complete (and you will need to start
+// another one), or false otherwise (you can wait again).
+bool WinNamedPipeStream::WaitForOverlappedOperation(OVERLAPPED& Overlapped,
+ int Timeout, int64_t* pBytesTransferred)
+{
+ if (Timeout == IOStream::TimeOutInfinite)
+ {
+ Timeout = INFINITE;
+ }
+
+ // overlapped I/O completed successfully? (wait if needed)
+ DWORD waitResult = WaitForSingleObject(Overlapped.hEvent, Timeout);
+ DWORD NumBytesTransferred = -1;
+
+ if (waitResult == WAIT_FAILED)
+ {
+ THROW_WIN_ERROR_NUMBER("Failed to wait for overlapped I/O",
+ GetLastError(), ServerException, Internal);
+ }
+
+ if (waitResult == WAIT_ABANDONED)
+ {
+ THROW_EXCEPTION_MESSAGE(ServerException, Internal,
+ "Wait for overlapped I/O abandoned by system");
+ }
+
+ if (waitResult == WAIT_TIMEOUT)
+ {
+ // wait timed out, nothing to read
+ *pBytesTransferred = 0;
+ return false;
+ }
+
+ if (waitResult != WAIT_OBJECT_0)
+ {
+ THROW_EXCEPTION_MESSAGE(ServerException, BadSocketHandle,
+ "Failed to wait for overlapped I/O: unknown "
+ "result code: " << waitResult);
+ }
+
+ // Overlapped operation completed successfully. Return the number
+ // of bytes transferred.
+ if (GetOverlappedResult(mSocketHandle, &Overlapped,
+ &NumBytesTransferred, TRUE))
+ {
+ *pBytesTransferred = NumBytesTransferred;
+ return true;
+ }
+
+ // We are here because GetOverlappedResult() informed us that the
+ // overlapped operation encountered an error, so what was it?
+ DWORD err = GetLastError();
+
+ if (err == ERROR_HANDLE_EOF)
+ {
+ Close();
+ *pBytesTransferred = 0;
+ return true;
+ }
+
+ // ERROR_NO_DATA is a strange name for
+ // "The pipe is being closed". No exception wanted.
+
+ if (err == ERROR_NO_DATA ||
+ err == ERROR_PIPE_NOT_CONNECTED ||
+ err == ERROR_BROKEN_PIPE)
+ {
+ BOX_INFO(BOX_WIN_ERRNO_MESSAGE(err,
+ "Named pipe peer disconnected"));
+ Close();
+ *pBytesTransferred = 0;
+ return true;
+ }
+
+ THROW_WIN_ERROR_NUMBER("Failed to wait for overlapped I/O "
+ "to complete", err, ConnectionException, SocketReadError);
}
// --------------------------------------------------------------------------
@@ -249,192 +343,61 @@ void WinNamedPipeStream::Connect(const std::string& rName)
// --------------------------------------------------------------------------
int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
{
- // TODO no support for timeouts yet
- if (!mIsServer && Timeout != IOStream::TimeOutInfinite)
- {
- THROW_EXCEPTION(CommonException, AssertFailed)
- }
-
if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
{
- THROW_EXCEPTION(ServerException, BadSocketHandle)
+ THROW_EXCEPTION_MESSAGE(ServerException, BadSocketHandle,
+ "Tried to read from closed pipe");
}
if (mReadClosed)
{
- THROW_EXCEPTION(ConnectionException, SocketShutdownError)
+ THROW_EXCEPTION_MESSAGE(ConnectionException,
+ SocketShutdownError, "Tried to read from closing pipe");
}
// ensure safe to cast NBytes to unsigned
if (NBytes < 0)
{
- THROW_EXCEPTION(CommonException, AssertFailed)
+ THROW_EXCEPTION(CommonException, AssertFailed);
}
- DWORD NumBytesRead;
+ int64_t NumBytesRead;
- if (mIsServer)
+ // Satisfy from buffer if possible, to avoid blocking on read.
+ if (mBytesInBuffer == 0)
{
- // satisfy from buffer if possible, to avoid
- // blocking on read.
- bool needAnotherRead = false;
- if (mBytesInBuffer == 0)
- {
- // overlapped I/O completed successfully?
- // (wait if needed)
- DWORD waitResult = WaitForSingleObject(
- mReadOverlap.hEvent, Timeout);
-
- if (waitResult == WAIT_ABANDONED)
- {
- BOX_ERROR("Wait for command socket read "
- "abandoned by system");
- THROW_EXCEPTION(ServerException,
- BadSocketHandle);
- }
- else if (waitResult == WAIT_TIMEOUT)
- {
- // wait timed out, nothing to read
- NumBytesRead = 0;
- }
- else if (waitResult != WAIT_OBJECT_0)
- {
- BOX_ERROR("Failed to wait for command "
- "socket read: unknown result " <<
- waitResult);
- }
- // object is ready to read from
- else if (GetOverlappedResult(mSocketHandle,
- &mReadOverlap, &NumBytesRead, TRUE))
- {
- needAnotherRead = true;
- }
- else
- {
- DWORD err = GetLastError();
-
- if (err == ERROR_HANDLE_EOF)
- {
- mReadClosed = true;
- }
- else
- {
- if (err == ERROR_BROKEN_PIPE)
- {
- BOX_NOTICE("Control client "
- "disconnected");
- }
- else
- {
- BOX_ERROR("Failed to wait for "
- "ReadFile to complete: "
- << GetErrorMessage(err));
- }
-
- Close();
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketReadError)
- }
- }
- }
- else
- {
- NumBytesRead = 0;
- }
-
- size_t BytesToCopy = NumBytesRead + mBytesInBuffer;
- size_t BytesRemaining = 0;
-
- if (BytesToCopy > (size_t)NBytes)
- {
- BytesRemaining = BytesToCopy - NBytes;
- BytesToCopy = NBytes;
- }
-
- memcpy(pBuffer, mReadBuffer, BytesToCopy);
- memmove(mReadBuffer, mReadBuffer + BytesToCopy, BytesRemaining);
-
- mBytesInBuffer = BytesRemaining;
- NumBytesRead = BytesToCopy;
-
- if (needAnotherRead)
+ if (mNeedAnotherRead)
{
- // reinitialise the OVERLAPPED structure
- memset(&mReadOverlap, 0, sizeof(mReadOverlap));
- mReadOverlap.hEvent = mReadableEvent;
+ // Start the next overlapped read
+ StartOverlappedRead();
}
- // start the next overlapped read
- if (needAnotherRead && !ReadFile(mSocketHandle,
- mReadBuffer + mBytesInBuffer,
- sizeof(mReadBuffer) - mBytesInBuffer,
- NULL, &mReadOverlap))
- {
- DWORD err = GetLastError();
- if (err == ERROR_IO_PENDING)
- {
- // Don't reset yet, there might be data
- // in the buffer waiting to be read,
- // will check below.
- // ResetEvent(mReadableEvent);
- }
- else if (err == ERROR_HANDLE_EOF)
- {
- mReadClosed = true;
- }
- else if (err == ERROR_BROKEN_PIPE)
- {
- BOX_ERROR("Control client disconnected");
- mReadClosed = true;
- }
- else
- {
- BOX_ERROR("Failed to start overlapped read: "
- << GetErrorMessage(err));
- Close();
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketReadError)
- }
- }
+ mNeedAnotherRead = WaitForOverlappedOperation(mReadOverlap,
+ Timeout, &NumBytesRead);
}
else
{
- if (!ReadFile(
- mSocketHandle, // pipe handle
- pBuffer, // buffer to receive reply
- NBytes, // size of buffer
- &NumBytesRead, // number of bytes read
- NULL)) // not overlapped
- {
- DWORD err = GetLastError();
-
- Close();
+ // Just return the existing data from the buffer
+ // this time around. The caller should call again,
+ // and then the buffer will be empty.
+ NumBytesRead = 0;
+ }
- // ERROR_NO_DATA is a strange name for
- // "The pipe is being closed". No exception wanted.
-
- if (err == ERROR_NO_DATA ||
- err == ERROR_PIPE_NOT_CONNECTED)
- {
- NumBytesRead = 0;
- }
- else
- {
- BOX_ERROR("Failed to read from control socket: "
- << GetErrorMessage(err));
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketReadError)
- }
- }
-
- // Closed for reading at EOF?
- if (NumBytesRead == 0)
- {
- mReadClosed = true;
- }
+ int BytesToCopy = NumBytesRead + mBytesInBuffer;
+
+ if (NBytes < BytesToCopy)
+ {
+ BytesToCopy = NBytes;
}
-
- return NumBytesRead;
+
+ memcpy(pBuffer, mReadBuffer, BytesToCopy);
+
+ size_t BytesRemaining = mBytesInBuffer + NumBytesRead - BytesToCopy;
+ ASSERT(BytesToCopy + BytesRemaining <= sizeof(mReadBuffer));
+ memmove(mReadBuffer, mReadBuffer + BytesToCopy, BytesRemaining);
+ mBytesInBuffer = BytesRemaining;
+
+ return BytesToCopy;
}
// --------------------------------------------------------------------------
@@ -445,8 +408,15 @@ int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
-void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
+void WinNamedPipeStream::Write(const void *pBuffer, int NBytes, int Timeout)
{
+ // Calculate the deadline at the beginning. Not valid if Timeout is
+ // IOStream::TimeOutInfinite!
+ ASSERT(Timeout != IOStream::TimeOutInfinite);
+
+ box_time_t deadline = GetCurrentBoxTime() +
+ MilliSecondsToBoxTime(Timeout);
+
if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
{
THROW_EXCEPTION(ServerException, BadSocketHandle)
@@ -454,41 +424,62 @@ void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
// Buffer in byte sized type.
ASSERT(sizeof(char) == 1);
- const char *pByteBuffer = (char *)pBuffer;
-
- int NumBytesWrittenTotal = 0;
-
- while (NumBytesWrittenTotal < NBytes)
+ WriteInProgress* new_write = new WriteInProgress(
+ std::string((char *)pBuffer, NBytes));
+
+ // Start the WriteFile operation, and add to queue if pending.
+ BOOL Success = WriteFile(
+ mSocketHandle, // pipe handle
+ new_write->mBuffer.c_str(), // message
+ NBytes, // message length
+ NULL, // bytes written this time
+ &(new_write->mOverlap));
+
+ if (Success == TRUE)
{
- DWORD NumBytesWrittenThisTime = 0;
-
- bool Success = WriteFile(
- mSocketHandle, // pipe handle
- pByteBuffer + NumBytesWrittenTotal, // message
- NBytes - NumBytesWrittenTotal, // message length
- &NumBytesWrittenThisTime, // bytes written this time
- NULL); // not overlapped
+ // Unfortunately this does happen. We should still call
+ // GetOverlappedResult() to get the number of bytes written,
+ // so we can treat it just the same.
+ // BOX_NOTICE("Write claimed success while overlapped?");
+ mWritesInProgress.push_back(new_write);
+ }
+ else
+ {
+ DWORD err = GetLastError();
- if (!Success)
+ if (err == ERROR_IO_PENDING)
{
- // ERROR_NO_DATA is a strange name for
- // "The pipe is being closed".
-
- DWORD err = GetLastError();
-
- if (err != ERROR_NO_DATA)
- {
- BOX_ERROR("Failed to write to control "
- "socket: " << GetErrorMessage(err));
- }
-
+ BOX_TRACE("WriteFile is pending, adding to queue");
+ mWritesInProgress.push_back(new_write);
+ }
+ else
+ {
+ // Not in progress any more, pop it
Close();
-
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketWriteError)
+ THROW_WIN_ERROR_NUMBER("Failed to start overlapped "
+ "write", err, ConnectionException,
+ SocketWriteError);
}
+ }
+
+ // Wait for previous WriteFile operations to complete, one at a time,
+ // until the deadline expires or the pipe becomes disconnected.
+ for(box_time_t remaining = deadline - GetCurrentBoxTime();
+ remaining > 0 && !mWritesInProgress.empty() && mIsConnected;
+ remaining = deadline - GetCurrentBoxTime())
+ {
+ int new_timeout = BoxTimeToMilliSeconds(remaining);
+ WriteInProgress* oldest_write =
+ *(mWritesInProgress.begin());
- NumBytesWrittenTotal += NumBytesWrittenThisTime;
+ int64_t bytes_written = 0;
+ if(WaitForOverlappedOperation(oldest_write->mOverlap,
+ new_timeout, &bytes_written))
+ {
+ // This one is complete, pop it and start a new one
+ delete oldest_write;
+ mWritesInProgress.pop_front();
+ }
}
}
@@ -513,59 +504,47 @@ void WinNamedPipeStream::Close()
THROW_EXCEPTION(ServerException, BadSocketHandle)
}
- if (mIsServer)
+ if (!CancelIo(mSocketHandle))
{
- if (!CancelIo(mSocketHandle))
- {
- BOX_ERROR("Failed to cancel outstanding I/O: " <<
- GetErrorMessage(GetLastError()));
- }
+ BOX_LOG_WIN_ERROR("Failed to cancel outstanding I/O");
+ }
- if (mReadableEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to destroy Readable event: "
- "invalid handle");
- }
- else if (!CloseHandle(mReadableEvent))
- {
- BOX_ERROR("Failed to destroy Readable event: " <<
- GetErrorMessage(GetLastError()));
- }
+ if (mReadableEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_ERROR("Failed to destroy Readable event: "
+ "invalid handle");
+ }
+ else if (!CloseHandle(mReadableEvent))
+ {
+ BOX_LOG_WIN_ERROR("Failed to destroy Readable event");
+ }
- mReadableEvent = INVALID_HANDLE_VALUE;
+ mReadableEvent = INVALID_HANDLE_VALUE;
- if (!FlushFileBuffers(mSocketHandle))
- {
- BOX_ERROR("Failed to FlushFileBuffers: " <<
- GetErrorMessage(GetLastError()));
- }
-
- if (!DisconnectNamedPipe(mSocketHandle))
+ if (mIsConnected && !FlushFileBuffers(mSocketHandle))
+ {
+ BOX_LOG_WIN_ERROR("Failed to FlushFileBuffers");
+ }
+
+ if (mIsServer && mIsConnected && !DisconnectNamedPipe(mSocketHandle))
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_PIPE_NOT_CONNECTED)
{
- DWORD err = GetLastError();
- if (err != ERROR_PIPE_NOT_CONNECTED)
- {
- BOX_ERROR("Failed to DisconnectNamedPipe: " <<
- GetErrorMessage(err));
- }
+ BOX_LOG_WIN_ERROR("Failed to DisconnectNamedPipe");
}
-
- mIsServer = false;
}
- bool result = CloseHandle(mSocketHandle);
+ if (!CloseHandle(mSocketHandle))
+ {
+ THROW_WIN_ERROR_NUMBER("Failed to CloseHandle",
+ GetLastError(), ServerException, SocketCloseError);
+ }
mSocketHandle = INVALID_HANDLE_VALUE;
mIsConnected = false;
mReadClosed = true;
mWriteClosed = true;
-
- if (!result)
- {
- BOX_ERROR("Failed to CloseHandle: " <<
- GetErrorMessage(GetLastError()));
- THROW_EXCEPTION(ServerException, SocketCloseError)
- }
}
// --------------------------------------------------------------------------