Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cdae31e authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "MediaMetrics:LockWrap: Use recursive mutex."

parents b96075bf 42b99304
Loading
Loading
Loading
Loading
+27 −12
Original line number Original line Diff line number Diff line
@@ -150,11 +150,17 @@ class LockWrap {
      */
      */
    class LockedPointer {
    class LockedPointer {
        friend LockWrap;
        friend LockWrap;
        LockedPointer(T *t, std::mutex *lock)
        LockedPointer(T *t, std::recursive_mutex *lock, std::atomic<size_t> *recursionDepth)
            : mT(t), mLock(*lock) {}
            : mT(t), mLock(*lock), mRecursionDepth(recursionDepth) { ++*mRecursionDepth; }

        T* const mT;
        T* const mT;
        std::lock_guard<std::mutex> mLock;
        std::lock_guard<std::recursive_mutex> mLock;
        std::atomic<size_t>* mRecursionDepth;
    public:
    public:
        ~LockedPointer() {
            --*mRecursionDepth; // Used for testing, we do not check underflow.
        }

        const T* operator->() const {
        const T* operator->() const {
            return mT;
            return mT;
        }
        }
@@ -163,27 +169,36 @@ class LockWrap {
        }
        }
    };
    };


    mutable std::mutex mLock;
    // We must use a recursive mutex because the end of the full expression may
    // involve another reference to T->.
    //
    // A recursive mutex allows the same thread to recursively acquire,
    // but different thread would block.
    //
    // Example which fails with a normal mutex:
    //
    // android::mediametrics::LockWrap<std::vector<int>> v{std::initializer_list<int>{1, 2}};
    // const int sum = v->operator[](0) + v->operator[](1);
    //
    mutable std::recursive_mutex mLock;
    mutable T mT;
    mutable T mT;
    mutable std::atomic<size_t> mRecursionDepth{};  // Used for testing.


public:
public:
    template <typename... Args>
    template <typename... Args>
    explicit LockWrap(Args&&... args) : mT(std::forward<Args>(args)...) {}
    explicit LockWrap(Args&&... args) : mT(std::forward<Args>(args)...) {}


    const LockedPointer operator->() const {
    const LockedPointer operator->() const {
        return LockedPointer(&mT, &mLock);
        return LockedPointer(&mT, &mLock, &mRecursionDepth);
    }
    }
    LockedPointer operator->() {
    LockedPointer operator->() {
        return LockedPointer(&mT, &mLock);
        return LockedPointer(&mT, &mLock, &mRecursionDepth);
    }
    }


    // Returns the lock depth of the recursive mutex.
    // @TestApi
    // @TestApi
    bool isLocked() const {
    size_t getRecursionDepth() const {
        if (mLock.try_lock()) {
        return mRecursionDepth;
            mLock.unlock();
            return false; // we were able to get the lock.
        }
        return true; // we were NOT able to get the lock.
    }
    }
};
};


+24 −1
Original line number Original line Diff line number Diff line
@@ -112,6 +112,27 @@ TEST(mediametrics_tests, lock_wrap) {
  s3->operator=("abc");
  s3->operator=("abc");
  ASSERT_EQ('b', s3->operator[](1)); // s2[1] = 'b';
  ASSERT_EQ('b', s3->operator[](1)); // s2[1] = 'b';


  // Check that we can recursively hold lock.
  android::mediametrics::LockWrap<std::vector<int>> v{std::initializer_list<int>{1, 2}};
  v->push_back(3);
  v->push_back(4);
  ASSERT_EQ(1, v->operator[](0));
  ASSERT_EQ(2, v->operator[](1));
  ASSERT_EQ(3, v->operator[](2));
  ASSERT_EQ(4, v->operator[](3));
  // The end of the full expression here requires recursive depth of 4.
  ASSERT_EQ(10, v->operator[](0) + v->operator[](1) + v->operator[](2) + v->operator[](3));

  // Mikhail's note: a non-recursive lock implementation could be used if one obtains
  // the LockedPointer helper object like this and directly hold the lock through RAII,
  // though it is trickier in use.
  //
  // We include an example here for completeness.
  {
    auto l = v.operator->();
    ASSERT_EQ(10, l->operator[](0) + l->operator[](1) + l->operator[](2) + l->operator[](3));
  }

  // Use Thunk to check whether we have the lock when calling a method through LockWrap.
  // Use Thunk to check whether we have the lock when calling a method through LockWrap.


  class Thunk {
  class Thunk {
@@ -123,11 +144,13 @@ TEST(mediametrics_tests, lock_wrap) {
  };
  };


  android::mediametrics::LockWrap<Thunk> s4([&]{
  android::mediametrics::LockWrap<Thunk> s4([&]{
    ASSERT_EQ(true, s4.isLocked()); // we must be locked when thunk() is called.
    ASSERT_EQ((size_t)1, s4.getRecursionDepth()); // we must be locked when thunk() is called.
  });
  });


  ASSERT_EQ((size_t)0, s4.getRecursionDepth());
  // This will fail if we are not locked during method access.
  // This will fail if we are not locked during method access.
  s4->thunk();
  s4->thunk();
  ASSERT_EQ((size_t)0, s4.getRecursionDepth());
}
}


TEST(mediametrics_tests, lock_wrap_multithread) {
TEST(mediametrics_tests, lock_wrap_multithread) {