Yarn: Add blocking_call.

A solution to blocking, non-yielding function calls.

Bug: b/139010488
Change-Id: I8f2a9458980f83fbecafb22a5531aa6e3dbbc040
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35573
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Yarn/BlockingCall.hpp b/src/Yarn/BlockingCall.hpp
new file mode 100644
index 0000000..161bba9
--- /dev/null
+++ b/src/Yarn/BlockingCall.hpp
@@ -0,0 +1,85 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 "WaitGroup.hpp"
+
+#include <thread>
+#include <type_traits>
+
+namespace yarn {
+namespace detail {
+
+template <typename RETURN_TYPE>
+class OnNewThread
+{
+public:
+    template <typename F, typename ... Args>
+    inline static RETURN_TYPE call(F&& f, Args&& ... args)
+    {
+        RETURN_TYPE result;
+        WaitGroup wg(1);
+        auto thread = std::thread([&]
+        {
+            defer(wg.done());
+            result = f(args...);
+        });
+        wg.wait();
+        thread.join();
+        return result;
+    }
+};
+
+template <>
+class OnNewThread<void>
+{
+public:
+    template <typename F, typename ... Args>
+    inline static void call(F&& f, Args&& ... args)
+    {
+        WaitGroup wg(1);
+        auto thread = std::thread([&]
+        {
+            defer(wg.done());
+            f(args...);
+        });
+        wg.wait();
+        thread.join()
+    }
+};
+
+} // namespace detail
+
+// blocking_call() calls the function F on a new thread, yielding this fiber
+// to execute other tasks until F has returned.
+//
+// Example:
+//
+//  void runABlockingFunctionOnATask()
+//  {
+//      // Schedule a task that calls a blocking, non-yielding function.
+//      yarn::schedule([=] {
+//          // call_blocking_function() may block indefinitely.
+//          // Ensure this call does not block other tasks from running.
+//          auto result = yarn::blocking_call(call_blocking_function);
+//          // call_blocking_function() has now returned.
+//          // result holds the return value of the blocking function call.
+//      });
+//  }
+template <typename F, typename ... Args>
+auto inline blocking_call(F&& f, Args&& ... args) -> decltype(f(args...))
+{
+    return detail::OnNewThread<decltype(f(args...))>::call(std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+} // namespace yarn