ConditionalWait to replace boost conditional variable usage 17/42617/2
authorKimmo Hoikka <kimmo.hoikka@samsung.com>
Tue, 30 Jun 2015 15:00:41 +0000 (16:00 +0100)
committerKimmo Hoikka <kimmo.hoikka@samsung.com>
Wed, 1 Jul 2015 09:58:23 +0000 (10:58 +0100)
Change-Id: I337a7c0577056bf40e4a3cddd1cc100f6d5af7af

adaptors/base/conditional-wait.cpp [new file with mode: 0644]
adaptors/base/conditional-wait.h [new file with mode: 0644]
adaptors/base/file.list
automated-tests/src/dali-adaptor-internal/CMakeLists.txt
automated-tests/src/dali-adaptor-internal/utc-ConditionalWait.cpp [new file with mode: 0644]
automated-tests/src/dali-adaptor/dali-test-suite-utils/dali-test-suite-utils.cpp
automated-tests/src/dali-adaptor/dali-test-suite-utils/dali-test-suite-utils.h
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-harness.cpp
build/tizen/adaptor/Makefile.am

diff --git a/adaptors/base/conditional-wait.cpp b/adaptors/base/conditional-wait.cpp
new file mode 100644 (file)
index 0000000..cb0b16c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015 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.
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include "conditional-wait.h"
+
+// EXTERNAL INCLUDES
+#include <pthread.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace
+{
+} // unnamed namespace
+
+struct ConditionalWait::ConditionalWaitImpl
+{
+  pthread_mutex_t mutex;
+  pthread_cond_t condition;
+  volatile bool wait;
+};
+
+ConditionalWait::ConditionalWait()
+: mImpl( new ConditionalWaitImpl )
+{
+  pthread_mutex_init( &mImpl->mutex, NULL );
+  pthread_cond_init( &mImpl->condition, NULL );
+  mImpl->wait = false;
+}
+
+ConditionalWait::~ConditionalWait()
+{
+  pthread_cond_destroy( &mImpl->condition );
+  pthread_mutex_destroy( &mImpl->mutex );
+  delete mImpl;
+}
+
+void ConditionalWait::Notify()
+{
+  // pthread_cond_wait requires a lock to be held
+  pthread_mutex_lock( &mImpl->mutex );
+  bool wasWaiting = mImpl->wait;
+  mImpl->wait = false;
+  pthread_mutex_unlock( &mImpl->mutex );
+  // broadcast does nothing if the thread is not waiting but still has a system call overhead
+  // broadcast all threads to continue
+  if( wasWaiting )
+  {
+    pthread_cond_broadcast( &mImpl->condition );
+  }
+}
+
+void ConditionalWait::Wait()
+{
+  // pthread_cond_wait requires a lock to be held
+  pthread_mutex_lock( &mImpl->mutex );
+  mImpl->wait = true;
+  // pthread_cond_wait may wake up without anyone calling Notify
+  while( mImpl->wait )
+  {
+    // wait while condition changes
+    pthread_cond_wait( &mImpl->condition, &mImpl->mutex ); // releases the lock whilst waiting
+  }
+  // when condition returns the mutex is locked so release the lock
+  pthread_mutex_unlock( &mImpl->mutex );
+}
+
+bool ConditionalWait::IsWaiting() const
+{
+  bool isWaiting( false );
+  pthread_mutex_lock( &mImpl->mutex );
+  isWaiting = mImpl->wait;
+  pthread_mutex_unlock( &mImpl->mutex );
+  return isWaiting;
+}
+
+
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/adaptors/base/conditional-wait.h b/adaptors/base/conditional-wait.h
new file mode 100644 (file)
index 0000000..3638bfa
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef __DALI_INTERNAL_CONDITIONAL_WAIT_H__
+#define __DALI_INTERNAL_CONDITIONAL_WAIT_H__
+
+/*
+ * Copyright (c) 2015 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.
+ * 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.
+ *
+ */
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * Helper class to allow conditional waiting and notifications between multiple threads
+ */
+class ConditionalWait
+{
+public:
+
+  /**
+   * @brief Constructor, creates the internal synchronization objects
+   */
+  ConditionalWait();
+
+  /**
+   * @brief Destructor, non-virtual as this is not a base class
+   */
+  ~ConditionalWait();
+
+  /**
+   * @brief Notifies another thread to continue if it is blocked on a wait.
+   *
+   * Can be called from any thread.
+   * Does not block the current thread but may cause a rescheduling of threads.
+   */
+  void Notify();
+
+  /**
+   * @brief Wait for another thread to notify us when the condition is true and we can continue
+   *
+   * Will always block current thread until Notify is called
+   */
+  void Wait();
+
+  /**
+   * @brief Return true if the wait is locked, i.e. someone is waiting for it
+   * @return true if this object is waiting on a thread
+   */
+  bool IsWaiting() const;
+
+private:
+
+  /// Not implemented as ConditionalWait is not copyable
+  ConditionalWait( const ConditionalWait& );
+  const ConditionalWait& operator= ( const ConditionalWait& );
+
+  struct ConditionalWaitImpl;
+  ConditionalWaitImpl* mImpl;
+
+};
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_CONDITIONAL_WAIT_H__
index de1452d..f787a18 100644 (file)
@@ -1,6 +1,7 @@
 # Add local source files here
 
 base_adaptor_src_files = \
+  $(base_adaptor_src_dir)/conditional-wait.cpp \
   $(base_adaptor_src_dir)/frame-time.cpp \
   $(base_adaptor_src_dir)/display-connection.cpp \
   $(base_adaptor_src_dir)/render-thread.cpp \
index fc8854c..f35aeb7 100644 (file)
@@ -6,11 +6,12 @@ SET(RPM_NAME "core-${PKG_NAME}-tests")
 SET(CAPI_LIB "dali-adaptor-internal")
 
 SET(TC_SOURCES
-    utc-Dali-GifLoader.cpp
-    utc-Dali-TiltSensor.cpp
+    utc-ConditionalWait.cpp
     utc-Dali-CommandLineOptions.cpp
-    utc-Dali-Lifecycle-Controller.cpp
+    utc-Dali-GifLoader.cpp
     utc-Dali-ImageOperations.cpp
+    utc-Dali-Lifecycle-Controller.cpp
+    utc-Dali-TiltSensor.cpp
 )
 
 LIST(APPEND TC_SOURCES
@@ -65,6 +66,7 @@ INCLUDE_DIRECTORIES(
 ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES})
 TARGET_LINK_LIBRARIES(${EXEC_NAME}
     ${${CAPI_LIB}_LIBRARIES}
+    -lpthread
 )
 
 INSTALL(PROGRAMS ${EXEC_NAME}
diff --git a/automated-tests/src/dali-adaptor-internal/utc-ConditionalWait.cpp b/automated-tests/src/dali-adaptor-internal/utc-ConditionalWait.cpp
new file mode 100644 (file)
index 0000000..85abcf6
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2015 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.
+ * 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 <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dali-test-suite-utils.h>
+#include "conditional-wait.h"
+
+using Dali::Internal::Adaptor::ConditionalWait;
+
+namespace // for local variables to avoid name clashes
+{
+volatile int gGlobalValue = 0;
+volatile bool gWorkerThreadWait = true;
+enum ThreadState { INIT, RUN, TERMINATE } volatile gWorkerThreadState = INIT;
+ConditionalWait* volatile gConditionalWait; // volatile pointer to a ConditionalWait object
+}
+
+void* WorkerThreadNotify( void* ptr )
+{
+  gGlobalValue = -1;
+  while( gWorkerThreadWait ) // wait till we can exit
+  {
+    gWorkerThreadState = RUN;
+    usleep( 1 ); // 1 microseconds
+  }
+  usleep( 1000 ); // 1000 microseconds to give other thread time to do its thing
+  gConditionalWait->Notify();
+  gWorkerThreadState = TERMINATE;
+  return NULL;
+}
+
+int UtcConditionalWait1P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario 1:  wait - notify from separate thread");
+
+  pthread_t thread1;
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+  gWorkerThreadWait = true;
+  DALI_TEST_EQUALS( INIT, gWorkerThreadState, TEST_LOCATION );
+  DALI_TEST_EQUALS( 0, gGlobalValue, TEST_LOCATION );
+
+  pthread_create( &thread1, NULL, &WorkerThreadNotify, NULL );
+  // wait till the thread is in run state
+  while( RUN != gWorkerThreadState )
+  {
+    usleep( 1 ); // 1 microsecond
+  }
+  // let worker continue and finish
+  gWorkerThreadWait = false;
+  gConditionalWait->Wait();
+
+  // wait till the thread is terminated state
+  while( TERMINATE != gWorkerThreadState )
+  {
+    usleep( 1 ); // 1 microsecond
+  }
+
+  void* exitValue;
+  pthread_join( thread1, &exitValue );
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
+int UtcConditionalWait2P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario 2:  notify without wait");
+
+  ConditionalWait wait;
+  DALI_TEST_EQUALS( false, wait.IsWaiting(), TEST_LOCATION );
+  wait.Notify();
+  DALI_TEST_EQUALS( false, wait.IsWaiting(), TEST_LOCATION );
+
+  END_TEST;
+}
+
+volatile unsigned int gNotifyCount = 0;
+void* WorkerThreadNotifyN( void* ptr )
+{
+  while( gNotifyCount )
+  {
+    gConditionalWait->Notify();
+    usleep( 10 ); // 10 microseconds between each notify
+  }
+  return NULL;
+}
+
+int UtcConditionalWait3P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario 1:  wait - notify N times");
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+  gNotifyCount = 100;
+
+  pthread_t thread1;
+  pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL );
+
+  while( gNotifyCount )
+  {
+    gConditionalWait->Wait();
+    --gNotifyCount;
+    DALI_TEST_EQUALS( false, gConditionalWait->IsWaiting(), TEST_LOCATION );
+    usleep( 10 ); // 10 microseconds between each notify
+  }
+  DALI_TEST_EQUALS( false, gConditionalWait->IsWaiting(), TEST_LOCATION );
+
+  void* exitValue;
+  pthread_join( thread1, &exitValue );
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
+int UtcConditionalWait4P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario 1:  wait - notify N times from multiple threads");
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+  gNotifyCount = 100;
+
+  pthread_t thread1;
+  pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL );
+
+  pthread_t thread2;
+  pthread_create( &thread2, NULL, &WorkerThreadNotifyN, NULL );
+
+  pthread_t thread3;
+  pthread_create( &thread3, NULL, &WorkerThreadNotifyN, NULL );
+
+  while( gNotifyCount )
+  {
+    gConditionalWait->Wait();
+    --gNotifyCount;
+    DALI_TEST_EQUALS( false, gConditionalWait->IsWaiting(), TEST_LOCATION );
+    usleep( 10 ); // 10 microseconds between each notify
+  }
+
+  void* exitValue;
+  pthread_join( thread1, &exitValue );
+  pthread_join( thread2, &exitValue );
+  pthread_join( thread3, &exitValue );
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
+
+int UtcConditionalWaitNonCopyable(void)
+{
+  // we want to make sure that ConditionalWait is not copyable (its copy constructor is not defined)
+  // this test will stop compiling if ConditionalWait has compiler generated copy constructor
+  DALI_COMPILE_TIME_ASSERT( !__has_trivial_copy( ConditionalWait ) );
+
+  DALI_TEST_CHECK( true );
+  END_TEST;
+}
+
+
index 9137f3d..f2cf564 100644 (file)
@@ -19,7 +19,6 @@
 #include "dali-test-suite-utils.h"
 
 // EXTERNAL INCLUDES
-#include <ostream>
 #include <cstdio>
 #include <cstdarg>
 
@@ -102,7 +101,7 @@ void DALI_TEST_EQUALS( const Matrix3& matrix1, const Matrix3& matrix2, const cha
 
   for (int i=0;i<9;++i)
   {
-    equivalent &= (m1[i] != m2[i]);
+    equivalent &= (fabsf(m1[i] - m2[i])< GetRangedEpsilon(m1[i], m2[i]));
   }
 
   if (!equivalent)
@@ -244,47 +243,6 @@ void DALI_TEST_EQUALS( const char* str1, const std::string &str2, const char* lo
   DALI_TEST_EQUALS(str1, str2.c_str(), location);
 }
 
-
-/**
- * Test whether one unsigned integer value is greater than another.
- * Test succeeds if value1 > value2
- * @param[in] value1 The first value
- * @param[in] value2 The second value
- * @param[in] location The TEST_LOCATION macro should be used here
- */
-void DALI_TEST_GREATER(unsigned int value1, unsigned int value2, const char* location)
-{
-  if (!(value1 > value2))
-  {
-    fprintf(stderr, "%s, checking %d > %d\n", location, value1, value2);
-    tet_result(TET_FAIL);
-  }
-  else
-  {
-    tet_result(TET_PASS);
-  }
-}
-
-/**
- * Test whether one float value is greater than another.
- * Test succeeds if value1 > value2
- * @param[in] value1 The first value
- * @param[in] value2 The second value
- * @param[in] location The TEST_LOCATION macro should be used here
- */
-void DALI_TEST_GREATER( float value1, float value2, const char* location)
-{
-  if (!(value1 > value2))
-  {
-    fprintf(stderr, "%s, checking %f > %f\n", location, value1, value2);
-    tet_result(TET_FAIL);
-  }
-  else
-  {
-    tet_result(TET_PASS);
-  }
-}
-
 void DALI_TEST_ASSERT( DaliException& e, std::string conditionSubString, const char* location )
 {
   if( NULL == strstr( e.condition, conditionSubString.c_str() ) )
index 37f698c..8163028 100644 (file)
@@ -20,7 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <cstdarg>
-#include <iosfwd>
+#include <iostream>
 
 // INTERNAL INCLUDES
 #include <dali/public-api/dali-core.h>
@@ -307,16 +307,19 @@ void DALI_TEST_EQUALS( const char* str1, const std::string &str2, const char* lo
  * @param[in] value2 The second value
  * @param[in] location The TEST_LOCATION macro should be used here
  */
-void DALI_TEST_GREATER(unsigned int value1, unsigned int value2, const char* location);
-
-/**
- * Test whether one float value is greater than another.
- * Test succeeds if value1 > value2
- * @param[in] value1 The first value
- * @param[in] value2 The second value
- * @param[in] location The TEST_LOCATION macro should be used here
- */
-void DALI_TEST_GREATER( float value1, float value2, const char* location);
+template< typename T >
+void DALI_TEST_GREATER(unsigned int value1, unsigned int value2, const char* location)
+{
+  if (!(value1 > value2))
+  {
+    std::cerr << location << ", checking " << value1 <<" > " << value2 << "\n";
+    tet_result(TET_FAIL);
+  }
+  else
+  {
+    tet_result(TET_PASS);
+  }
+}
 
 /**
  * Test whether the assertion condition that failed and thus triggered the
index 3fed0a2..475b62a 100644 (file)
@@ -42,27 +42,23 @@ int RunTestCase( struct ::testcase_s& testCase )
 {
   int result = EXIT_STATUS_TESTCASE_FAILED;
 
-  try
+// dont want to catch exception as we want to be able to get
+// gdb stack trace from the first error
+// by default tests should all always pass with no exceptions
+  if( testCase.startup )
   {
-    if( testCase.startup )
-    {
-      testCase.startup();
-    }
-    result = testCase.function();
-    if( testCase.cleanup )
-    {
-      testCase.cleanup();
-    }
+    testCase.startup();
   }
-  catch (...)
+  result = testCase.function();
+  if( testCase.cleanup )
   {
-    printf("Caught exception in test case.\n");
-    result = EXIT_STATUS_TESTCASE_ABORTED;
+    testCase.cleanup();
   }
 
   return result;
 }
 
+
 int RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput )
 {
   int testResult = EXIT_STATUS_TESTCASE_FAILED;
index beb39b2..f39980d 100644 (file)
@@ -291,6 +291,7 @@ libdali_adaptor_la_LIBADD = \
                       $(CAPI_SYSTEM_INFO_LIBS) \
                       $(ELDBUS_LIBS) \
                       -lgif \
+                      -lpthread \
                       -lboost_thread \
                       -lturbojpeg