Merge changes I1f0e998b,I39adf4aa,I5b8ea4e2,I02928be0,I75b20025, ... into emu-master-dev

* changes:
  emugl: Simplify RenderChannel interface.
  emugl: Introduce BufferQueue class.
  emugl: ChannelBuffer -> RenderChannel::Buffer
  emugl: put ChannelStream/ReadBuffer/RenderThread in emugl namespace.
  emugl: Move IOStream.h to host/include/libOpenglRender/
  emugl: Add emugl/common/debug.h
diff --git a/android-rebuild.sh b/android-rebuild.sh
index 900a2ce..e01fd44 100755
--- a/android-rebuild.sh
+++ b/android-rebuild.sh
@@ -229,6 +229,7 @@
         for UNIT_TEST in android_emu_unittests emugl_common_host_unittests \
                          emulator_libui_unittests \
                          emulator_crashreport_unittests \
+                         libOpenglRender_unittests \
                          libGLcommon_unittests; do
             for TEST in $OUT_DIR/$UNIT_TEST$EXE_SUFFIX; do
                 echo "   - ${TEST#$OUT_DIR/}"
@@ -242,6 +243,7 @@
         for UNIT_TEST in android_emu64_unittests emugl64_common_host_unittests \
                          emulator64_libui_unittests \
                          emulator64_crashreport_unittests \
+                         lib64OpenglRender_unittests \
                          lib64GLcommon_unittests; do
              for TEST in $OUT_DIR/$UNIT_TEST$EXE_SUFFIX; do
                  echo "   - ${TEST#$OUT_DIR/}"
diff --git a/android/opengl/OpenglEsPipe.cpp b/android/opengl/OpenglEsPipe.cpp
index 35aafff..9e0491c 100644
--- a/android/opengl/OpenglEsPipe.cpp
+++ b/android/opengl/OpenglEsPipe.cpp
@@ -22,7 +22,7 @@
 #include <string.h>
 
 // Set to 1 or 2 for debug traces
-//#define DEBUG 1
+#define DEBUG 0
 
 #if DEBUG >= 1
 #define D(...) printf(__VA_ARGS__), printf("\n"), fflush(stdout)
@@ -36,10 +36,11 @@
 #define DD(...) ((void)0)
 #endif
 
-using emugl::ChannelBuffer;
+using ChannelBuffer = emugl::RenderChannel::Buffer;
 using emugl::RenderChannel;
 using emugl::RenderChannelPtr;
 using ChannelState = emugl::RenderChannel::State;
+using IoResult = emugl::RenderChannel::IoResult;
 
 namespace android {
 namespace opengl {
@@ -79,8 +80,7 @@
     /////////////////////////////////////////////////////////////////////////
     // Constructor, check that |mIsWorking| is true after this call to verify
     // that everything went well.
-    EmuglPipe(void* hwPipe,
-              Service* service,
+    EmuglPipe(void* hwPipe, Service* service,
               const emugl::RendererPtr& renderer)
         : AndroidPipe(hwPipe, service) {
         mChannel = renderer->createRenderChannel();
@@ -90,10 +90,10 @@
         }
 
         mIsWorking = true;
-        mChannel->setEventCallback([this](ChannelState state,
-                                          RenderChannel::EventSource source) {
-            this->onChannelIoEvent(state);
-        });
+        mChannel->setEventCallback(
+                [this](RenderChannel::State events) {
+                    this->onChannelHostEvent(events);
+                });
     }
 
     //////////////////////////////////////////////////////////////////////////
@@ -103,20 +103,27 @@
         D("%s", __func__);
         mIsWorking = false;
         mChannel->stop();
-        // stop callback will call close() which deletes the pipe
+        delete this;
     }
 
     virtual unsigned onGuestPoll() const override {
         DD("%s", __func__);
 
         unsigned ret = 0;
-        ChannelState state = mChannel->currentState();
-        if (canReadAny(state)) {
+        if (mDataForReadingLeft > 0) {
             ret |= PIPE_POLL_IN;
         }
-        if (canWrite(state)) {
+        ChannelState state = mChannel->state();
+        if ((state & ChannelState::CanRead) != 0) {
+            ret |= PIPE_POLL_IN;
+        }
+        if ((state & ChannelState::CanWrite) != 0) {
             ret |= PIPE_POLL_OUT;
         }
+        if ((state & ChannelState::Stopped) != 0) {
+            ret |= PIPE_POLL_HUP;
+        }
+        DD("%s: returning %d", __func__, ret);
         return ret;
     }
 
@@ -124,8 +131,8 @@
             override {
         DD("%s", __func__);
 
-        // Consume the pipe's dataForReading, then put the next received data piece
-        // there. Repeat until the buffers are full or we're out of data
+        // Consume the pipe's dataForReading, then put the next received data
+        // piece there. Repeat until the buffers are full or we're out of data
         // in the channel.
         int len = 0;
         size_t buffOffset = 0;
@@ -135,29 +142,37 @@
         while (buff != buffEnd) {
             if (mDataForReadingLeft == 0) {
                 // No data left, read a new chunk from the channel.
-                //
-                // Spin a little bit here: many GL calls are much faster than
-                // the whole host-to-guest-to-host transition.
                 int spinCount = 20;
-                while (!mChannel->read(&mDataForReading,
-                                    RenderChannel::CallType::Nonblocking)) {
-                    if (mChannel->isStopped()) {
-                        return PIPE_ERROR_IO;
-                    } else if (len == 0) {
-                        if (canRead(mChannel->currentState()) || --spinCount > 0) {
-                            continue;
-                        }
-                        return PIPE_ERROR_AGAIN;
-                    } else {
+                for (;;) {
+                    auto result = mChannel->tryRead(&mDataForReading);
+                    if (result == IoResult::Ok) {
+                        mDataForReadingLeft = mDataForReading.size();
+                        break;
+                    }
+                    DD("%s: tryRead() failed with %d", __func__, (int)result);
+                    // This failed either because the channel was stopped
+                    // from the host, or if there was no data yet in the
+                    // channel.
+                    if (len > 0) {
+                        DD("%s: returning %d bytes", __func__, (int)len);
                         return len;
                     }
+                    if (result == IoResult::Error) {
+                        return PIPE_ERROR_IO;
+                    }
+                    // Spin a little before declaring there is nothing
+                    // to read. Many GL calls are much faster than the
+                    // whole host-to-guest-to-host transition.
+                    if (--spinCount > 0) {
+                        continue;
+                    }
+                    DD("%s: returning PIPE_ERROR_AGAIN", __func__);
+                    return PIPE_ERROR_AGAIN;
                 }
-
-                mDataForReadingLeft = mDataForReading.size();
             }
 
             const size_t curSize =
-                    std::min(buff->size - buffOffset, mDataForReadingLeft.load());
+                    std::min(buff->size - buffOffset, mDataForReadingLeft);
             memcpy(buff->data + buffOffset,
                 mDataForReading.data() +
                         (mDataForReading.size() - mDataForReadingLeft),
@@ -172,6 +187,7 @@
             }
         }
 
+        DD("%s: received %d bytes", __func__, (int)len);
         return len;
     }
 
@@ -179,8 +195,9 @@
                             int numBuffers) override {
         DD("%s", __func__);
 
-        if (int ret = sendReadyStatus()) {
-            return ret;
+        if (!mIsWorking) {
+            DD("%s: pipe already closed!", __func__);
+            return PIPE_ERROR_IO;
         }
 
         // Count the total bytes to send.
@@ -198,9 +215,12 @@
             ptr += buffers[n].size;
         }
 
+        D("%s: sending %d bytes to host", __func__, count);
         // Send it through the channel.
-        if (!mChannel->write(std::move(outBuffer))) {
-            return PIPE_ERROR_IO;
+        auto result = mChannel->tryWrite(std::move(outBuffer));
+        if (result != IoResult::Ok) {
+            D("%s: tryWrite() failed with %d", __func__, (int)result);
+            return result == IoResult::Error ? PIPE_ERROR_IO : PIPE_ERROR_AGAIN;
         }
 
         return count;
@@ -209,112 +229,66 @@
     virtual void onGuestWantWakeOn(int flags) override {
         DD("%s: flags=%d", __func__, flags);
 
-        // We reset these flags when we actually wake the pipe, so only
-        // add to them here.
-        mCareAboutRead |= (flags & PIPE_WAKE_READ) != 0;
-        mCareAboutWrite |= (flags & PIPE_WAKE_WRITE) != 0;
+        // Translate |flags| into ChannelState flags.
+        ChannelState wanted = ChannelState::Empty;
+        if (flags & PIPE_WAKE_READ) {
+            wanted |= ChannelState::CanRead;
+        }
+        if (flags & PIPE_WAKE_WRITE) {
+            wanted |= ChannelState::CanWrite;
+        }
 
-        ChannelState state = mChannel->currentState();
-        processIoEvents(state);
+        // Signal events that are already available now.
+        ChannelState state = mChannel->state();
+        ChannelState available = state & wanted;
+        DD("%s: state=%d wanted=%d available=%d", __func__, (int)state,
+           (int)wanted, (int)available);
+        if (available != ChannelState::Empty) {
+            DD("%s: signaling events %d", __func__, (int)available);
+            signalState(available);
+            wanted &= ~available;
+        }
+
+        // Ask the channel to be notified of remaining events.
+        if (wanted != ChannelState::Empty) {
+            DD("%s: waiting for events %d", __func__, (int)wanted);
+            mChannel->setWantedEvents(wanted);
+        }
     }
 
 private:
-    // Returns true iff there is data to read from the emugl channel.
-    static bool canRead(ChannelState state) {
-        return (state & ChannelState::CanRead) != ChannelState::Empty;
-    }
-
-    // Returns true iff the emugl channel can accept data.
-    static bool canWrite(ChannelState state) {
-        return (state & ChannelState::CanWrite) != ChannelState::Empty;
-    }
-
-    // Returns true iff there is data to read from the local cache or from
-    // the emugl channel.
-    bool canReadAny(ChannelState state) const {
-        return mDataForReadingLeft != 0 || canRead(state);
-    }
-
-    // Check that the pipe is working and that the render channel can be
-    // written to. Return 0 in case of success, and one PIPE_ERROR_XXX
-    // value on error.
-    int sendReadyStatus() const {
-        if (mIsWorking) {
-            ChannelState state = mChannel->currentState();
-            if (canWrite(state)) {
-                return 0;
-            }
-            return PIPE_ERROR_AGAIN;
-        } else if (!mHwPipe) {
-            return PIPE_ERROR_IO;
-        } else {
-            return PIPE_ERROR_INVAL;
-        }
-    }
-
-    // Check the read/write state of the render channel and signal the pipe
-    // if any condition meets mCareAboutRead or mCareAboutWrite.
-    void processIoEvents(ChannelState state) {
+    // Called to signal the guest that read/write wake events occured.
+    // Note: this can be called from either the guest or host render
+    // thread.
+    void signalState(ChannelState state) {
         int wakeFlags = 0;
-
-        if (mCareAboutRead && canReadAny(state)) {
+        if ((state & ChannelState::CanRead) != 0) {
             wakeFlags |= PIPE_WAKE_READ;
-            mCareAboutRead = false;
         }
-        if (mCareAboutWrite && canWrite(state)) {
+        if ((state & ChannelState::CanWrite) != 0) {
             wakeFlags |= PIPE_WAKE_WRITE;
-            mCareAboutWrite = false;
         }
-
-        // Send wake signal to the guest if needed.
         if (wakeFlags != 0) {
-            signalWake(wakeFlags);
+            this->signalWake(wakeFlags);
         }
     }
 
     // Called when an i/o event occurs on the render channel
-    void onChannelIoEvent(ChannelState state) {
-        D("%s: %d", __func__, (int)state);
-
-        if ((state & ChannelState::Stopped) != ChannelState::Empty) {
-            close();
-        } else {
-            processIoEvents(state);
-        }
-    }
-
-    // Close the pipe, this may be called from the host or guest side.
-    void close() {
-        D("%s", __func__);
-
-        // If the pipe isn't in a working state, delete immediately.
-        if (!mIsWorking) {
-            mChannel->stop();
-            delete this;
+    void onChannelHostEvent(ChannelState state) {
+        D("%s: events %d", __func__, (int)state);
+        // NOTE: This is called from the host-side render thread.
+        // but closeFromHost() and signalWake() can be called from
+        // any thread.
+        if ((state & ChannelState::Stopped) != 0) {
+            this->closeFromHost();
             return;
         }
-
-        // Force the closure of the channel - if a guest is blocked
-        // waiting for a wake signal, it will receive an error.
-        if (mHwPipe) {
-            closeFromHost();
-            mHwPipe = nullptr;
-        }
-
-        mIsWorking = false;
+        signalState(state);
     }
 
     // A RenderChannel pointer used for communication.
     RenderChannelPtr mChannel;
 
-    // Guest state tracking - if it requested us to wake on read/write
-    // availability. If guest doesn't care about some operation type, we should
-    // not wake it when that operation becomes available.
-    // Note: we need an atomic or operation, and atomic<bool> doesn't have it -
-    //  that's why it is atomic<char> here.
-    std::atomic<char> mCareAboutRead {false};
-    std::atomic<char> mCareAboutWrite {false};
-
     // Set to |true| if the pipe is in working state, |false| means we're not
     // initialized or the pipe is closed.
     bool mIsWorking = false;
@@ -326,7 +300,7 @@
     // If guest didn't have enough room for the whole buffer, we track the
     // number of remaining bytes in |mDataForReadingLeft| for the next read().
     ChannelBuffer mDataForReading;
-    std::atomic<size_t> mDataForReadingLeft { 0 };
+    size_t mDataForReadingLeft = 0;
 
     DISALLOW_COPY_ASSIGN_AND_MOVE(EmuglPipe);
 };
diff --git a/distrib/android-emugl/DESIGN b/distrib/android-emugl/DESIGN
index 943a0e4..ceb8331 100644
--- a/distrib/android-emugl/DESIGN
+++ b/distrib/android-emugl/DESIGN
@@ -301,7 +301,7 @@
 
 The "IOStream" is a very simple abstract class used to send byte messages
 both in the guest and host. It is defined through a shared header under
-$EMUGL/host/include/libOpenglRender/IOStream.h
+$EMUGL/host/include/OpenglRender/IOStream.h
 
 Note that despite the path, this header is included by *both* host and guest
 source code. The main idea around IOStream's design is that to send a message,
diff --git a/distrib/android-emugl/host/include/libOpenglRender/IOStream.h b/distrib/android-emugl/host/include/OpenglRender/IOStream.h
similarity index 94%
rename from distrib/android-emugl/host/include/libOpenglRender/IOStream.h
rename to distrib/android-emugl/host/include/OpenglRender/IOStream.h
index a0df579..46b8c6a 100644
--- a/distrib/android-emugl/host/include/libOpenglRender/IOStream.h
+++ b/distrib/android-emugl/host/include/OpenglRender/IOStream.h
@@ -33,7 +33,7 @@
 
 public:
     size_t read(void* buf, size_t bufLen) {
-        if (!read(buf, &bufLen)) {
+        if (!readRaw(buf, &bufLen)) {
             return 0;
         }
         return bufLen;
@@ -75,7 +75,7 @@
 protected:
     virtual void *allocBuffer(size_t minSize) = 0;
     virtual int commitBuffer(size_t size) = 0;
-    virtual const unsigned char *read(void *buf, size_t *inout_len) = 0;
+    virtual const unsigned char *readRaw(void *buf, size_t *inout_len) = 0;
 
 private:
     unsigned char* m_buf = nullptr;
diff --git a/distrib/android-emugl/host/include/OpenglRender/RenderChannel.h b/distrib/android-emugl/host/include/OpenglRender/RenderChannel.h
index 6c139c3..40232e2 100644
--- a/distrib/android-emugl/host/include/OpenglRender/RenderChannel.h
+++ b/distrib/android-emugl/host/include/OpenglRender/RenderChannel.h
@@ -21,18 +21,35 @@
 
 namespace emugl {
 
-// Turn the RenderChannel::Event enum into flags.
+// Turn the RenderChannel::State enum into flags.
 using namespace ::android::base::EnumFlags;
 
-// A type used for data passing.
-using ChannelBuffer = android::base::SmallFixedVector<char, 512>;
-
-// RenderChannel - an interface for a single guest to host renderer connection.
-// It allows the guest to send GPU emulation protocol-serialized messages to an
-// asynchronous renderer, read the responses and subscribe for state updates.
+// RenderChannel - For each guest-to-host renderer connection, this provides
+// an interface for the guest side to interact with the corresponding renderer
+// thread on the host. Its main purpose is to send and receive wire protocol
+// bytes in an asynchronous way (compatible with Android pipes).
+//
+// Usage is the following:
+//    1) Get an instance pointer through a dedicated Renderer function
+//       (e.g. RendererImpl::createRenderChannel()).
+//
+//    2) Call setEventCallback() to indicate which callback should be called
+//       when the channel's state has changed due to a host thread event.
+//
 class RenderChannel {
 public:
-    // Flags for the channel state.
+    // A type used to pass byte packets between the guest and the
+    // RenderChannel instance. Experience has shown that using a
+    // SmallFixedVector<char, N> instance instead of a std::vector<char>
+    // avoids a lot of un-necessary heap allocations. The current size
+    // of 512 was selected after profiling existing traffic, including
+    // the one used in protocol-heavy benchmark like Antutu3D.
+    using Buffer = android::base::SmallFixedVector<char, 512>;
+
+    // Bit-flags for the channel state.
+    // |CanRead| means there is data from the host to read.
+    // |CanWrite| means there is room to send data to the host.
+    // |Stopped| means the channel was stopped.
     enum class State {
         // Can't use None here, some system header declares it as a macro.
         Empty = 0,
@@ -41,47 +58,60 @@
         Stopped = 1 << 2,
     };
 
-    // Possible points of origin for an event in EventCallback.
-    enum class EventSource {
-        RenderChannel,
-        Client,
+    // Values corresponding to the result of i/o operations.
+    // |Ok| means everything went well.
+    // |TryAgain| means the operation could not be performed and should be
+    // tried later.
+    // |Error| means an error happened (i.e. the channel is stopped).
+    enum class IoResult {
+        Ok = 0,
+        TryAgain = 1,
+        Error = 2,
     };
 
-    // Types of read() the channel supports.
-    enum class CallType {
-        Blocking,    // if the call can't do what it needs, block until it can
-        Nonblocking, // immidiately return if the call can't do the job
-    };
+    // Type of a callback used to tell the guest when the RenderChannel
+    // state changes. Used by setEventCallback(). The parameter contains
+    // the State bits matching the event, i.e. it is the logical AND of
+    // the last value passed to setWantedEvents() and the current
+    // RenderChannel state.
+    using EventCallback = std::function<void(State)>;
 
-    // Sets a single (!) callback that is called if some event happends that
-    // changes the channel state - e.g. when it's stopped, or it gets some data
-    // the client can read after being empty, or it isn't full anymore and the
-    // client may write again without blocking.
-    // If the state isn't State::Empty, the |callback| is called for the first
-    // time during the setEventCallback() to report this initial state.
-    using EventCallback = std::function<void(State, EventSource)>;
-    virtual void setEventCallback(EventCallback callback) = 0;
+    // Sets a single (!) callback that is called when the channel state's
+    // changes due to an event *from* *the* *host* only. |callback| is a
+    // guest-provided callback that will be called from the host renderer
+    // thread, not the guest one.
+    virtual void setEventCallback(EventCallback&& callback) = 0;
 
-    // Writes the data in |buffer| into the channel. |buffer| is moved from.
-    // Blocks if there's no room in the channel (shouldn't really happen).
-    // Returns false if the channel is stopped.
-    virtual bool write(ChannelBuffer&& buffer) = 0;
-    // Reads a chunk of data from the channel. Returns false if there was no
-    // data for a non-blocking call or if the channel is stopped.
-    virtual bool read(ChannelBuffer* buffer, CallType callType) = 0;
+    // Used to indicate which i/o events the guest wants to be notified
+    // through its StateChangeCallback. |state| must be a combination of
+    // State::CanRead or State::CanWrite only. This will *not* call the
+    // callback directly since this happens in the guest thread.
+    virtual void setWantedEvents(State state) = 0;
 
     // Get the current state flags.
-    virtual State currentState() const = 0;
+    virtual State state() const = 0;
+
+    // Try to writes the data in |buffer| into the channel. On success,
+    // return IoResult::Ok and moves |buffer|. On failure, return
+    // IoResult::TryAgain if the channel was full, or IoResult::Error
+    // if it is stopped.
+    virtual IoResult tryWrite(Buffer&& buffer) = 0;
+
+    // Try to read data from the channel. On success, return IoResult::Ok and
+    // sets |*buffer| to contain the data. On failure, return
+    // IoResult::TryAgain if the channel was empty, or IoResult::Error if
+    // it was stopped.
+    virtual IoResult tryRead(Buffer* buffer) = 0;
 
     // Abort all pending operations. Any following operation is a noop.
+    // Once a channel is stopped, it cannot be re-started.
     virtual void stop() = 0;
-    // Check if the channel is stopped.
-    virtual bool isStopped() const = 0;
 
 protected:
     ~RenderChannel() = default;
 };
 
+// Shared pointer to RenderChannel instance.
 using RenderChannelPtr = std::shared_ptr<RenderChannel>;
 
 }  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/Android.mk b/distrib/android-emugl/host/libs/libOpenglRender/Android.mk
index f165400..ca1a857 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/Android.mk
+++ b/distrib/android-emugl/host/libs/libOpenglRender/Android.mk
@@ -63,3 +63,13 @@
 $(call emugl-export,CFLAGS,$(EMUGL_USER_CFLAGS))
 
 $(call emugl-end-module)
+
+### OpenglRender unittests
+$(call emugl-begin-executable,lib$(BUILD_TARGET_SUFFIX)OpenglRender_unittests)
+
+LOCAL_SRC_FILES := \
+    BufferQueue_unittest.cpp \
+
+$(call emugl-import,lib$(BUILD_TARGET_SUFFIX)OpenglRender libemugl_gtest)
+$(call local-link-static-c++lib)
+$(call emugl-end-module)
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue.h b/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue.h
new file mode 100644
index 0000000..3a00a4b
--- /dev/null
+++ b/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue.h
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 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.
+#pragma once
+
+#include "OpenglRender/RenderChannel.h"
+#include "android/base/Compiler.h"
+#include "android/base/synchronization/Lock.h"
+#include "android/base/synchronization/MessageChannel.h"
+
+#include <memory>
+
+#include <stddef.h>
+
+namespace emugl {
+
+// BufferQueue models a FIFO queue of RenderChannel::Buffer instances
+// that can be used between two different threads. Note that it depends,
+// for synchronization, on an external lock (passed as a reference in
+// the BufferQueue constructor).
+//
+// This allows one to use multiple BufferQueue instances whose content
+// are protected by a single lock.
+class BufferQueue {
+    using ConditionVariable = android::base::ConditionVariable;
+    using Lock = android::base::Lock;
+    using AutoLock = android::base::AutoLock;
+
+public:
+    using IoResult = RenderChannel::IoResult;
+    using Buffer = RenderChannel::Buffer;
+
+    // Constructor. |capacity| is the maximum number of Buffer instances in
+    // the queue, and |lock| is a reference to an external lock provided by
+    // the caller.
+    BufferQueue(size_t capacity, android::base::Lock& lock)
+        : mCapacity(capacity), mBuffers(new Buffer[capacity]), mLock(lock) {}
+
+    // Return true iff the queue has been closed. Remaining items can
+    // be read from its, but it won't be possible to add new items to it.
+    // bool closed() const { return mClosed; }
+
+    // Return true iff one can send a buffer to the queue, i.e. if it
+    // is not full.
+    bool canPushLocked() const { return !mClosed && (mCount < mCapacity); }
+
+    // Return true iff one can receive a buffer from the queue, i.e. if
+    // it is not empty.
+    bool canPopLocked() const { return mCount > 0U; }
+
+    // Return true iff the queue is closed.
+    bool isClosedLocked() const { return mClosed; }
+
+    // Try to send a buffer to the queue. On success, return IoResult::Ok
+    // and moves |buffer| to the queue. On failure, return
+    // IoResult::TryAgain if the queue was full, or IoResult::Error
+    // if it was closed.
+    IoResult tryPushLocked(Buffer&& buffer) {
+        if (mClosed) {
+            return IoResult::Error;
+        }
+        if (mCount >= mCapacity) {
+            return IoResult::TryAgain;
+        }
+        size_t pos = mPos + mCount;
+        if (pos >= mCapacity) {
+            pos -= mCapacity;
+        }
+        mBuffers[pos] = std::move(buffer);
+        if (mCount++ == 0) {
+            mCanPop.signal();
+        }
+        return IoResult::Ok;
+    }
+
+    // Push a buffer to the queue. This is a blocking call. On success,
+    // move |buffer| into the queue and return IoResult::Ok. On failure,
+    // return IoResult::Error meaning the queue was closed.
+    IoResult pushLocked(Buffer&& buffer) {
+        while (mCount == mCapacity) {
+            if (mClosed) {
+                return IoResult::Error;
+            }
+            mCanPush.wait(&mLock);
+        }
+        return tryPushLocked(std::move(buffer));
+    }
+
+    // Try to read a buffer from the queue. On success, moves item into
+    // |*buffer| and return IoResult::Ok. On failure, return IoResult::Error
+    // if the queue is empty and closed, and IoResult::TryAgain if it is
+    // empty but not close.
+    IoResult tryPopLocked(Buffer* buffer) {
+        if (mCount == 0) {
+            return mClosed ? IoResult::Error : IoResult::TryAgain;
+        }
+        *buffer = std::move(mBuffers[mPos]);
+        size_t pos = mPos + 1;
+        if (pos >= mCapacity) {
+            pos -= mCapacity;
+        }
+        mPos = pos;
+        if (mCount-- == mCapacity) {
+            mCanPush.signal();
+        }
+        return IoResult::Ok;
+    }
+
+    // Pop a buffer from the queue. This is a blocking call. On success,
+    // move item into |*buffer| and return IoResult::Ok. On failure,
+    // return IoResult::Error to indicate the queue was closed.
+    IoResult popLocked(Buffer* buffer) {
+        while (mCount == 0) {
+            if (mClosed) {
+                // Closed queue is empty.
+                return IoResult::Error;
+            }
+            mCanPop.wait(&mLock);
+        }
+        return tryPopLocked(buffer);
+    }
+
+    // Close the queue, it is no longer possible to push new items
+    // to it (i.e. push() will always return IoResult::Error), or to
+    // read from an empty queue (i.e. pop() will always return
+    // IoResult::Error once the queue becomes empty).
+    void closeLocked() {
+        mClosed = true;
+
+        // Wake any potential waiters.
+        if (mCount == mCapacity) {
+            mCanPush.broadcast();
+        }
+        if (mCount == 0) {
+            mCanPop.broadcast();
+        }
+    }
+
+private:
+    size_t mCapacity = 0;
+    size_t mPos = 0;
+    size_t mCount = 0;
+    bool mClosed = false;
+    std::unique_ptr<Buffer[]> mBuffers;
+
+    Lock& mLock;
+    ConditionVariable mCanPush;
+    ConditionVariable mCanPop;
+
+    // This will force the same for SyncBufferQueue and RenderChannelImpl
+    DISALLOW_COPY_ASSIGN_AND_MOVE(BufferQueue);
+};
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue_unittest.cpp b/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue_unittest.cpp
new file mode 100644
index 0000000..778a766
--- /dev/null
+++ b/distrib/android-emugl/host/libs/libOpenglRender/BufferQueue_unittest.cpp
@@ -0,0 +1,328 @@
+// Copyright (C) 2016 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 "BufferQueue.h"
+
+#ifdef _WIN32
+#undef ERROR   // Make LOG(ERROR) compile properly.
+#endif
+
+#include "android/base/Log.h"
+#include "android/base/synchronization/Lock.h"
+#include "android/base/synchronization/MessageChannel.h"
+#include "android/base/threads/Thread.h"
+#include "OpenglRender/RenderChannel.h"
+
+#include <gtest/gtest.h>
+
+namespace emugl {
+
+using android::base::Lock;
+using android::base::AutoLock;
+using Buffer = BufferQueue::Buffer;
+using IoResult = BufferQueue::IoResult;
+
+TEST(BufferQueue, Constructor) {
+    Lock lock;
+    BufferQueue queue(16, lock);
+}
+
+TEST(BufferQueue, tryPushLocked) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    AutoLock al(lock);
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("Hello")));
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("World")));
+
+    Buffer buff0("You Shall Not Move");
+    EXPECT_EQ(IoResult::TryAgain, queue.tryPushLocked(std::move(buff0)));
+    EXPECT_FALSE(buff0.empty()) << "Buffer should not be moved on failure!";
+}
+
+TEST(BufferQueue, tryPushLockedOnClosedQueue) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    AutoLock al(lock);
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("Hello")));
+
+    // Closing the queue prevents pushing new items to the queue.
+    queue.closeLocked();
+
+    EXPECT_EQ(IoResult::Error, queue.tryPushLocked(Buffer("World")));
+}
+
+TEST(BufferQueue, tryPopLocked) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    AutoLock al(lock);
+
+    Buffer buffer;
+    EXPECT_EQ(IoResult::TryAgain, queue.tryPopLocked(&buffer));
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("Hello")));
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("World")));
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("Hello", buffer.data());
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("World", buffer.data());
+
+    EXPECT_EQ(IoResult::TryAgain, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("World", buffer.data());
+}
+
+TEST(BufferQueue, tryPopLockedOnClosedQueue) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    AutoLock al(lock);
+
+    Buffer buffer;
+    EXPECT_EQ(IoResult::TryAgain, queue.tryPopLocked(&buffer));
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("Hello")));
+    EXPECT_EQ(IoResult::Ok, queue.tryPushLocked(Buffer("World")));
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("Hello", buffer.data());
+
+    // Closing the queue doesn't prevent popping existing items, but
+    // will generate IoResult::Error once it is empty.
+    queue.closeLocked();
+
+    EXPECT_EQ(IoResult::Ok, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("World", buffer.data());
+
+    EXPECT_EQ(IoResult::Error, queue.tryPopLocked(&buffer));
+    EXPECT_STREQ("World", buffer.data());
+}
+
+namespace {
+
+// A TestThread instance that holds a reference to a queue and can either
+// push or pull to it, on command from another thread. This uses a
+// MessageChannel to implement the communication channel between the
+// command thread and this one.
+class TestThread final : public android::base::Thread {
+public:
+    TestThread(Lock& lock, BufferQueue& queue)
+        // NOTE: The default stack size of android::base::Thread is too
+        //       small and will result in runtime errors.
+        : mLock(lock), mQueue(queue) {}
+
+    // Tell the test thread to push |buffer| to the queue.
+    // Call endPush() later to get the command's result.
+    bool startPush(Buffer&& buffer) {
+        return mInput.send(
+                Request { .cmd = Cmd::Push, .buffer = std::move(buffer) });
+    }
+
+    // Get the result of a previous startPush() command.
+    IoResult endPush() {
+        Reply reply = {};
+        if (!mOutput.receive(&reply)) {
+            return IoResult::Error;
+        }
+        return reply.result;
+    }
+
+    // Tell the test thread to pop a buffer from the queue.
+    // Call endPop() to get the command's result, as well as the popped
+    // buffer if it is IoResult::Ok.
+    bool startPop() {
+        return mInput.send(Request { .cmd = Cmd::Pop });
+    }
+
+    // Return the result of a previous startPop() command. If result is
+    // IoResult::Ok, sets |*buffer| to the result buffer.
+    IoResult endPop(Buffer* buffer) {
+        Reply reply = {};
+        if (!mOutput.receive(&reply)) {
+            return IoResult::Error;
+        }
+        if (reply.result == IoResult::Ok) {
+            *buffer = std::move(reply.buffer);
+        }
+        return reply.result;
+    }
+
+    // Tell the test thread to close the queue from its side.
+    void doClose() {
+        mInput.send(Request { .cmd = Cmd::Close });
+    }
+
+    // Tell the test thread to stop after completing its current command.
+    void stop() {
+        mInput.send(Request { .cmd = Cmd::Stop });
+        ASSERT_TRUE(this->wait());
+    }
+
+private:
+    enum class Cmd {
+        Push,
+        Pop,
+        Close,
+        Stop,
+    };
+
+    struct Request {
+        Cmd cmd;
+        Buffer buffer;
+    };
+
+    struct Reply {
+        IoResult result;
+        Buffer buffer;
+    };
+
+    // Main thread function.
+    virtual intptr_t main() override final {
+        for (;;) {
+            Request r;
+            if (!mInput.receive(&r)) {
+                LOG(ERROR) << "Could not receive command";
+                break;
+            }
+            if (r.cmd == Cmd::Stop) {
+                break;
+            }
+            mLock.lock();
+            Reply reply = {};
+            bool sendReply = false;
+            switch (r.cmd) {
+                case Cmd::Push:
+                    reply.result = mQueue.pushLocked(std::move(r.buffer));
+                    sendReply = true;
+                    break;
+
+                case Cmd::Pop:
+                    reply.result = mQueue.popLocked(&reply.buffer);
+                    sendReply = true;
+                    break;
+
+                case Cmd::Close:
+                    mQueue.closeLocked();
+                    break;
+
+                default:
+                    ;
+            }
+            mLock.unlock();
+            if (sendReply) {
+                if (!mOutput.send(std::move(reply))) {
+                    LOG(ERROR) << "Could not send reply";
+                    break;
+                }
+            }
+        }
+        return 0U;
+    }
+
+    Lock& mLock;
+    BufferQueue& mQueue;
+    android::base::MessageChannel<Request, 4> mInput;
+    android::base::MessageChannel<Reply, 4> mOutput;
+};
+
+}  // namespace
+
+TEST(BufferQueue, pushLocked) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    TestThread thread(lock, queue);
+
+    ASSERT_TRUE(thread.start());
+    ASSERT_TRUE(thread.startPop());
+
+    lock.lock();
+    EXPECT_EQ(IoResult::Ok, queue.pushLocked(Buffer("Hello")));
+    EXPECT_EQ(IoResult::Ok, queue.pushLocked(Buffer("World")));
+    EXPECT_EQ(IoResult::Ok, queue.pushLocked(Buffer("Foo")));
+    lock.unlock();
+
+    thread.stop();
+}
+
+TEST(BufferQueue, pushLockedWithClosedQueue) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    TestThread thread(lock, queue);
+
+    ASSERT_TRUE(thread.start());
+
+    lock.lock();
+    EXPECT_EQ(IoResult::Ok, queue.pushLocked(Buffer("Hello")));
+    // Closing the queue prevents pushing new items, but not
+    // pulling from the queue.
+    queue.closeLocked();
+    EXPECT_EQ(IoResult::Error, queue.pushLocked(Buffer("World")));
+    lock.unlock();
+
+    Buffer buffer;
+    ASSERT_TRUE(thread.startPop());
+    EXPECT_EQ(IoResult::Ok, thread.endPop(&buffer));
+    EXPECT_STREQ("Hello", buffer.data());
+
+    thread.stop();
+}
+
+TEST(BufferQueue, popLocked) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    TestThread thread(lock, queue);
+
+    ASSERT_TRUE(thread.start());
+    ASSERT_TRUE(thread.startPush(Buffer("Hello World")));
+    EXPECT_EQ(IoResult::Ok, thread.endPush());
+
+    lock.lock();
+    Buffer buffer;
+    EXPECT_EQ(IoResult::Ok, queue.popLocked(&buffer));
+    EXPECT_STREQ("Hello World", buffer.data());
+    lock.unlock();
+
+    thread.stop();
+}
+
+TEST(BufferQueue, popLockedWithClosedQueue) {
+    Lock lock;
+    BufferQueue queue(2, lock);
+    TestThread thread(lock, queue);
+
+    ASSERT_TRUE(thread.start());
+    ASSERT_TRUE(thread.startPush(Buffer("Hello World")));
+    EXPECT_EQ(IoResult::Ok, thread.endPush());
+
+    // Closing the queue shall not prevent pulling items from it.
+    // After that, IoResult::Error shall be returned.
+    thread.doClose();
+
+    ASSERT_TRUE(thread.startPush(Buffer("Foo Bar")));
+    EXPECT_EQ(IoResult::Error, thread.endPush());
+
+    lock.lock();
+    Buffer buffer;
+    EXPECT_EQ(IoResult::Ok, queue.popLocked(&buffer));
+    EXPECT_STREQ("Hello World", buffer.data());
+
+    EXPECT_EQ(IoResult::Error, queue.popLocked(&buffer));
+    EXPECT_STREQ("Hello World", buffer.data());
+    lock.unlock();
+
+    thread.stop();
+}
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.cpp b/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.cpp
index db0589b..0f402b9 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.cpp
+++ b/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.cpp
@@ -13,42 +13,81 @@
 // limitations under the License.
 #include "ChannelStream.h"
 
-#include <assert.h>
+#include "OpenglRender/RenderChannel.h"
 
-ChannelStream::ChannelStream(std::shared_ptr<emugl::RenderChannelImpl> channel,
+#define EMUGL_DEBUG_LEVEL  0
+#include "emugl/common/debug.h"
+
+#include <assert.h>
+#include <memory.h>
+
+namespace emugl {
+
+using IoResult = RenderChannel::IoResult;
+
+ChannelStream::ChannelStream(std::shared_ptr<RenderChannelImpl> channel,
                              size_t bufSize)
     : IOStream(bufSize), mChannel(channel) {
-    mBuf.resize_noinit(bufSize);
+    mWriteBuffer.resize_noinit(bufSize);
 }
 
 void* ChannelStream::allocBuffer(size_t minSize) {
-    if (mBuf.size() < minSize) {
-        mBuf.resize_noinit(minSize);
+    if (mWriteBuffer.size() < minSize) {
+        mWriteBuffer.resize_noinit(minSize);
     }
-    return mBuf.data();
+    return mWriteBuffer.data();
 }
 
 int ChannelStream::commitBuffer(size_t size) {
-    assert(size <= mBuf.size());
-    if (mBuf.isAllocated()) {
-        mBuf.resize(size);
-        mChannel->writeToGuest(std::move(mBuf));
+    assert(size <= mWriteBuffer.size());
+    if (mWriteBuffer.isAllocated()) {
+        mWriteBuffer.resize(size);
+        mChannel->writeToGuest(std::move(mWriteBuffer));
     } else {
         mChannel->writeToGuest(
-                emugl::ChannelBuffer(mBuf.data(), mBuf.data() + size));
+                RenderChannel::Buffer(mWriteBuffer.data(), mWriteBuffer.data() + size));
     }
     return size;
 }
 
-const unsigned char* ChannelStream::read(void* buf, size_t* inout_len) {
-    size_t size = mChannel->readFromGuest((char*)buf, *inout_len, true);
-    if (size == 0) {
+const unsigned char* ChannelStream::readRaw(void* buf, size_t* inout_len) {
+    size_t wanted = *inout_len;
+    size_t count = 0U;
+    auto dst = static_cast<uint8_t*>(buf);
+    D("wanted %d bytes", (int)wanted);
+    while (count < wanted) {
+        if (mReadBufferLeft > 0) {
+            size_t avail = std::min<size_t>(wanted - count, mReadBufferLeft);
+            memcpy(dst + count,
+                   mReadBuffer.data() + (mReadBuffer.size() - mReadBufferLeft),
+                   avail);
+            count += avail;
+            mReadBufferLeft -= avail;
+            continue;
+        }
+        bool blocking = (count == 0);
+        auto result = mChannel->readFromGuest(&mReadBuffer, blocking);
+        D("readFromGuest() returned %d, size %d", (int)result, (int)mReadBuffer.size());
+        if (result == IoResult::Ok) {
+            mReadBufferLeft = mReadBuffer.size();
+            continue;
+        }
+        if (count > 0) {  // There is some data to return.
+            break;
+        }
+        // Result can only be IoResult::Error if |count == 0| since |blocking|
+        // was true, it cannot be IoResult::TryAgain.
+        assert(result == IoResult::Error);
+        D("error while trying to read");
         return nullptr;
     }
-    *inout_len = size;
+    *inout_len = count;
+    D("read %d bytes", (int)count);
     return (const unsigned char*)buf;
 }
 
 void ChannelStream::forceStop() {
-    mChannel->stop();
+    mChannel->stopFromHost();
 }
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.h b/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.h
index 0a2d5f4..02b2fe7 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.h
+++ b/distrib/android-emugl/host/libs/libOpenglRender/ChannelStream.h
@@ -13,25 +13,32 @@
 // limitations under the License.
 #pragma once
 
-#include "IOStream.h"
+#include "OpenglRender/IOStream.h"
 #include "RenderChannelImpl.h"
 
 #include <memory>
-#include <vector>
 
+namespace emugl {
+
+// An IOStream instance that can be used by the host RenderThread to
+// wrap a RenderChannelImpl channel.
 class ChannelStream final : public IOStream {
 public:
-    ChannelStream(std::shared_ptr<emugl::RenderChannelImpl> channel,
-                  size_t bufSize);
-
-    virtual void* allocBuffer(size_t minSize) override final;
-    virtual int commitBuffer(size_t size) override final;
-    virtual const unsigned char* read(void* buf,
-                                      size_t* inout_len) override final;
+    ChannelStream(std::shared_ptr<RenderChannelImpl> channel, size_t bufSize);
 
     void forceStop();
 
+protected:
+    virtual void* allocBuffer(size_t minSize) override final;
+    virtual int commitBuffer(size_t size) override final;
+    virtual const unsigned char* readRaw(void* buf, size_t* inout_len)
+            override final;
+
 private:
-    std::shared_ptr<emugl::RenderChannelImpl> mChannel;
-    emugl::ChannelBuffer mBuf;
+    std::shared_ptr<RenderChannelImpl> mChannel;
+    RenderChannel::Buffer mWriteBuffer;
+    RenderChannel::Buffer mReadBuffer;
+    size_t mReadBufferLeft = 0;
 };
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp b/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp
index 9f616b8..46851a3 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp
+++ b/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <limits.h>
 
+namespace emugl {
+
 ReadBuffer::ReadBuffer(size_t bufsize) {
     m_size = bufsize;
     m_buf = (unsigned char*)malloc(m_size);
@@ -97,3 +99,5 @@
     m_validData -= amount;
     m_readPtr += amount;
 }
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.h b/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.h
index c302e79..f3472ac 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.h
+++ b/distrib/android-emugl/host/libs/libOpenglRender/ReadBuffer.h
@@ -14,7 +14,9 @@
 * limitations under the License.
 */
 #pragma once
-#include "IOStream.h"
+#include "OpenglRender/IOStream.h"
+
+namespace emugl {
 
 class ReadBuffer {
 public:
@@ -30,3 +32,5 @@
     size_t m_size;
     size_t m_validData;
 };
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.cpp b/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.cpp
index cd8b979..db27b41 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.cpp
+++ b/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.cpp
@@ -13,6 +13,8 @@
 // limitations under the License.
 #include "RenderChannelImpl.h"
 
+#include "android/base/synchronization/Lock.h"
+
 #include <algorithm>
 #include <utility>
 
@@ -21,147 +23,130 @@
 
 namespace emugl {
 
-RenderChannelImpl::RenderChannelImpl(std::shared_ptr<RendererImpl> renderer)
-    : mRenderer(std::move(renderer)), mState(State::Empty) {
-    assert(mState.is_lock_free()); // rethink it if this fails - we've already
-                                   // got a lock, use it instead.
+#define EMUGL_DEBUG_LEVEL 0
+#include "emugl/common/debug.h"
+
+using Buffer = RenderChannel::Buffer;
+using IoResult = RenderChannel::IoResult;
+using State = RenderChannel::State;
+using AutoLock = android::base::AutoLock;
+
+// These constants correspond to the capacities of buffer queues
+// used by each RenderChannelImpl instance. Benchmarking shows that
+// it's important to have a large queue for guest -> host transfers,
+// but a much smaller one works for host -> guest ones.
+static constexpr size_t kGuestToHostQueueCapacity = 1024U;
+static constexpr size_t kHostToGuestQueueCapacity = 16U;
+
+RenderChannelImpl::RenderChannelImpl()
+    : mLock(),
+      mFromGuest(kGuestToHostQueueCapacity, mLock),
+      mToGuest(kHostToGuestQueueCapacity, mLock) {
+    updateStateLocked();
 }
 
-void RenderChannelImpl::setEventCallback(
-        RenderChannelImpl::EventCallback callback) {
-    mOnEvent = std::move(callback);
-
-    // Reset the current state to make sure the new subscriber gets it.
-    mState.store(State::Empty, std::memory_order_release);
-    onEvent(true);
+void RenderChannelImpl::setEventCallback(EventCallback&& callback) {
+    mEventCallback = std::move(callback);
 }
 
-bool RenderChannelImpl::write(ChannelBuffer&& buffer) {
-    const bool res = mFromGuest.send(std::move(buffer));
-    onEvent(true);
-    return res;
+void RenderChannelImpl::setWantedEvents(State state) {
+    D("state=%d", (int)state);
+    AutoLock lock(mLock);
+    mWantedEvents |= state;
+    notifyStateChangeLocked();
 }
 
-bool RenderChannelImpl::read(ChannelBuffer* buffer, CallType type) {
-    if (type == CallType::Nonblocking && mToGuest.size() == 0) {
-        return false;
-    }
-    const bool res = mToGuest.receive(buffer);
-    onEvent(true);
-    return res;
+RenderChannel::State RenderChannelImpl::state() const {
+    AutoLock lock(mLock);
+    return mState;
+}
+
+IoResult RenderChannelImpl::tryWrite(Buffer&& buffer) {
+    D("buffer size=%d", (int)buffer.size());
+    AutoLock lock(mLock);
+    auto result = mFromGuest.tryPushLocked(std::move(buffer));
+    updateStateLocked();
+    DD("mFromGuest.tryPushLocked() returned %d, state %d", (int)result,
+       (int)mState);
+    return result;
+}
+
+IoResult RenderChannelImpl::tryRead(Buffer* buffer) {
+    D("enter");
+    AutoLock lock(mLock);
+    auto result = mToGuest.tryPopLocked(buffer);
+    updateStateLocked();
+    DD("mToGuest.tryPopLocked() returned %d, buffer size %d, state %d",
+       (int)result, (int)buffer->size(), (int)mState);
+    return result;
 }
 
 void RenderChannelImpl::stop() {
-    stop(true);
+    D("enter");
+    AutoLock lock(mLock);
+    mFromGuest.closeLocked();
+    mToGuest.closeLocked();
 }
 
-void RenderChannelImpl::forceStop() {
-    stop(false);
+bool RenderChannelImpl::writeToGuest(Buffer&& buffer) {
+    D("buffer size=%d", (int)buffer.size());
+    AutoLock lock(mLock);
+    IoResult result = mToGuest.pushLocked(std::move(buffer));
+    updateStateLocked();
+    DD("mToGuest.pushLocked() returned %d, state %d", (int)result, (int)mState);
+    notifyStateChangeLocked();
+    return result == IoResult::Ok;
 }
 
-void RenderChannelImpl::stop(bool byGuest) {
-    if (mStopped) {
-        return;
-    }
-    mStopped = true;
-    mFromGuest.stop();
-    mToGuest.stop();
-    onEvent(byGuest);
-}
-
-bool RenderChannelImpl::isStopped() const {
-    return mStopped;
-}
-
-void RenderChannelImpl::writeToGuest(ChannelBuffer&& buf) {
-    mToGuest.send(std::move(buf));
-    onEvent(false);
-}
-
-size_t RenderChannelImpl::readFromGuest(ChannelBuffer::value_type* buf,
-                                        size_t size,
-                                        bool blocking) {
-    assert(buf);
-
-    size_t read = 0;
-    const auto bufEnd = buf + size;
-    while (buf != bufEnd && !mStopped) {
-        if (mFromGuestBufferLeft == 0) {
-            if (mFromGuest.size() == 0 && (read > 0 || !blocking)) {
-                break;
-            }
-            if (!mFromGuest.receive(&mFromGuestBuffer)) {
-                break;
-            }
-            mFromGuestBufferLeft = mFromGuestBuffer.size();
-        }
-
-        const size_t curSize =
-                std::min<size_t>(bufEnd - buf, mFromGuestBufferLeft);
-        memcpy(buf, mFromGuestBuffer.data() +
-                            (mFromGuestBuffer.size() - mFromGuestBufferLeft),
-               curSize);
-
-        read += curSize;
-        buf += curSize;
-        mFromGuestBufferLeft -= curSize;
-    }
-    onEvent(false);
-    return read;
-}
-
-void RenderChannelImpl::onEvent(bool byGuest) {
-    if (!mOnEvent) {
-        return;
-    }
-
-    // We need to make sure that the state returned from calcState() remains
-    // valid when we write it into mState; this means we need to lock both
-    // calcState() and mState assignment with the same lock - otherwise it is
-    // possible (and it happened before the lock was added) that some other
-    // thread would overwrite a newer state with its older value, e.g.:
-    //  - thread 1 reads the last message from |mToGuest|
-    //  - thread 1 enters onEvent()
-    //  - thread 1 calculates state 1 = "can't read", switches out
-    //  -   thread 2 writes some data into |mToGuest|
-    //  -   thread 2 enters onEvent()
-    //  -   thread 2 calculates state 2 = "can read"
-    //  -   thread 2 gets the lock
-    //  -   thread 2 updates |mState| to "can read", unlocks, switches out
-    //  - thread 1 gets the lock
-    //  - thread 1 overwrites |mState| with state 1 = "can't read"
-    //  - thread 1 unlocks and calls the |mOnEvent| callback.
-    //
-    // The result is that state 2 = "can read" is completely lost - callback
-    // is never called when |mState| is "can read".
-    //
-    // But if the whole block of code is locked, threads can't overwrite newer
-    // |mState| with some older value, and the described situation would never
-    // happen.
-    android::base::AutoLock lock(mStateLock);
-    const State newState = calcState();
-    if (mState != newState) {
-        mState = newState;
-        lock.unlock();
-
-        mOnEvent(newState,
-                 byGuest ? EventSource::Client : EventSource::RenderChannel);
-    }
-}
-
-RenderChannel::State RenderChannelImpl::calcState() const {
-    State state = State::Empty;
-    if (mStopped) {
-        state = State::Stopped;
+IoResult RenderChannelImpl::readFromGuest(Buffer* buffer, bool blocking) {
+    D("enter");
+    AutoLock lock(mLock);
+    IoResult result;
+    if (blocking) {
+        result = mFromGuest.popLocked(buffer);
     } else {
-        if (mFromGuest.size() < mFromGuest.capacity()) {
-            state |= State::CanWrite;
-        }
-        if (mToGuest.size() > 0) {
-            state |= State::CanRead;
-        }
+        result = mFromGuest.tryPopLocked(buffer);
     }
-    return state;
+    updateStateLocked();
+    DD("mFromGuest.%s() return %d, buffer size %d, state %d",
+       blocking ? "popLocked" : "tryPopLocked", (int)result,
+       (int)buffer->size(), (int)mState);
+    notifyStateChangeLocked();
+    return result;
+}
+
+void RenderChannelImpl::stopFromHost() {
+    D("enter");
+
+    AutoLock lock(mLock);
+    mFromGuest.closeLocked();
+    mToGuest.closeLocked();
+    mState |= State::Stopped;
+    notifyStateChangeLocked();
+}
+
+void RenderChannelImpl::updateStateLocked() {
+    State state = RenderChannel::State::Empty;
+
+    if (mToGuest.canPopLocked()) {
+        state |= State::CanRead;
+    }
+    if (mFromGuest.canPushLocked()) {
+        state |= State::CanWrite;
+    }
+    if (mToGuest.isClosedLocked()) {
+        state |= State::Stopped;
+    }
+    mState = state;
+}
+
+void RenderChannelImpl::notifyStateChangeLocked() {
+    State available = mState & mWantedEvents;
+    if (available != 0) {
+        D("callback with %d", (int)available);
+        mWantedEvents &= ~mState;
+        mEventCallback(available);
+    }
 }
 
 }  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.h b/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.h
index eca2966..0d306d1 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.h
+++ b/distrib/android-emugl/host/libs/libOpenglRender/RenderChannelImpl.h
@@ -14,80 +14,76 @@
 #pragma once
 
 #include "OpenglRender/RenderChannel.h"
+#include "BufferQueue.h"
 #include "RendererImpl.h"
 
-#include "android/base/Compiler.h"
-#include "android/base/synchronization/Lock.h"
-#include "android/base/synchronization/MessageChannel.h"
-
-#include <atomic>
-#include <memory>
-
 namespace emugl {
 
+// Implementation of the RenderChannel interface that connects a guest
+// client thread (really an AndroidPipe implementation) to a host
+// RenderThread instance.
 class RenderChannelImpl final : public RenderChannel {
 public:
-    RenderChannelImpl(std::shared_ptr<RendererImpl> renderer);
+    // Default constructor.
+    RenderChannelImpl();
 
-public:
-    // RenderChannel implementation, operations provided for a guest system
-    virtual void setEventCallback(EventCallback callback) override final;
+    /////////////////////////////////////////////////////////////////
+    // RenderChannel overriden methods. These are called from the guest
+    // client thread.
 
-    virtual bool write(ChannelBuffer&& buffer) override final;
-    virtual bool read(ChannelBuffer* buffer, CallType type) override final;
+    // Set the event |callback| to be notified when the host changes the
+    // state of the channel, according to the event mask provided by
+    // setWantedEvents(). Call this function right after creating the
+    // instance.
+    virtual void setEventCallback(EventCallback&& callback) override final;
 
-    virtual State currentState() const override final {
-        return mState.load(std::memory_order_acquire);
-    }
+    // Set the mask of events the guest wants to be notified of from the
+    // host thread.
+    virtual void setWantedEvents(State state) override final;
 
+    // Return the current channel state relative to the guest.
+    virtual State state() const override final;
+
+    // Try to send a buffer from the guest to the host render thread.
+    virtual IoResult tryWrite(Buffer&& buffer) override final;
+
+    // Try to read a buffer from the host render thread into the guest.
+    virtual IoResult tryRead(Buffer* buffer) override final;
+
+    // Close the channel from the guest.
     virtual void stop() override final;
-    virtual bool isStopped() const override final;
 
-public:
-    // These functions are for the RenderThread, they could be called in
-    // parallel with the ones from the RenderChannel interface. Make sure the
-    // internal state remains consistent all the time.
-    void writeToGuest(ChannelBuffer&& buf);
-    size_t readFromGuest(ChannelBuffer::value_type* buf, size_t size,
-                         bool blocking);
-    void forceStop();
+    /////////////////////////////////////////////////////////////////
+    // These functions are called from the host render thread.
+
+    // Send a buffer to the guest, this call is blocking. On success,
+    // move |buffer| into the channel and return true. On failure, return
+    // false (meaning that the channel was closed).
+    bool writeToGuest(Buffer&& buffer);
+
+    // Read data from the guest. If |blocking| is true, the call will be
+    // blocking. On success, move item into |*buffer| and return true. On
+    // failure, return IoResult::Error to indicate the channel was closed,
+    // or IoResult::TryAgain to indicate it was empty (this can happen only
+    // if |blocking| is false).
+    IoResult readFromGuest(Buffer* buffer, bool blocking);
+
+    // Close the channel from the host.
+    void stopFromHost();
 
 private:
-    DISALLOW_COPY_ASSIGN_AND_MOVE(RenderChannelImpl);
+    void updateStateLocked();
+    void notifyStateChangeLocked();
 
-private:
-    void onEvent(bool byGuest);
-    State calcState() const;
-    void stop(bool byGuest);
+    EventCallback mEventCallback;
 
-private:
-    std::shared_ptr<RendererImpl> mRenderer;
-
-    EventCallback mOnEvent;
-
-    // Protects the state recalculation and writes to mState.
-    //
-    // The correctness condition governing the relationship between mFromGuest,
-    // mToGuest, and mState is that we can't reach a potentially stable state
-    // (i.e., at the end of a set of invocations of publically-visible
-    // operations) in which either:
-    //  - mFromGuest().size() < mFromGuest.capacity(), yet state does not have
-    //    State::CanWrite, or
-    //  - mToGuest().size > 0, yet state does not have State::CanRead.
-    // Clients assume they can poll currentState() and have the indicate whether
-    // a write or read might possibly succeed, and this correctness condition
-    // makes that assumption valid -- if a write or read might succeed,
-    // mState is required to eventually indicate this.
-    android::base::Lock mStateLock;
-    std::atomic<State> mState;
-
-    bool mStopped = false;
-
-    android::base::MessageChannel<ChannelBuffer, 1024> mFromGuest;
-    android::base::MessageChannel<ChannelBuffer, 16> mToGuest;
-
-    ChannelBuffer mFromGuestBuffer;
-    size_t mFromGuestBufferLeft = 0;
+    // A single lock to protect the state and the two buffer queues at the
+    // same time. NOTE: This needs to appear before the BufferQueue instances.
+    mutable android::base::Lock mLock;
+    State mState = State::Empty;
+    State mWantedEvents = State::Empty;
+    BufferQueue mFromGuest;
+    BufferQueue mToGuest;
 };
 
 }  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.cpp b/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.cpp
index bf7f45f..a34c985 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.cpp
+++ b/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.cpp
@@ -31,38 +31,43 @@
 
 #include "android/base/system/System.h"
 
+#define EMUGL_DEBUG_LEVEL 0
+#include "emugl/common/debug.h"
+
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
+namespace emugl {
+
 // Start with a smaller buffer to not waste memory on a low-used render threads.
 static constexpr int kStreamBufferSize = 128 * 1024;
 
-RenderThread::RenderThread(std::weak_ptr<emugl::RendererImpl> renderer,
-                           std::shared_ptr<emugl::RenderChannelImpl> channel)
+RenderThread::RenderThread(std::weak_ptr<RendererImpl> renderer,
+                           std::shared_ptr<RenderChannelImpl> channel)
     : mChannel(channel), mRenderer(renderer) {}
 
 RenderThread::~RenderThread() = default;
 
 // static
 std::unique_ptr<RenderThread> RenderThread::create(
-        std::weak_ptr<emugl::RendererImpl> renderer,
-        std::shared_ptr<emugl::RenderChannelImpl> channel) {
+        std::weak_ptr<RendererImpl> renderer,
+        std::shared_ptr<RenderChannelImpl> channel) {
     return std::unique_ptr<RenderThread>(
             new RenderThread(renderer, channel));
 }
 
 intptr_t RenderThread::main() {
+    ChannelStream stream(mChannel, RenderChannel::Buffer::kSmallSize);
+
     uint32_t flags = 0;
-    if (mChannel->readFromGuest(reinterpret_cast<char*>(&flags),
-                                sizeof(flags), true) != sizeof(flags)) {
+    if (stream.read(&flags, sizeof(flags)) != sizeof(flags)) {
         return 0;
     }
 
     // |flags| used to have something, now they're not used.
     (void)flags;
 
-    ChannelStream stream(mChannel, emugl::ChannelBuffer::kSmallSize);
     RenderThreadInfo tInfo;
     ChecksumCalculatorThreadInfo tChecksumInfo;
     ChecksumCalculator& checksumCalc = tChecksumInfo.get();
@@ -114,8 +119,12 @@
 
         const int stat = readBuf.getData(&stream, packetSize);
         if (stat <= 0) {
+            D("Warning: render thread could not read data from stream");
             break;
         }
+        DD("render thread read %d bytes, op %d, packet size %d",
+           (int)readBuf.validData(), *(int32_t*)readBuf.buf(),
+           *(int32_t*)(readBuf.buf() + 4));
 
         //
         // log received bandwidth statistics
@@ -214,3 +223,5 @@
 
     return 0;
 }
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.h b/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.h
index 7b4b63e..723f274 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.h
+++ b/distrib/android-emugl/host/libs/libOpenglRender/RenderThread.h
@@ -14,7 +14,6 @@
 * limitations under the License.
 */
 #pragma once
-#include "IOStream.h"
 
 #include "emugl/common/mutex.h"
 #include "emugl/common/thread.h"
@@ -22,9 +21,9 @@
 #include <memory>
 
 namespace emugl {
+
 class RenderChannelImpl;
 class RendererImpl;
-}
 
 // A class used to model a thread of the RenderServer. Each one of them
 // handles a single guest client / protocol byte stream.
@@ -32,8 +31,8 @@
 public:
     // Create a new RenderThread instance.
     static std::unique_ptr<RenderThread> create(
-            std::weak_ptr<emugl::RendererImpl> renderer,
-            std::shared_ptr<emugl::RenderChannelImpl> channel);
+            std::weak_ptr<RendererImpl> renderer,
+            std::shared_ptr<RenderChannelImpl> channel);
 
     virtual ~RenderThread();
 
@@ -42,11 +41,13 @@
     bool isFinished() { return tryWait(NULL); }
 
 private:
-    RenderThread(std::weak_ptr<emugl::RendererImpl> renderer,
-                 std::shared_ptr<emugl::RenderChannelImpl> channel);
+    RenderThread(std::weak_ptr<RendererImpl> renderer,
+                 std::shared_ptr<RenderChannelImpl> channel);
 
     virtual intptr_t main();
 
-    std::shared_ptr<emugl::RenderChannelImpl> mChannel;
-    std::weak_ptr<emugl::RendererImpl> mRenderer;
+    std::shared_ptr<RenderChannelImpl> mChannel;
+    std::weak_ptr<RendererImpl> mRenderer;
 };
+
+}  // namespace emugl
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/RendererImpl.cpp b/distrib/android-emugl/host/libs/libOpenglRender/RendererImpl.cpp
index ff14aac..68d646f 100644
--- a/distrib/android-emugl/host/libs/libOpenglRender/RendererImpl.cpp
+++ b/distrib/android-emugl/host/libs/libOpenglRender/RendererImpl.cpp
@@ -92,7 +92,7 @@
 
     for (const auto& t : threads) {
         if (const auto channel = t.second.lock()) {
-            channel->forceStop();
+            channel->stopFromHost();
         }
     }
     // We're stopping the renderer, so there's no need to clean up resources
@@ -106,8 +106,7 @@
 }
 
 RenderChannelPtr RendererImpl::createRenderChannel() {
-    const auto channel =
-            std::make_shared<RenderChannelImpl>(shared_from_this());
+    const auto channel = std::make_shared<RenderChannelImpl>();
 
     std::unique_ptr<RenderThread> rt(RenderThread::create(
             shared_from_this(), channel));
diff --git a/distrib/android-emugl/host/tools/emugen/ApiGen.cpp b/distrib/android-emugl/host/tools/emugen/ApiGen.cpp
index 8af6e29..fc51ebc 100644
--- a/distrib/android-emugl/host/tools/emugen/ApiGen.cpp
+++ b/distrib/android-emugl/host/tools/emugen/ApiGen.cpp
@@ -803,7 +803,7 @@
     fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
     fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
 
-    fprintf(fp, "#include \"IOStream.h\"\n");
+    fprintf(fp, "#include \"OpenglRender/IOStream.h\"\n");
     fprintf(fp, "#include \"ChecksumCalculator.h\"\n");
     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
     fprintf(fp, "#include \"emugl/common/logging.h\"\n");
diff --git a/distrib/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h b/distrib/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h
index 30ada6f..d025090 100644
--- a/distrib/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h
+++ b/distrib/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h
@@ -4,7 +4,7 @@
 #ifndef GUARD_foo_decoder_context_t
 #define GUARD_foo_decoder_context_t
 
-#include "IOStream.h"
+#include "OpenglRender/IOStream.h"
 #include "ChecksumCalculator.h"
 #include "foo_server_context.h"
 
diff --git a/distrib/android-emugl/shared/OpenglCodecCommon/Android.mk b/distrib/android-emugl/shared/OpenglCodecCommon/Android.mk
index 509812a..e5cefae 100644
--- a/distrib/android-emugl/shared/OpenglCodecCommon/Android.mk
+++ b/distrib/android-emugl/shared/OpenglCodecCommon/Android.mk
@@ -17,7 +17,7 @@
 
 LOCAL_SRC_FILES := $(host_commonSources)
 $(call emugl-import, libemugl_common)
-$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include/libOpenglRender $(LOCAL_PATH))
+$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include/OpenglRender $(LOCAL_PATH))
 $(call emugl-export,LDLIBS,$(host_commonLdLibs))
 $(call emugl-end-module)
 
diff --git a/distrib/android-emugl/shared/emugl/common/debug.h b/distrib/android-emugl/shared/emugl/common/debug.h
new file mode 100644
index 0000000..d431381
--- /dev/null
+++ b/distrib/android-emugl/shared/emugl/common/debug.h
@@ -0,0 +1,33 @@
+/*
+* Copyright (C) 2016 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.
+*/
+
+#pragma once
+
+// Usage: Define EMUGL_DEBUG_LEVEL before including this header to
+//        select the behaviour of the D() and DD() macros.
+
+#if defined(EMUGL_DEBUG_LEVEL) && EMUGL_DEBUG_LEVEL > 0
+#include <stdio.h>
+#define D(...)  (printf("%s:%d:%s: ", __FILE__, __LINE__, __func__), printf(__VA_ARGS__), printf("\n"), fflush(stdout))
+#else
+#define D(...)  (void)0
+#endif // EMUGL_DEBUG_LEVEL > 0
+
+#if defined(EMUGL_DEBUG_LEVEL) && EMUGL_DEBUG_LEVEL > 1
+#define DD(...) D(__VA_ARGS__)
+#else
+#define DD(...) (void)0
+#endif // EMUGL_DEBUG_LEVEL > 1