From 2e0f52d25a6e165f406a922c2ab657b43e315eae Mon Sep 17 00:00:00 2001
From: Maged Michael <magedmichael@fb.com>
Date: Sat, 25 Mar 2017 03:28:15 -0700
Subject: [PATCH] (folly) Add lock holder interface to flat combining. Change
 an assert to a static_assert.

Summary:
Two changes:
- Add a lock holder interface to enable users to move exclusive access and implicitly release the held lock.
- Change an assert to a static_assert to check at compile time if a provided function fits in the Function structure without dynamic allocation.

Reviewed By: djwatson

Differential Revision: D4770229

fbshipit-source-id: 89408164c08d7660231a6ca4e37637dd688356cd
---
 .../flat_combining/FlatCombining.h            | 22 +++++++++++++------
 .../test/FlatCombiningTestHelpers.h           | 16 +++++++++++++-
 2 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/folly/experimental/flat_combining/FlatCombining.h b/folly/experimental/flat_combining/FlatCombining.h
index 9c033503..445d29a3 100644
--- a/folly/experimental/flat_combining/FlatCombining.h
+++ b/folly/experimental/flat_combining/FlatCombining.h
@@ -197,15 +197,16 @@ class FlatCombining {
 
     template <typename Func>
     void setFn(Func&& fn) {
+      static_assert(
+          std::is_nothrow_constructible<
+              folly::Function<void()>,
+              _t<std::decay<Func>>>::value,
+          "Try using a smaller function object that can fit in folly::Function "
+          "without allocation, or use the custom interface of requestFC() to "
+          "manage the requested function's arguments and results explicitly "
+          "in a custom request structure without allocation.");
       fn_ = std::forward<Func>(fn);
       assert(fn_);
-      // If the following assertion is triggered, the user should
-      // either change the provided function, i.e., fn, to fit in
-      // folly::Function without allocation or use the custom
-      // interface to request combining for fn and manage its
-      // arguments (and results, if any) explicitly in a custom
-      // request structure.
-      assert(!fn_.hasAllocatedMemory());
     }
 
     void clearFn() {
@@ -276,6 +277,13 @@ class FlatCombining {
     m_.lock();
   }
 
+  // Give the caller exclusive access through a lock holder.
+  // No need for explicit release.
+  template <typename LockHolder>
+  void acquireExclusive(LockHolder& l) {
+    l = LockHolder(m_);
+  }
+
   // Try to give the caller exclusive access. Returns true iff successful.
   bool tryExclusive() {
     return m_.try_lock();
diff --git a/folly/experimental/flat_combining/test/FlatCombiningTestHelpers.h b/folly/experimental/flat_combining/test/FlatCombiningTestHelpers.h
index f9b4980d..07742e12 100644
--- a/folly/experimental/flat_combining/test/FlatCombiningTestHelpers.h
+++ b/folly/experimental/flat_combining/test/FlatCombiningTestHelpers.h
@@ -104,7 +104,21 @@ uint64_t fc_test(
       }
 
       if (excl) {
-        // test of unstructured exclusive access
+        // test of exclusive access through a lock holder
+        {
+          std::unique_lock<Mutex> l;
+          ex.acquireExclusive(l);
+          CHECK(!mutex);
+          mutex = true;
+          VLOG(2) << tid << " " << ex.getVal() << " ...........";
+          using namespace std::chrono_literals;
+          /* sleep override */ // for coverage
+          std::this_thread::sleep_for(10ms);
+          VLOG(2) << tid << " " << ex.getVal() << " ===========";
+          CHECK(mutex);
+          mutex = false;
+        }
+        // test of explicit acquisition and release of exclusive access
         ex.acquireExclusive();
         {
           CHECK(!mutex);
-- 
2.34.1