emulator: opengl: Support named pipes on Win32

This patch adds support for Win32 named pipes for the communication
channel between the Opengl renderer library and its clients.

Named pipes should be much faster than local TCP sockets on this
platform. Note that by default, TCP sockets are still used. The
emulator needs to call setStreamMode(STREAM_MODE_PIPE) to be able
to use these.

Change-Id: I86d36624cf2b7fdd50f41e1e43c908348dca4657
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
index fb5948b..78f305c 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
@@ -15,7 +15,9 @@
 */
 #include "RenderServer.h"
 #include "TcpStream.h"
-#ifndef _WIN32
+#ifdef _WIN32
+#include "Win32PipeStream.h"
+#else
 #include "UnixStream.h"
 #endif
 #include "RenderThread.h"
@@ -43,9 +45,9 @@
         server->m_listenSock = new TcpStream();
     } else {
 #ifdef _WIN32
-    server->m_listenSock = new TcpStream();
+        server->m_listenSock = new Win32PipeStream();
 #else
-    server->m_listenSock = new UnixStream();
+        server->m_listenSock = new UnixStream();
 #endif
     }
 
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
index 7381b7d..8a5f464 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
@@ -21,7 +21,9 @@
 #include "TimeUtils.h"
 
 #include "TcpStream.h"
-#ifndef _WIN32
+#ifdef _WIN32
+#include "Win32PipeStream.h"
+#else
 #include "UnixStream.h"
 #endif
 
@@ -289,12 +291,10 @@
 
     if (gRendererStreamMode == STREAM_MODE_TCP) {
         stream = new TcpStream(p_stream_buffer_size);
-#ifdef _WIN32
     } else {
-        /* XXX: Need Win32 named pipe stream here */
-        stream = new TcpStream(p_stream_buffer_size);
-#else
-    } else if (gRendererStreamMode == STREAM_MODE_UNIX) {
+#ifdef _WIN32
+        stream = new Win32PipeStream(p_stream_buffer_size);
+#else /* !_WIN32 */
         stream = new UnixStream(p_stream_buffer_size);
 #endif
     }
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp
new file mode 100644
index 0000000..e1a0b9b
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp
@@ -0,0 +1,239 @@
+/*
+* Copyright (C) 2011 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#include "Win32PipeStream.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <windows.h>
+
+#ifndef _WIN32
+#error ONLY BUILD THIS SOURCE FILE FOR WINDOWS!
+#endif
+
+/* The official documentation states that the name of a given named
+ * pipe cannot be more than 256 characters long.
+ */
+#define NAMED_PIPE_MAX 256
+
+Win32PipeStream::Win32PipeStream(size_t bufSize) :
+    SocketStream(bufSize),
+    m_pipe(INVALID_HANDLE_VALUE)
+{
+}
+
+Win32PipeStream::Win32PipeStream(HANDLE pipe, size_t bufSize) :
+    SocketStream(-1, bufSize),
+    m_pipe(pipe)
+{
+}
+
+Win32PipeStream::~Win32PipeStream()
+{
+    if (m_pipe != INVALID_HANDLE_VALUE) {
+        CloseHandle(m_pipe);
+        m_pipe = INVALID_HANDLE_VALUE;
+    }
+}
+
+/* Initialize the pipe name corresponding to a given port
+ */
+static void
+make_pipe_name(char *path, size_t  pathlen, int port_number)
+{
+    snprintf(path, pathlen, "\\\\.\\pipe\\qemu-gles-%d", port_number);
+}
+
+
+/* Technical note: Named pipes work differently from BSD Sockets.
+ * One does not create/bind a pipe, and collect a new handle each
+ * time a client connects with accept().
+ *
+ * Instead, the server creates a new pipe instance each time it wants
+ * to get a new client connection, then calls ConnectNamedPipe() to
+ * wait for a connection.
+ *
+ * So listen() is a no-op, and accept() really creates the pipe handle.
+ *
+ * Also, connect() must create a pipe handle with CreateFile() and
+ * wait for a server instance with WaitNamedPipe()
+ */
+int Win32PipeStream::listen(unsigned short port)
+{
+    // just save the port number for accept()
+    m_port = port;
+    return 0;
+}
+
+SocketStream * Win32PipeStream::accept()
+{
+    char path[NAMED_PIPE_MAX+1];
+    SocketStream*  clientStream;
+    HANDLE pipe;
+
+    make_pipe_name(path, sizeof(path), m_port);
+
+    pipe = ::CreateNamedPipe(
+                path,                // pipe name
+                PIPE_ACCESS_DUPLEX,  // read-write access
+                PIPE_TYPE_BYTE |     // byte-oriented writes
+                PIPE_READMODE_BYTE | // byte-oriented reads
+                PIPE_WAIT,           // blocking operations
+                PIPE_UNLIMITED_INSTANCES, // no limit on clients
+                4096,                // input buffer size
+                4096,                // output buffer size
+                0,                   // client time-out
+                NULL);               // default security attributes
+
+    if (pipe == INVALID_HANDLE_VALUE) {
+        ERR("%s: CreateNamedPipe failed %d\n", __FUNCTION__, (int)GetLastError());
+        return NULL;
+    }
+
+    // Stupid Win32 API design: If a client is already connected, then
+    // ConnectNamedPipe will return 0, and GetLastError() will return
+    // ERROR_PIPE_CONNECTED. This is not an error! It just means that the
+    // function didn't have to wait.
+    //
+    if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) {
+        ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
+        CloseHandle(pipe);
+        return NULL;
+    }
+
+    clientStream = new Win32PipeStream(pipe, m_bufsize);
+    return clientStream;
+}
+
+int Win32PipeStream::connect(unsigned short port)
+{
+    char   path[NAMED_PIPE_MAX+1];
+    HANDLE pipe;
+    int    tries = 10;
+
+    make_pipe_name(path, sizeof(path), port);
+
+    /* We're going to loop in order to wait for the pipe server to
+     * be setup properly.
+     */
+    for (; tries > 0; tries--) {
+        pipe = ::CreateFile(
+                    path,                          // pipe name
+                    GENERIC_READ | GENERIC_WRITE,  // read & write
+                    0,                             // no sharing
+                    NULL,                          // default security attrs
+                    OPEN_EXISTING,                 // open existing pipe
+                    0,                             // default attributes
+                    NULL);                         // no template file
+
+        /* If we have a valid pipe handle, break from the loop */
+        if (pipe != INVALID_HANDLE_VALUE) {
+            break;
+        }
+
+        /* We can get here if the pipe is busy, i.e. if the server hasn't
+         * create a new pipe instance to service our request. In which case
+         * GetLastError() will return ERROR_PIPE_BUSY.
+         *
+         * If so, then use WaitNamedPipe() to wait for a decent time
+         * to try again.
+         */
+        if (GetLastError() != ERROR_PIPE_BUSY) {
+            /* Not ERROR_PIPE_BUSY */
+            ERR("%s: CreateFile failed: %d\n", __FUNCTION__, (int)GetLastError());
+            errno = EINVAL;
+            return -1;
+        }
+
+        /* Wait for 5 seconds */
+        if ( !WaitNamedPipe(path, 5000) ) {
+            ERR("%s: WaitNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
+            errno = EINVAL;
+            return -1;
+        }
+    }
+
+    m_pipe = pipe;
+    return 0;
+}
+
+/* Special buffer methods, since we can't use socket functions here */
+
+int Win32PipeStream::commitBuffer(size_t size)
+{
+    if (m_pipe == INVALID_HANDLE_VALUE)
+        return -1;
+
+    size_t res = size;
+    int retval = 0;
+
+    while (res > 0) {
+        DWORD  written;
+        if (! ::WriteFile(m_pipe, (const char *)m_buf + (size - res), res, &written, NULL)) {
+            retval =  -1;
+            ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError());
+            break;
+        }
+        res -= written;
+    }
+    return retval;
+}
+
+const unsigned char *Win32PipeStream::readFully(void *buf, size_t len)
+{
+    const unsigned char* ret = NULL;
+
+    if (m_pipe == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    if (!buf) {
+        return NULL;  // do not allow NULL buf in that implementation
+    }
+
+    size_t res = len;
+    while (res > 0) {
+        DWORD  readcount = 0;
+        if (! ::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) {
+            errno = (int)GetLastError();
+            return NULL;
+        }
+        res -= readcount;
+    }
+    return (const unsigned char *)buf;
+}
+
+const unsigned char *Win32PipeStream::read( void *buf, size_t *inout_len)
+{
+    size_t len = *inout_len;
+    DWORD  readcount;
+
+    if (m_pipe == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    if (!buf) {
+        return NULL;  // do not allow NULL buf in that implementation
+    }
+
+    if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) {
+        errno = (int)GetLastError();
+        return NULL;
+    }
+
+    *inout_len = (size_t)readcount;
+    return (const unsigned char *)buf;
+}
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h
new file mode 100644
index 0000000..4114545
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2011 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#ifndef __WIN32_PIPE_STREAM_H
+#define __WIN32_PIPE_STREAM_H
+
+#include "SocketStream.h"
+#include <windows.h>
+
+class Win32PipeStream : public SocketStream {
+public:
+    explicit Win32PipeStream(size_t bufsize = 10000);
+    virtual ~Win32PipeStream();
+    virtual int listen(unsigned short port);
+    virtual SocketStream *accept();
+    virtual int connect(unsigned short port);
+
+    virtual int commitBuffer(size_t size);
+    virtual const unsigned char *readFully(void *buf, size_t len);
+    virtual const unsigned char *read(void *buf, size_t *inout_len);
+
+private:
+    Win32PipeStream(HANDLE pipe, size_t bufSize);
+    HANDLE  m_pipe;
+    int     m_port;
+};
+
+
+#endif