drm_hwcomposer: fix typo in formats switch statement
am: 78beee0ecd

Change-Id: I13a0894555d719e5d15b656c35918bc891f836db
diff --git a/Android.mk b/Android.mk
index 2c4f7e3..f46276c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -55,11 +55,9 @@
 	system/core/libsync/include \
 
 LOCAL_SRC_FILES := \
-	autolock.cpp \
 	drmresources.cpp \
 	drmcomposition.cpp \
 	drmcompositor.cpp \
-	drmcompositorworker.cpp \
 	drmconnector.cpp \
 	drmcrtc.cpp \
 	drmdisplaycomposition.cpp \
diff --git a/autolock.cpp b/autolock.cpp
deleted file mode 100644
index 1a2ded7..0000000
--- a/autolock.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#define LOG_TAG "hwc-drm-auto-lock"
-
-#include "autolock.h"
-
-#include <errno.h>
-#include <pthread.h>
-
-#include <cutils/log.h>
-
-namespace android {
-
-int AutoLock::Lock() {
-  if (locked_) {
-    ALOGE("Invalid attempt to double lock AutoLock %s", name_);
-    return -EINVAL;
-  }
-  int ret = pthread_mutex_lock(mutex_);
-  if (ret) {
-    ALOGE("Failed to acquire %s lock %d", name_, ret);
-    return ret;
-  }
-  locked_ = true;
-  return 0;
-}
-
-int AutoLock::Unlock() {
-  if (!locked_) {
-    ALOGE("Invalid attempt to unlock unlocked AutoLock %s", name_);
-    return -EINVAL;
-  }
-  int ret = pthread_mutex_unlock(mutex_);
-  if (ret) {
-    ALOGE("Failed to release %s lock %d", name_, ret);
-    return ret;
-  }
-  locked_ = false;
-  return 0;
-}
-}
diff --git a/autolock.h b/autolock.h
deleted file mode 100644
index 3b824e2..0000000
--- a/autolock.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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 <pthread.h>
-
-namespace android {
-
-class AutoLock {
- public:
-  AutoLock(pthread_mutex_t *mutex, const char *const name)
-      : mutex_(mutex), name_(name) {
-  }
-  ~AutoLock() {
-    if (locked_)
-      Unlock();
-  }
-
-  AutoLock(const AutoLock &rhs) = delete;
-  AutoLock &operator=(const AutoLock &rhs) = delete;
-
-  int Lock();
-  int Unlock();
-
- private:
-  pthread_mutex_t *const mutex_;
-  bool locked_ = false;
-  const char *const name_;
-};
-}
diff --git a/drmcompositorworker.cpp b/drmcompositorworker.cpp
deleted file mode 100644
index a4e7fc9..0000000
--- a/drmcompositorworker.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "hwc-drm-compositor-worker"
-
-#include "drmdisplaycompositor.h"
-#include "drmcompositorworker.h"
-#include "worker.h"
-
-#include <stdlib.h>
-
-#include <cutils/log.h>
-#include <hardware/hardware.h>
-
-namespace android {
-
-static const int64_t kSquashWait = 500000000LL;
-
-DrmCompositorWorker::DrmCompositorWorker(DrmDisplayCompositor *compositor)
-    : Worker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
-      compositor_(compositor) {
-}
-
-DrmCompositorWorker::~DrmCompositorWorker() {
-}
-
-int DrmCompositorWorker::Init() {
-  return InitWorker();
-}
-
-void DrmCompositorWorker::Routine() {
-  int ret;
-  if (!compositor_->HaveQueuedComposites()) {
-    Lock();
-
-    // Only use a timeout if we didn't do a SquashAll last time. This will
-    // prevent wait_ret == -ETIMEDOUT which would trigger a SquashAll and be a
-    // pointless drain on resources.
-    int wait_ret = did_squash_all_ ? WaitForSignalOrExitLocked()
-                                   : WaitForSignalOrExitLocked(kSquashWait);
-    Unlock();
-
-    switch (wait_ret) {
-      case 0:
-        break;
-      case -EINTR:
-        return;
-      case -ETIMEDOUT:
-        ret = compositor_->SquashAll();
-        if (ret)
-          ALOGE("Failed to squash all %d", ret);
-        did_squash_all_ = true;
-        return;
-      default:
-        ALOGE("Failed to wait for signal, %d", wait_ret);
-        return;
-    }
-  }
-
-  ret = compositor_->Composite();
-  if (ret)
-    ALOGE("Failed to composite! %d", ret);
-  did_squash_all_ = false;
-}
-}
diff --git a/drmcompositorworker.h b/drmcompositorworker.h
deleted file mode 100644
index 731bc65..0000000
--- a/drmcompositorworker.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 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 ANDROID_DRM_COMPOSITOR_WORKER_H_
-#define ANDROID_DRM_COMPOSITOR_WORKER_H_
-
-#include "worker.h"
-
-namespace android {
-
-class DrmDisplayCompositor;
-
-class DrmCompositorWorker : public Worker {
- public:
-  DrmCompositorWorker(DrmDisplayCompositor *compositor);
-  ~DrmCompositorWorker() override;
-
-  int Init();
-
- protected:
-  void Routine() override;
-
-  DrmDisplayCompositor *compositor_;
-  bool did_squash_all_ = false;
-};
-}
-
-#endif
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index a1baed1..642a1c7 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -19,10 +19,13 @@
 
 #include "drmdisplaycompositor.h"
 
-#include <pthread.h>
 #include <sched.h>
 #include <stdlib.h>
 #include <time.h>
+#include <algorithm>
+#include <bitset>
+#include <cinttypes>
+#include <mutex>
 #include <sstream>
 #include <vector>
 
@@ -31,7 +34,6 @@
 #include <sync/sync.h>
 #include <utils/Trace.h>
 
-#include "autolock.h"
 #include "drmcrtc.h"
 #include "drmplane.h"
 #include "drmresources.h"
@@ -41,6 +43,8 @@
 
 namespace android {
 
+static const int64_t kSquashWait = 500LL;
+
 void SquashState::Init(DrmHwcLayer *layers, size_t num_layers) {
   generation_number_++;
   valid_history_ = 0;
@@ -177,64 +181,53 @@
 }
 
 DrmDisplayCompositor::FrameWorker::FrameWorker(DrmDisplayCompositor *compositor)
-    : Worker("frame-worker", HAL_PRIORITY_URGENT_DISPLAY),
+    : QueueWorker("frame-worker", HAL_PRIORITY_URGENT_DISPLAY),
       compositor_(compositor) {
 }
 
-DrmDisplayCompositor::FrameWorker::~FrameWorker() {
-}
-
 int DrmDisplayCompositor::FrameWorker::Init() {
+  set_max_queue_size(DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH);
   return InitWorker();
 }
 
 void DrmDisplayCompositor::FrameWorker::QueueFrame(
     std::unique_ptr<DrmDisplayComposition> composition, int status) {
-  Lock();
-  FrameState frame;
-  frame.composition = std::move(composition);
-  frame.status = status;
-  frame_queue_.push(std::move(frame));
-  Unlock();
-  Signal();
+  std::unique_ptr<FrameState> frame(
+      new FrameState(std::move(composition), status));
+
+  auto start = std::chrono::high_resolution_clock::now();
+  int ret = QueueWork(std::move(frame));
+  if (ret) {
+    ALOGE("Unable to queue frame work (%d)", ret);
+    // TODO: error handling (timeout or exit)
+    return;
+  }
+  auto end = std::chrono::high_resolution_clock::now();
+
+  uint64_t duration_us =
+      std::chrono::duration_cast<std::chrono::microseconds>(end - start)
+          .count();
+  if (duration_us > max_duration_us)
+    max_duration_us = duration_us;
 }
 
-void DrmDisplayCompositor::FrameWorker::Routine() {
-  int wait_ret = 0;
-
-  Lock();
-  if (frame_queue_.empty()) {
-    wait_ret = WaitForSignalOrExitLocked();
-  }
-
-  FrameState frame;
-  if (!frame_queue_.empty()) {
-    frame = std::move(frame_queue_.front());
-    frame_queue_.pop();
-  }
-  Unlock();
-
-  if (wait_ret == -EINTR) {
-    return;
-  } else if (wait_ret) {
-    ALOGE("Failed to wait for signal, %d", wait_ret);
-    return;
-  }
-  compositor_->ApplyFrame(std::move(frame.composition), frame.status);
+void DrmDisplayCompositor::FrameWorker::ProcessWork(
+    std::unique_ptr<FrameState> frame) {
+  compositor_->ApplyFrame(std::move(frame->composition), frame->status);
 }
 
 DrmDisplayCompositor::DrmDisplayCompositor()
-    : drm_(NULL),
+    : QueueWorker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+      drm_(NULL),
       display_(-1),
-      worker_(this),
       frame_worker_(this),
-      initialized_(false),
       active_(false),
       use_hw_overlays_(true),
       framebuffer_index_(0),
       squash_framebuffer_index_(0),
       dump_frames_composited_(0),
-      dump_last_timestamp_ns_(0) {
+      dump_last_timestamp_ns_(0),
+      max_duration_us(0) {
   struct timespec ts;
   if (clock_gettime(CLOCK_MONOTONIC, &ts))
     return;
@@ -242,58 +235,32 @@
 }
 
 DrmDisplayCompositor::~DrmDisplayCompositor() {
-  if (!initialized_)
+  if (!initialized())
     return;
 
-  worker_.Exit();
   frame_worker_.Exit();
+  Exit();
 
-  int ret = pthread_mutex_lock(&lock_);
-  if (ret)
-    ALOGE("Failed to acquire compositor lock %d", ret);
+  std::lock_guard<std::mutex> lk(mutex_);
 
   if (mode_.blob_id)
     drm_->DestroyPropertyBlob(mode_.blob_id);
   if (mode_.old_blob_id)
     drm_->DestroyPropertyBlob(mode_.old_blob_id);
 
-  while (!composite_queue_.empty()) {
-    composite_queue_.front().reset();
-    composite_queue_.pop();
-  }
   active_composition_.reset();
-
-  ret = pthread_mutex_unlock(&lock_);
-  if (ret)
-    ALOGE("Failed to acquire compositor lock %d", ret);
-
-  pthread_mutex_destroy(&lock_);
 }
 
 int DrmDisplayCompositor::Init(DrmResources *drm, int display) {
   drm_ = drm;
   display_ = display;
 
-  int ret = pthread_mutex_init(&lock_, NULL);
-  if (ret) {
-    ALOGE("Failed to initialize drm compositor lock %d\n", ret);
-    return ret;
-  }
-  ret = worker_.Init();
-  if (ret) {
-    pthread_mutex_destroy(&lock_);
-    ALOGE("Failed to initialize compositor worker %d\n", ret);
-    return ret;
-  }
-  ret = frame_worker_.Init();
-  if (ret) {
-    pthread_mutex_destroy(&lock_);
-    ALOGE("Failed to initialize frame worker %d\n", ret);
-    return ret;
-  }
+  frame_worker_.Init();
 
-  initialized_ = true;
-  return 0;
+  set_max_queue_size(DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH);
+  set_idle_timeout(kSquashWait);
+
+  return InitWorker();
 }
 
 std::unique_ptr<DrmDisplayComposition> DrmDisplayCompositor::CreateComposition()
@@ -324,29 +291,23 @@
       return -ENOENT;
   }
 
-  int ret = pthread_mutex_lock(&lock_);
+  auto start = std::chrono::high_resolution_clock::now();
+
+  int ret = QueueWork(std::move(composition));
   if (ret) {
-    ALOGE("Failed to acquire compositor lock %d", ret);
+    ALOGE("Unable to queue work (%d)", ret);
+    // TODO: error handling (timeout or exit)
     return ret;
   }
 
-  // Block the queue if it gets too large. Otherwise, SurfaceFlinger will start
-  // to eat our buffer handles when we get about 1 second behind.
-  while (composite_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH) {
-    pthread_mutex_unlock(&lock_);
-    sched_yield();
-    pthread_mutex_lock(&lock_);
-  }
+  auto end = std::chrono::high_resolution_clock::now();
 
-  composite_queue_.push(std::move(composition));
+  uint64_t duration_us =
+      std::chrono::duration_cast<std::chrono::microseconds>(end - start)
+          .count();
+  if (duration_us > max_duration_us)
+    max_duration_us = duration_us;
 
-  ret = pthread_mutex_unlock(&lock_);
-  if (ret) {
-    ALOGE("Failed to release compositor lock %d", ret);
-    return ret;
-  }
-
-  worker_.Signal();
   return 0;
 }
 
@@ -859,11 +820,7 @@
 }
 
 void DrmDisplayCompositor::ClearDisplay() {
-  AutoLock lock(&lock_, "compositor");
-  int ret = lock.Lock();
-  if (ret)
-    return;
-
+  std::lock_guard<std::mutex> lk(mutex_);
   if (!active_composition_)
     return;
 
@@ -894,19 +851,12 @@
   if (active_composition_)
     active_composition_->SignalCompositionDone();
 
-  ret = pthread_mutex_lock(&lock_);
-  if (ret)
-    ALOGE("Failed to acquire lock for active_composition swap");
-
+  std::lock_guard<std::mutex> lk(mutex_);
   active_composition_.swap(composition);
-
-  if (!ret)
-    ret = pthread_mutex_unlock(&lock_);
-  if (ret)
-    ALOGE("Failed to release lock for active_composition swap");
 }
 
-int DrmDisplayCompositor::Composite() {
+void DrmDisplayCompositor::ProcessWork(
+    std::unique_ptr<DrmDisplayComposition> composition) {
   ATRACE_CALL();
 
   if (!pre_compositor_) {
@@ -914,39 +864,17 @@
     int ret = pre_compositor_->Init();
     if (ret) {
       ALOGE("Failed to initialize OpenGL compositor %d", ret);
-      return ret;
+      return;
     }
   }
 
-  int ret = pthread_mutex_lock(&lock_);
-  if (ret) {
-    ALOGE("Failed to acquire compositor lock %d", ret);
-    return ret;
-  }
-  if (composite_queue_.empty()) {
-    ret = pthread_mutex_unlock(&lock_);
-    if (ret)
-      ALOGE("Failed to release compositor lock %d", ret);
-    return ret;
-  }
-
-  std::unique_ptr<DrmDisplayComposition> composition(
-      std::move(composite_queue_.front()));
-
-  composite_queue_.pop();
-
-  ret = pthread_mutex_unlock(&lock_);
-  if (ret) {
-    ALOGE("Failed to release compositor lock %d", ret);
-    return ret;
-  }
-
+  int ret;
   switch (composition->type()) {
     case DRM_COMPOSITION_TYPE_FRAME:
       ret = PrepareFrame(composition.get());
       if (ret) {
         ALOGE("Failed to prepare frame for display %d", display_);
-        return ret;
+        return;
       }
       if (composition->geometry_changed()) {
         // Send the composition to the kernel to ensure we can commit it. This
@@ -972,7 +900,7 @@
           // to signal the release fences from that composition to avoid
           // hanging.
           ClearDisplay();
-          return ret;
+          return;
         }
       }
       frame_worker_.QueueFrame(std::move(composition), ret);
@@ -981,7 +909,7 @@
       ret = ApplyDpms(composition.get());
       if (ret)
         ALOGE("Failed to apply dpms for display %d", display_);
-      return ret;
+      break;
     case DRM_COMPOSITION_TYPE_MODESET:
       mode_.mode = composition->display_mode();
       if (mode_.blob_id)
@@ -989,41 +917,19 @@
       std::tie(ret, mode_.blob_id) = CreateModeBlob(mode_.mode);
       if (ret) {
         ALOGE("Failed to create mode blob for display %d", display_);
-        return ret;
+        return;
       }
       mode_.needs_modeset = true;
-      return 0;
+      break;
     default:
       ALOGE("Unknown composition type %d", composition->type());
-      return -EINVAL;
+      break;
   }
-
-  return ret;
-}
-
-bool DrmDisplayCompositor::HaveQueuedComposites() const {
-  int ret = pthread_mutex_lock(&lock_);
-  if (ret) {
-    ALOGE("Failed to acquire compositor lock %d", ret);
-    return false;
-  }
-
-  bool empty_ret = !composite_queue_.empty();
-
-  ret = pthread_mutex_unlock(&lock_);
-  if (ret) {
-    ALOGE("Failed to release compositor lock %d", ret);
-    return false;
-  }
-
-  return empty_ret;
 }
 
 int DrmDisplayCompositor::SquashAll() {
-  AutoLock lock(&lock_, "compositor");
-  int ret = lock.Lock();
-  if (ret)
-    return ret;
+  std::unique_lock<std::mutex> lk(mutex_);
+  int ret;
 
   if (!active_composition_)
     return 0;
@@ -1032,7 +938,7 @@
   ret = SquashFrame(active_composition_.get(), comp.get());
 
   // ApplyFrame needs the lock
-  lock.Unlock();
+  lk.unlock();
 
   if (!ret)
     ApplyFrame(std::move(comp), 0);
@@ -1164,17 +1070,13 @@
 }
 
 void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
-  int ret = pthread_mutex_lock(&lock_);
-  if (ret)
-    return;
-
+  std::lock_guard<std::mutex> lk(mutex_);
   uint64_t num_frames = dump_frames_composited_;
   dump_frames_composited_ = 0;
 
   struct timespec ts;
-  ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+  int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
   if (ret) {
-    pthread_mutex_unlock(&lock_);
     return;
   }
 
@@ -1188,11 +1090,21 @@
 
   dump_last_timestamp_ns_ = cur_ts;
 
+  *out << "----Jank Stats: "
+       << " compositor_max_q_wait_us=" << max_duration_us
+       << " frameworker_max_q_wait_us=" << frame_worker_.max_duration_us
+       << "\n";
+
+  max_duration_us = 0;
+  frame_worker_.max_duration_us = 0;
+
   if (active_composition_)
     active_composition_->Dump(out);
 
   squash_state_.Dump(out);
+}
 
-  pthread_mutex_unlock(&lock_);
+void DrmDisplayCompositor::ProcessIdle() {
+  SquashAll();
 }
 }
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index 9487cac..961fe72 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -17,13 +17,13 @@
 #ifndef ANDROID_DRM_DISPLAY_COMPOSITOR_H_
 #define ANDROID_DRM_DISPLAY_COMPOSITOR_H_
 
-#include "drmhwcomposer.h"
 #include "drmcomposition.h"
-#include "drmcompositorworker.h"
 #include "drmframebuffer.h"
+#include "drmhwcomposer.h"
+#include "queue_worker.h"
 #include "separate_rects.h"
 
-#include <pthread.h>
+#include <chrono>
 #include <memory>
 #include <queue>
 #include <sstream>
@@ -81,7 +81,7 @@
   std::vector<Region> regions_;
 };
 
-class DrmDisplayCompositor {
+class DrmDisplayCompositor : public QueueWorker<DrmDisplayComposition> {
  public:
   DrmDisplayCompositor();
   ~DrmDisplayCompositor();
@@ -90,39 +90,42 @@
 
   std::unique_ptr<DrmDisplayComposition> CreateComposition() const;
   int QueueComposition(std::unique_ptr<DrmDisplayComposition> composition);
-  int Composite();
+  void ProcessWork(std::unique_ptr<DrmDisplayComposition> composition);
+  void ProcessIdle();
   int SquashAll();
   void Dump(std::ostringstream *out) const;
 
   std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
 
-  bool HaveQueuedComposites() const;
-
   SquashState *squash_state() {
     return &squash_state_;
   }
 
  private:
   struct FrameState {
+    FrameState(std::unique_ptr<DrmDisplayComposition> composition, int status)
+        : composition(std::move(composition)), status(status) {
+    }
+
     std::unique_ptr<DrmDisplayComposition> composition;
     int status = 0;
   };
 
-  class FrameWorker : public Worker {
+  class FrameWorker : public QueueWorker<FrameState> {
    public:
     FrameWorker(DrmDisplayCompositor *compositor);
-    ~FrameWorker() override;
 
     int Init();
     void QueueFrame(std::unique_ptr<DrmDisplayComposition> composition,
                     int status);
 
+    mutable uint64_t max_duration_us;
+
    protected:
-    void Routine() override;
+    void ProcessWork(std::unique_ptr<FrameState> frame);
 
    private:
     DrmDisplayCompositor *compositor_;
-    std::queue<FrameState> frame_queue_;
   };
 
   struct ModeState {
@@ -158,13 +161,10 @@
   DrmResources *drm_;
   int display_;
 
-  DrmCompositorWorker worker_;
   FrameWorker frame_worker_;
 
-  std::queue<std::unique_ptr<DrmDisplayComposition>> composite_queue_;
   std::unique_ptr<DrmDisplayComposition> active_composition_;
 
-  bool initialized_;
   bool active_;
   bool use_hw_overlays_;
 
@@ -179,12 +179,13 @@
   DrmFramebuffer squash_framebuffers_[2];
 
   // mutable since we need to acquire in HaveQueuedComposites
-  mutable pthread_mutex_t lock_;
+  mutable std::mutex mutex_;
 
   // State tracking progress since our last Dump(). These are mutable since
   // we need to reset them on every Dump() call.
   mutable uint64_t dump_frames_composited_;
   mutable uint64_t dump_last_timestamp_ns_;
+  mutable uint64_t max_duration_us;
 };
 }
 
diff --git a/drmeventlistener.cpp b/drmeventlistener.cpp
index 0514aa6..7a21980 100644
--- a/drmeventlistener.cpp
+++ b/drmeventlistener.cpp
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
+#include <assert.h>
+
 #define LOG_TAG "hwc-drm-event-listener"
 
 #include "drmeventlistener.h"
 #include "drmresources.h"
 
-#include <assert.h>
 #include <linux/netlink.h>
 #include <sys/socket.h>
 
+#include <assert.h>
 #include <cutils/log.h>
 #include <xf86drm.h>
 
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index c0aafef..875056d 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -33,7 +33,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <pthread.h>
 #include <sys/param.h>
 #include <sys/resource.h>
 #include <xf86drm.h>
diff --git a/queue_worker.h b/queue_worker.h
new file mode 100644
index 0000000..7e96eec
--- /dev/null
+++ b/queue_worker.h
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_QUEUE_WORKER_H_
+#define ANDROID_QUEUE_WORKER_H_
+
+#include "worker.h"
+
+#include <queue>
+
+namespace android {
+
+template <typename T>
+class QueueWorker : public Worker {
+ public:
+  static const size_t kDefaultMaxQueueSize = 2;
+  static const int64_t kTimeoutDisabled = -1;
+
+  QueueWorker(const char *name, int priority)
+      : Worker(name, priority),
+        max_queue_size_(kDefaultMaxQueueSize),
+        queue_timeout_ms_(kTimeoutDisabled),
+        idle_timeout_ms_(kTimeoutDisabled),
+        idled_out_(false) {
+  }
+
+  int QueueWork(std::unique_ptr<T> workitem);
+
+  bool IsWorkPending() const {
+    return !queue_.empty();
+  }
+  bool idle() const {
+    return idled_out_;
+  }
+
+  int64_t idle_timeout() {
+    return idle_timeout_ms_;
+  }
+  void set_idle_timeout(int64_t timeout_ms) {
+    idle_timeout_ms_ = timeout_ms;
+  }
+
+  int64_t queue_timeout() {
+    return queue_timeout_ms_;
+  }
+  void set_queue_timeout(int64_t timeout_ms) {
+    queue_timeout_ms_ = timeout_ms;
+  }
+
+  size_t max_queue_size() const {
+    return max_queue_size_;
+  }
+  void set_max_queue_size(size_t size) {
+    max_queue_size_ = size;
+  }
+
+ protected:
+  virtual void ProcessWork(std::unique_ptr<T> workitem) = 0;
+  virtual void ProcessIdle(){}
+  virtual void Routine();
+
+  template <typename Predicate>
+  int WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred,
+               int64_t max_msecs);
+
+ private:
+  std::queue<std::unique_ptr<T>> queue_;
+  size_t max_queue_size_;
+  int64_t queue_timeout_ms_;
+  int64_t idle_timeout_ms_;
+  bool idled_out_;
+};
+
+template <typename T>
+template <typename Predicate>
+int QueueWorker<T>::WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred,
+                             int64_t max_msecs) {
+  bool ret = true;
+  auto wait_func = [&] { return pred() || should_exit(); };
+
+  if (max_msecs < 0) {
+    cond_.wait(lock, wait_func);
+  } else {
+    auto timeout = std::chrono::milliseconds(max_msecs);
+    ret = cond_.wait_for(lock, timeout, wait_func);
+  }
+
+  if (!ret)
+    return -ETIMEDOUT;
+  else if (should_exit())
+    return -EINTR;
+
+  return 0;
+}
+
+template <typename T>
+void QueueWorker<T>::Routine() {
+  std::unique_lock<std::mutex> lk(mutex_);
+  std::unique_ptr<T> workitem;
+
+  auto wait_func = [&] { return !queue_.empty(); };
+  int ret =
+      WaitCond(lk, wait_func, idled_out_ ? kTimeoutDisabled : idle_timeout_ms_);
+  switch (ret) {
+    case 0:
+      break;
+    case -ETIMEDOUT:
+      ProcessIdle();
+      idled_out_ = true;
+      return;
+    case -EINTR:
+    default:
+      return;
+  }
+
+  if (!queue_.empty()) {
+    workitem = std::move(queue_.front());
+    queue_.pop();
+  }
+  lk.unlock();
+  cond_.notify_all();
+
+  idled_out_ = false;
+  ProcessWork(std::move(workitem));
+}
+
+template <typename T>
+int QueueWorker<T>::QueueWork(std::unique_ptr<T> workitem) {
+  std::unique_lock<std::mutex> lk(mutex_);
+
+  auto wait_func = [&] { return queue_.size() < max_queue_size_; };
+  int ret = WaitCond(lk, wait_func, queue_timeout_ms_);
+  if (ret)
+    return ret;
+
+  queue_.push(std::move(workitem));
+  lk.unlock();
+
+  cond_.notify_one();
+
+  return 0;
+}
+};
+#endif
diff --git a/tests/Android.mk b/tests/Android.mk
index 5bbda93..b86cca6 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -3,6 +3,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
+	queue_worker_test.cpp \
 	worker_test.cpp
 
 LOCAL_MODULE := hwc-drm-tests
diff --git a/tests/queue_worker_test.cpp b/tests/queue_worker_test.cpp
new file mode 100644
index 0000000..d1c0470
--- /dev/null
+++ b/tests/queue_worker_test.cpp
@@ -0,0 +1,201 @@
+#include <gtest/gtest.h>
+#include <hardware/hardware.h>
+
+#include <chrono>
+#include <mutex>
+
+#include "queue_worker.h"
+
+using android::QueueWorker;
+
+#define UNUSED_ARG(x) (void)(x)
+
+struct TestData {
+  TestData(int val) : value(val) {
+  }
+  virtual ~TestData() {
+  }
+
+  virtual void CheckValue(int prev_value) {
+    ASSERT_EQ(prev_value + 1, value);
+  }
+
+  int value;
+};
+
+struct TestQueueWorker : public QueueWorker<TestData> {
+  TestQueueWorker()
+      : QueueWorker("test-queueworker", HAL_PRIORITY_URGENT_DISPLAY), value(0) {
+  }
+
+  int Init() {
+    return InitWorker();
+  }
+
+  void ProcessWork(std::unique_ptr<TestData> data) {
+    std::lock_guard<std::mutex> blk(block);
+    data->CheckValue(value);
+    {
+      std::lock_guard<std::mutex> lk(lock);
+      value = data->value;
+    }
+    cond.notify_one();
+  }
+
+  void ProcessIdle() {
+    ASSERT_FALSE(idle());
+  }
+
+  std::mutex lock;
+  std::mutex block;
+  std::condition_variable cond;
+  int value;
+};
+
+struct QueueWorkerTest : public testing::Test {
+  static const int kTimeoutMs = 1000;
+  TestQueueWorker qw;
+
+  virtual void SetUp() {
+    qw.Init();
+  }
+  bool QueueValue(int val) {
+    std::unique_ptr<TestData> data(new TestData(val));
+    return !qw.QueueWork(std::move(data));
+  }
+
+  bool WaitFor(int val, int timeout_ms = kTimeoutMs) {
+    std::unique_lock<std::mutex> lk(qw.lock);
+
+    auto timeout = std::chrono::milliseconds(timeout_ms);
+    return qw.cond.wait_for(lk, timeout, [&] { return qw.value == val; });
+  }
+};
+
+struct IdleQueueWorkerTest : public QueueWorkerTest {
+  const int64_t kIdleTimeoutMs = 100;
+
+  virtual void SetUp() {
+    qw.set_idle_timeout(kIdleTimeoutMs);
+    qw.Init();
+  }
+};
+
+TEST_F(QueueWorkerTest, single_queue) {
+  // already isInitialized so should fail
+  ASSERT_NE(qw.Init(), 0);
+
+  ASSERT_EQ(qw.value, 0);
+  ASSERT_TRUE(QueueValue(1));
+  ASSERT_TRUE(WaitFor(1));
+  ASSERT_EQ(qw.value, 1);
+  ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, multiple_waits) {
+  for (int i = 1; i <= 100; i++) {
+    ASSERT_TRUE(QueueValue(i));
+    ASSERT_TRUE(WaitFor(i));
+    ASSERT_EQ(qw.value, i);
+    ASSERT_FALSE(qw.IsWorkPending());
+  }
+}
+
+TEST_F(QueueWorkerTest, multiple_queue) {
+  for (int i = 1; i <= 100; i++) {
+    ASSERT_TRUE(QueueValue(i));
+  }
+  ASSERT_TRUE(WaitFor(100));
+  ASSERT_EQ(qw.value, 100);
+  ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, blocking) {
+  // First wait for inital value to be setup
+  ASSERT_TRUE(QueueValue(1));
+  ASSERT_TRUE(WaitFor(1));
+
+  // Block processing and fill up the queue
+  std::unique_lock<std::mutex> lk(qw.block);
+  size_t expected_value = qw.max_queue_size() + 2;
+  for (size_t i = 2; i <= expected_value; i++) {
+    ASSERT_TRUE(QueueValue(i));
+  }
+
+  qw.set_queue_timeout(100);
+  // any additional queueing should fail
+  ASSERT_FALSE(QueueValue(expected_value + 1));
+
+  // make sure value is not changed while blocked
+  {
+    std::unique_lock<std::mutex> lock(qw.lock);
+    auto timeout = std::chrono::milliseconds(100);
+    ASSERT_FALSE(
+        qw.cond.wait_for(lock, timeout, [&] { return qw.value != 1; }));
+  }
+  ASSERT_EQ(qw.value, 1);
+  ASSERT_TRUE(qw.IsWorkPending());
+
+  // unblock and wait for value to be reached
+  lk.unlock();
+  ASSERT_TRUE(WaitFor(expected_value));
+  ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, exit_slow) {
+  struct SlowData : public TestData {
+    SlowData(int val) : TestData(val) {
+    }
+    void CheckValue(int prev_value) {
+      UNUSED_ARG(prev_value);
+
+      std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+  };
+  std::unique_ptr<SlowData> data(new SlowData(1));
+  ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
+  data = std::unique_ptr<SlowData>(new SlowData(2));
+  ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
+  qw.Exit();
+  ASSERT_FALSE(qw.initialized());
+}
+
+TEST_F(QueueWorkerTest, exit_empty) {
+  qw.Exit();
+  ASSERT_FALSE(qw.initialized());
+}
+
+TEST_F(QueueWorkerTest, queue_worker_noidling) {
+  ASSERT_TRUE(QueueValue(1));
+  ASSERT_TRUE(WaitFor(1));
+
+  ASSERT_FALSE(qw.idle());
+  auto timeout = std::chrono::milliseconds(200);
+  std::this_thread::sleep_for(timeout);
+  ASSERT_FALSE(qw.idle());
+}
+
+TEST_F(IdleQueueWorkerTest, queue_worker_idling) {
+  ASSERT_TRUE(QueueValue(1));
+  ASSERT_TRUE(WaitFor(1));
+  ASSERT_FALSE(qw.idle());
+
+  auto timeout = std::chrono::milliseconds(kIdleTimeoutMs + 10);
+  std::this_thread::sleep_for(timeout);
+  ASSERT_TRUE(qw.idle());
+  ASSERT_TRUE(QueueValue(2));
+  ASSERT_TRUE(WaitFor(2));
+  ASSERT_FALSE(qw.idle());
+
+  std::this_thread::sleep_for(3 * timeout);
+  ASSERT_TRUE(qw.idle());
+
+  ASSERT_TRUE(QueueValue(3));
+  ASSERT_TRUE(WaitFor(3));
+  for (int i = 4; i <= 100; i++) {
+    QueueValue(i);
+  }
+  ASSERT_FALSE(qw.idle());
+  qw.Exit();
+  ASSERT_FALSE(qw.initialized());
+}
\ No newline at end of file
diff --git a/virtualcompositorworker.cpp b/virtualcompositorworker.cpp
index 639dc86..c1a6d2f 100644
--- a/virtualcompositorworker.cpp
+++ b/virtualcompositorworker.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015-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.
@@ -17,15 +17,8 @@
 #define LOG_TAG "hwc-virtual-compositor-worker"
 
 #include "virtualcompositorworker.h"
-#include "worker.h"
-
-#include <errno.h>
-#include <stdlib.h>
 
 #include <cutils/log.h>
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-#include <sched.h>
 #include <sw_sync.h>
 #include <sync/sync.h>
 
@@ -35,7 +28,7 @@
 static const int kAcquireWaitTimeoutMs = 3000;
 
 VirtualCompositorWorker::VirtualCompositorWorker()
-    : Worker("virtual-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+    : QueueWorker("virtual-compositor", HAL_PRIORITY_URGENT_DISPLAY),
       timeline_fd_(-1),
       timeline_(0),
       timeline_current_(0) {
@@ -56,6 +49,8 @@
     return ret;
   }
   timeline_fd_ = ret;
+
+  set_max_queue_size(kMaxQueueDepth);
   return InitWorker();
 }
 
@@ -81,41 +76,7 @@
 
   composition->release_timeline = timeline_;
 
-  Lock();
-  while (composite_queue_.size() >= kMaxQueueDepth) {
-    Unlock();
-    sched_yield();
-    Lock();
-  }
-
-  composite_queue_.push(std::move(composition));
-  Unlock();
-  Signal();
-}
-
-void VirtualCompositorWorker::Routine() {
-  int wait_ret = 0;
-
-  Lock();
-  if (composite_queue_.empty()) {
-    wait_ret = WaitForSignalOrExitLocked();
-  }
-
-  std::unique_ptr<VirtualComposition> composition;
-  if (!composite_queue_.empty()) {
-    composition = std::move(composite_queue_.front());
-    composite_queue_.pop();
-  }
-  Unlock();
-
-  if (wait_ret == -EINTR) {
-    return;
-  } else if (wait_ret) {
-    ALOGE("Failed to wait for signal, %d", wait_ret);
-    return;
-  }
-
-  Compose(std::move(composition));
+  QueueWork(std::move(composition));
 }
 
 int VirtualCompositorWorker::CreateNextTimelineFence() {
@@ -135,7 +96,7 @@
   return ret;
 }
 
-void VirtualCompositorWorker::Compose(
+void VirtualCompositorWorker::ProcessWork(
     std::unique_ptr<VirtualComposition> composition) {
   if (!composition.get())
     return;
diff --git a/virtualcompositorworker.h b/virtualcompositorworker.h
index 1fc5e43..885cf31 100644
--- a/virtualcompositorworker.h
+++ b/virtualcompositorworker.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015-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.
@@ -18,13 +18,17 @@
 #define ANDROID_VIRTUAL_COMPOSITOR_WORKER_H_
 
 #include "drmhwcomposer.h"
-#include "worker.h"
-
-#include <queue>
+#include "queue_worker.h"
 
 namespace android {
 
-class VirtualCompositorWorker : public Worker {
+struct VirtualComposition {
+  UniqueFd outbuf_acquire_fence;
+  std::vector<UniqueFd> layer_acquire_fences;
+  int release_timeline;
+};
+
+class VirtualCompositorWorker : public QueueWorker<VirtualComposition> {
  public:
   VirtualCompositorWorker();
   ~VirtualCompositorWorker() override;
@@ -33,20 +37,12 @@
   void QueueComposite(hwc_display_contents_1_t *dc);
 
  protected:
-  void Routine() override;
+  void ProcessWork(std::unique_ptr<VirtualComposition> composition);
 
  private:
-  struct VirtualComposition {
-    UniqueFd outbuf_acquire_fence;
-    std::vector<UniqueFd> layer_acquire_fences;
-    int release_timeline;
-  };
-
   int CreateNextTimelineFence();
   int FinishComposition(int timeline);
-  void Compose(std::unique_ptr<VirtualComposition> composition);
 
-  std::queue<std::unique_ptr<VirtualComposition>> composite_queue_;
   int timeline_fd_;
   int timeline_;
   int timeline_current_;
diff --git a/vsyncworker.cpp b/vsyncworker.cpp
index 3ad16fe..2177521 100644
--- a/vsyncworker.cpp
+++ b/vsyncworker.cpp
@@ -126,6 +126,7 @@
   bool enabled = enabled_;
   int display = display_;
   std::shared_ptr<VsyncCallback> callback(callback_);
+
   Unlock();
 
   if (!enabled)