Implmement ConditionalWait::WaitUntil 19/318019/4
authorEunki Hong <eunkiki.hong@samsung.com>
Mon, 23 Sep 2024 16:23:09 +0000 (01:23 +0900)
committerEunki Hong <eunkiki.hong@samsung.com>
Mon, 30 Sep 2024 06:37:41 +0000 (06:37 +0000)
Let we make wait_until feature for conditional wait for dali.

Change-Id: I502c794fd3e5027a907ce31535db545e9afb7222
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-ConditionalWait.cpp
dali/devel-api/threading/conditional-wait.cpp
dali/devel-api/threading/conditional-wait.h

index 71fe305a9b97403fc3408a35305885ad64681aaa..8641d2d4ce62b3b90dbdacd72f2d7bef38274c93 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -77,6 +77,29 @@ class WorkerThreadWaitN : public Thread
   }
 };
 
+class WorkerThreadWaitUntilN : public Thread
+{
+public:
+  WorkerThreadWaitUntilN()
+  : Thread()
+  {
+    auto now          = std::chrono::steady_clock::now();
+    auto waitDuration = std::chrono::milliseconds(100); // 0.1 second
+
+    mWaitUntilTimePoint = std::chrono::time_point_cast<ConditionalWait::TimePoint::duration>(now + waitDuration);
+  }
+
+  virtual void Run()
+  {
+    ConditionalWait::ScopedLock lock(*gConditionalWait);
+
+    gConditionalWait->WaitUntil(lock, mWaitUntilTimePoint);
+  }
+
+private:
+  ConditionalWait::TimePoint mWaitUntilTimePoint;
+};
+
 } // namespace
 
 int UtcConditionalWait1P(void)
@@ -254,6 +277,77 @@ int UtcConditionalWait6P(void)
   END_TEST;
 }
 
+int UtcConditionalWaitUntilP1(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario:  waituntil now + 100 ms - with 4 threads. No notify");
+
+  auto utcStartTime = std::chrono::steady_clock::now();
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+
+  WorkerThreadWaitUntilN thread1;
+  thread1.Start();
+  WorkerThreadWaitUntilN thread2;
+  thread2.Start();
+  WorkerThreadWaitUntilN thread3;
+  thread3.Start();
+  WorkerThreadWaitUntilN thread4;
+  thread4.Start();
+
+  // Let we check thread join well without any notification
+
+  thread1.Join();
+  thread2.Join();
+  thread3.Join();
+  thread4.Join();
+
+  DALI_TEST_EQUALS(0u, gConditionalWait->GetWaitCount(), TEST_LOCATION);
+
+  auto utcFinishedTime = std::chrono::steady_clock::now();
+
+  // Check thread join spened at least 100ms
+  auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(utcFinishedTime - utcStartTime).count();
+
+  DALI_TEST_GREATER(elapsedTime, static_cast<int64_t>(100 - 1), TEST_LOCATION); // 100ms is the minimum time for the threads to wait
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
+int UtcConditionalWaitUntilP2(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario:  waituntil now + 100 ms - notify with 4 threads");
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+
+  WorkerThreadWaitUntilN thread1;
+  thread1.Start();
+  WorkerThreadWaitUntilN thread2;
+  thread2.Start();
+  WorkerThreadWaitUntilN thread3;
+  thread3.Start();
+  WorkerThreadWaitUntilN thread4;
+  thread4.Start();
+
+  // Notify with scoped lock
+  {
+    ConditionalWait::ScopedLock lock(*gConditionalWait);
+    gConditionalWait->Notify(lock);
+  }
+
+  thread1.Join();
+  thread2.Join();
+  thread3.Join();
+  thread4.Join();
+
+  DALI_TEST_EQUALS(0u, gConditionalWait->GetWaitCount(), TEST_LOCATION);
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
 int UtcConditionalWaitNonCopyable(void)
 {
   // we want to make sure that ConditionalWait is not copyable (its copy constructor is not defined)
index 3259203648db462791fd0b416183c534d1e51957..3d6ec9bf7c683bcfdff8239e46d0d4b034f1cda9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ void ConditionalWait::Notify()
 void ConditionalWait::Notify(const ScopedLock& scope)
 {
   // Scope must be locked:
-  DALI_ASSERT_DEBUG(&scope.GetLockedWait() == this);
+  DALI_ASSERT_ALWAYS(&scope.GetLockedWait() == this);
 
   volatile unsigned int previousCount = mImpl->count;
 
@@ -127,7 +127,7 @@ void ConditionalWait::Wait()
 void ConditionalWait::Wait(const ScopedLock& scope)
 {
   // Scope must be locked:
-  DALI_ASSERT_DEBUG(&scope.GetLockedWait() == this);
+  DALI_ASSERT_ALWAYS(&scope.GetLockedWait() == this);
 
   ++(mImpl->count);
 
@@ -142,6 +142,32 @@ void ConditionalWait::Wait(const ScopedLock& scope)
   // passed in will unlock it in the caller.
 }
 
+void ConditionalWait::WaitUntil(const ScopedLock& scope, ConditionalWait::TimePoint timePoint)
+{
+  // Scope must be locked:
+  DALI_ASSERT_ALWAYS(&scope.GetLockedWait() == this);
+
+  ++(mImpl->count);
+
+  // conditional wait may wake up without anyone calling Notify
+  do
+  {
+    // wait while condition changes
+    if(mImpl->condition.wait_until(scope.mImpl->lock, timePoint) == std::cv_status::timeout)
+    {
+      // Pass-through for this wait request.
+      // Note that we should not wake-up other wait requests.
+      if(mImpl->count > 0u)
+      {
+        --mImpl->count;
+      }
+    }
+  } while(0 != mImpl->count);
+
+  // We return with our mutex locked safe in the knowledge that the ScopedLock
+  // passed in will unlock it in the caller.
+}
+
 unsigned int ConditionalWait::GetWaitCount() const
 {
   return mImpl->count;
index a18b93f5bae7ef125deadb346667d42ea1b8f57d..f0ea102df416111d6335305708618ed815264fc4 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_CONDITIONAL_WAIT_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@
  *
  */
 
+// EXTERNAL INCLUDES
+#include <chrono> ///< for std::chrono::time_point
+
 // INTERNAL INCLUDES
 #include <dali/public-api/common/dali-common.h>
 
@@ -65,6 +68,8 @@ public:
     const ScopedLock& operator=(const ScopedLock&);
   };
 
+  using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
   /**
    * @brief Constructor, creates the internal synchronization objects
    */
@@ -114,6 +119,19 @@ public:
    */
   void Wait(const ScopedLock& scope);
 
+  /**
+   * @brief Wait until specific time point or another thread to notify us when the condition is true and we can continue
+   *
+   * Will always block current thread until Notify is called.
+   * Assumes that the ScopedLock object passed in has already locked the internal state of
+   * this object. Releases the lock while waiting and re-acquires it when returning
+   * from the wait.
+   * @param[in] scope A pre-existing lock on the internal state of this object.
+   * @param[in] timePoint Maximum time point to wait.
+   * @pre scope must have been passed this ConditionalWait during its construction.
+   */
+  void WaitUntil(const ScopedLock& scope, TimePoint timePoint);
+
   /**
    * @brief Return the count of threads waiting for this conditional
    * @return count of waits