Create thread in passthrough objects lazily.

Also, moved SynchronizedQueue to not be exported
becaue it's not used outside of the library.

Notice, it would be cleaner to add variables to TaskRunner
and then use those to determine if it's initialized or not.
However, the ABI of TaskRunner is tied up in HIDL clients.
Perhaps in the future, we can consider renaming
SynchronizedQueue to be called TaskRunnerImpl.

Bug: 38039413
Test: boot + hidl_test

Change-Id: Id5dbffe0196447c7798ba89df0ff8c69b6796210
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);