Merge "Create thread in passthrough objects lazily."
am: 546ebf4c7d

Change-Id: Ib96c4bd2ff6fd55961f219b8f201a2fca29b020f
diff --git a/base/include/hidl/SynchronizedQueue.h b/base/SynchronizedQueue.h
similarity index 88%
rename from base/include/hidl/SynchronizedQueue.h
rename to base/SynchronizedQueue.h
index efb04e0..0ef9e4f 100644
--- a/base/include/hidl/SynchronizedQueue.h
+++ b/base/SynchronizedQueue.h
@@ -45,11 +45,23 @@
      */
     size_t size();
 
+    std::unique_lock<std::mutex> lock() {
+        return std::unique_lock<std::mutex>(mMutex);
+    }
+
+    bool isInitializedLocked() {
+        return mInitialized;
+    }
+    void setInitializedLocked(bool isInitialized) {
+        mInitialized = isInitialized;
+    }
+
 private:
     std::condition_variable mCondition;
     std::mutex mMutex;
     std::queue<T> mQueue;
     const size_t mQueueLimit;
+    bool mInitialized;
 };
 
 template <typename T>
diff --git a/base/TaskRunner.cpp b/base/TaskRunner.cpp
index 782b40b..d4357bf 100644
--- a/base/TaskRunner.cpp
+++ b/base/TaskRunner.cpp
@@ -15,6 +15,9 @@
  */
 
 #include <hidl/TaskRunner.h>
+
+#include "SynchronizedQueue.h"
+
 #include <thread>
 
 namespace android {
@@ -26,15 +29,6 @@
 
 void TaskRunner::start(size_t limit) {
     mQueue = std::make_shared<SynchronizedQueue<Task>>(limit);
-
-    // Allow the thread to continue running in background;
-    // TaskRunner do not care about the std::thread object.
-    std::thread{[q = mQueue] {
-        Task nextTask;
-        while (!!(nextTask = q->wait_pop())) {
-            nextTask();
-        }
-    }}.detach();
 }
 
 TaskRunner::~TaskRunner() {
@@ -44,7 +38,28 @@
 }
 
 bool TaskRunner::push(const Task &t) {
-    return (mQueue != nullptr) && (!!t) && this->mQueue->push(t);
+    if (mQueue == nullptr || !t) {
+        return false;
+    }
+
+    {
+        std::unique_lock<std::mutex> lock = mQueue->lock();
+
+        if (!mQueue->isInitializedLocked()) {
+            // Allow the thread to continue running in background;
+            // TaskRunner do not care about the std::thread object.
+            std::thread{[q = mQueue] {
+                Task nextTask;
+                while (!!(nextTask = q->wait_pop())) {
+                    nextTask();
+                }
+            }}.detach();
+
+            mQueue->setInitializedLocked(true);
+        }
+    }
+
+    return this->mQueue->push(t);
 }
 
 } // namespace details
diff --git a/base/include/hidl/TaskRunner.h b/base/include/hidl/TaskRunner.h
index 28ea01c..6a79ebf 100644
--- a/base/include/hidl/TaskRunner.h
+++ b/base/include/hidl/TaskRunner.h
@@ -16,7 +16,6 @@
 #ifndef ANDROID_HIDL_TASK_RUNNER_H
 #define ANDROID_HIDL_TASK_RUNNER_H
 
-#include "SynchronizedQueue.h"
 #include <memory>
 #include <thread>
 
@@ -24,13 +23,17 @@
 namespace hardware {
 namespace details {
 
+using Task = std::function<void(void)>;
+
+template <typename T>
+struct SynchronizedQueue;
+
 /*
  * A background infinite loop that runs the Tasks push()'ed.
  * Equivalent to a simple single-threaded Looper.
  */
 class TaskRunner {
 public:
-    using Task = std::function<void(void)>;
 
     /* Create an empty task runner. Nothing will be done until start() is called. */
     TaskRunner();
@@ -44,7 +47,9 @@
 
     /*
      * Sets the queue limit. Fails the push operation once the limit is reached.
-     * Then kicks off the loop.
+     * This function is named start for legacy reasons and to maintain ABI
+     * stability, but the underlying thread running tasks isn't started until
+     * the first task is pushed.
      */
     void start(size_t limit);