ConditionalWait moved to Core & add check for how many locks we're keeping 00/48000/4
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 10 Sep 2015 08:50:46 +0000 (09:50 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 11 Sep 2015 12:40:27 +0000 (13:40 +0100)
Option can be enabled via --enable-lock-backtrace which will show the backtrace for all locks
whenever more than 1 is held on a given thread.

Change-Id: Ib4b51f1167371922e57cee0e68909ac6bd83377f

automated-tests/src/dali-devel/CMakeLists.txt
automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp [new file with mode: 0644]
build/tizen/configure.ac
dali/devel-api/common/conditional-wait.cpp [new file with mode: 0644]
dali/devel-api/common/conditional-wait.h [new file with mode: 0644]
dali/devel-api/common/mutex.cpp
dali/devel-api/file.list
dali/internal/common/mutex-impl.cpp [new file with mode: 0644]
dali/internal/common/mutex-impl.h [new file with mode: 0644]
dali/internal/file.list

index dbd5168..96fa090 100644 (file)
@@ -8,6 +8,7 @@ SET(CAPI_LIB "dali-devel")
 SET(TC_SOURCES
         utc-Dali-Actor.cpp
         utc-Dali-Atlas.cpp
+        utc-Dali-ConditionalWait.cpp
         utc-Dali-Context.cpp
         utc-Dali-Constrainer.cpp
         utc-Dali-CullFace.cpp
diff --git a/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp b/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp
new file mode 100644 (file)
index 0000000..7946c8a
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * 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 <dali/devel-api/common/conditional-wait.h>
+
+using Dali::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( 200 ); // give other thread time to get to Wait
+  gGlobalValue = 1;
+  gConditionalWait->Notify();
+  gWorkerThreadState = TERMINATE;
+  return NULL;
+}
+
+int UtcConditionalWait1P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario:  wait - notify with 2 threads");
+
+  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();
+  DALI_TEST_EQUALS( 1, gGlobalValue, TEST_LOCATION );
+  DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION );
+
+  // 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: notify without wait");
+
+  ConditionalWait wait;
+  DALI_TEST_EQUALS( 0u, wait.GetWaitCount(), TEST_LOCATION );
+  wait.Notify();
+  DALI_TEST_EQUALS( 0u, wait.GetWaitCount(), TEST_LOCATION );
+
+  END_TEST;
+}
+
+volatile int gNotifyCount = 0;
+void* WorkerThreadNotifyN( void* ptr )
+{
+  while( gNotifyCount > 0 )
+  {
+    gConditionalWait->Notify();
+    usleep( 10 ); // 10 microseconds between each notify
+  }
+  return NULL;
+}
+
+int UtcConditionalWait3P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario: wait - notify N times 2 threads");
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+  gNotifyCount = 100;
+
+  pthread_t thread1;
+  pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL );
+
+  while( gNotifyCount > 0 )
+  {
+    gConditionalWait->Wait();
+    --gNotifyCount;
+    DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION );
+    usleep( 10 ); // 10 microseconds between each notify
+  }
+  DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION );
+
+  void* exitValue;
+  pthread_join( thread1, &exitValue );
+
+  delete gConditionalWait;
+  END_TEST;
+}
+
+int UtcConditionalWait4P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario:  wait - notify N times from 3 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 > 0 )
+  {
+    gConditionalWait->Wait();
+    --gNotifyCount;
+    DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), 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;
+}
+
+void* WorkerThreadWaitN( void* ptr )
+{
+  gConditionalWait->Wait();
+
+  return NULL;
+}
+
+int UtcConditionalWait5P(void)
+{
+  tet_infoline("Testing ConditionalWait - scenario:  4 threads wait - notify once from 1 thread");
+
+  // initialize values
+  gConditionalWait = new ConditionalWait();
+
+  pthread_t thread1;
+  pthread_create( &thread1, NULL, &WorkerThreadWaitN, NULL );
+  pthread_t thread2;
+  pthread_create( &thread2, NULL, &WorkerThreadWaitN, NULL );
+  pthread_t thread3;
+  pthread_create( &thread3, NULL, &WorkerThreadWaitN, NULL );
+  pthread_t thread4;
+  pthread_create( &thread4, NULL, &WorkerThreadWaitN, NULL );
+
+  // wait till all child threads are waiting
+  while( gConditionalWait->GetWaitCount() < 4 )
+  { }
+
+  // notify once, it will resume all threads
+  gConditionalWait->Notify();
+
+  void* exitValue;
+  pthread_join( thread1, &exitValue );
+  pthread_join( thread2, &exitValue );
+  pthread_join( thread3, &exitValue );
+  pthread_join( thread4, &exitValue );
+
+  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)
+  // 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 5560206..6398a82 100644 (file)
@@ -54,6 +54,12 @@ AC_ARG_ENABLE([backtrace],
               [enable_backtrace=$enableval],
               [enable_backtrace=yes])
 
+AC_ARG_ENABLE([lock_backtrace],
+              [AC_HELP_STRING([--enable-lock-backtrace],
+                              [Backtrace for when more than 1 lock is held on the same thread])],
+              [enable_lock_backtrace=$enableval],
+              [enable_lock_backtrace=no])
+
 if test "x$enable_debug" = "xyes"; then
   DALI_CFLAGS="$DALI_CFLAGS -DDEBUG_ENABLED"
 fi
@@ -66,9 +72,16 @@ if test "x$enable_emscripten" = "xyes"; then
   DALI_CFLAGS="$DALI_CFLAGS -DEMSCRIPTEN -std=c++11"
   # Automatically turn off backtrace support
   enable_backtrace="no"
+  enable_lock_backtrace="no"
 fi
 
 # Must come after Emscripten feature test
+if test "x$enable_lock_backtrace" = "xyes"; then
+  DALI_CFLAGS="$DALI_CFLAGS -DLOCK_BACKTRACE_ENABLED"
+  enable_backtrace="yes"
+fi
+
+# Must come after Emscripten & locks backtrace feature test
 if test "x$enable_backtrace" = "xyes"; then
   DALI_CFLAGS="$DALI_CFLAGS -DBACKTRACE_ENABLED"
 fi
@@ -114,4 +127,5 @@ Configuration
   Data Dir (Read Only):             $dataReadOnlyDir
   Emscripten:                       $enable_emscripten
   Backtrace:                        $enable_backtrace
+  ScopedLock Backtrace:             $enable_lock_backtrace
 "
diff --git a/dali/devel-api/common/conditional-wait.cpp b/dali/devel-api/common/conditional-wait.cpp
new file mode 100644 (file)
index 0000000..c22bff3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 <dali/devel-api/common/conditional-wait.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/common/mutex-impl.h>
+
+// EXTERNAL INCLUDES
+#include <pthread.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+struct ConditionalWait::ConditionalWaitImpl
+{
+  pthread_mutex_t mutex;
+  pthread_cond_t condition;
+  volatile unsigned int count;
+};
+
+
+ConditionalWait::ScopedLock::ScopedLock( ConditionalWait& wait ) : mWait(wait)
+{
+  Internal::Mutex::Lock( &wait.mImpl->mutex );
+}
+
+ConditionalWait::ScopedLock::~ScopedLock()
+{
+  ConditionalWait& wait = mWait;
+  Internal::Mutex::Unlock( &wait.mImpl->mutex );
+}
+
+ConditionalWait::ConditionalWait()
+: mImpl( new ConditionalWaitImpl )
+{
+  pthread_mutex_init( &mImpl->mutex, NULL );
+  pthread_cond_init( &mImpl->condition, NULL );
+  mImpl->count = 0;
+}
+
+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
+  Internal::Mutex::Lock( &mImpl->mutex );
+  volatile unsigned int previousCount = mImpl->count;
+  mImpl->count = 0; // change state before broadcast as that may wake clients immediately
+  // broadcast does nothing if the thread is not waiting but still has a system call overhead
+  // broadcast all threads to continue
+  if( 0 != previousCount )
+  {
+    pthread_cond_broadcast( &mImpl->condition );
+  }
+  Internal::Mutex::Unlock( &mImpl->mutex );
+}
+
+void ConditionalWait::Wait()
+{
+  // pthread_cond_wait requires a lock to be held
+  Internal::Mutex::Lock( &mImpl->mutex );
+  ++(mImpl->count);
+  // pthread_cond_wait may wake up without anyone calling Notify
+  do
+  {
+    // wait while condition changes
+    pthread_cond_wait( &mImpl->condition, &mImpl->mutex ); // releases the lock whilst waiting
+  }
+  while( 0 != mImpl->count );
+  // when condition returns the mutex is locked so release the lock
+  Internal::Mutex::Unlock( &mImpl->mutex );
+}
+
+void ConditionalWait::Wait( const ScopedLock& scope )
+{
+  // Scope must be locked:
+  DALI_ASSERT_DEBUG( &scope.GetLockedWait() == this );
+
+  ++(mImpl->count);
+
+  // pthread_cond_wait may wake up without anyone calling Notify so loop until
+  // count has been reset in a notify:
+  do
+  {
+    // wait while condition changes
+    pthread_cond_wait( &mImpl->condition, &mImpl->mutex ); // releases the lock whilst waiting
+  }
+  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;
+}
+
+} // namespace Dali
diff --git a/dali/devel-api/common/conditional-wait.h b/dali/devel-api/common/conditional-wait.h
new file mode 100644 (file)
index 0000000..4315fed
--- /dev/null
@@ -0,0 +1,125 @@
+#ifndef __DALI_CONDITIONAL_WAIT_H__
+#define __DALI_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+
+namespace Dali
+{
+
+/**
+ * Helper class to allow conditional waiting and notifications between multiple threads.
+ */
+class DALI_IMPORT_API ConditionalWait
+{
+public:
+
+  /**
+   * @brief Allows client code to synchronize updates to its own state with the
+   * internal state of a ConditionalWait object.
+   */
+  class ScopedLock
+  {
+  public:
+    /**
+     * Constructor
+     * @brief Will acquire the internal mutex of the ConditionalWait object passed in.
+     * @param[in] wait The ConditionalWait object to lock.
+     */
+    ScopedLock( ConditionalWait& wait );
+
+    /**
+     * Destructor
+     * @brief Will release the internal mutex of the ConditionalWait object passed in.
+     */
+    ~ScopedLock();
+
+    /**
+     * Getter for the ConditionalWait locked for this instance's lifetime.
+     * @return the ConditionalWait object currently locked.
+     */
+    ConditionalWait& GetLockedWait() const { return mWait; }
+
+  private:
+
+    // Not implemented as ScopedLock cannot be copied:
+    ScopedLock( const ScopedLock& );
+    const ScopedLock& operator=( const ScopedLock& );
+
+    ConditionalWait& mWait;
+  };
+
+  /**
+   * @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 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.
+   * 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.
+   * @pre scope must have been passed this ConditionalWait during its construction.
+   */
+  void Wait( const ScopedLock& scope );
+
+  /**
+   * @brief Return the count of threads waiting for this conditional
+   * @return count of waits
+   */
+  unsigned int GetWaitCount() const;
+
+private:
+
+  // Not implemented as ConditionalWait is not copyable
+  ConditionalWait( const ConditionalWait& );
+  const ConditionalWait& operator=( const ConditionalWait& );
+
+  struct ConditionalWaitImpl;
+  ConditionalWaitImpl* mImpl;
+
+};
+
+} // namespace Dali
+
+#endif // __DALI_CONDITIONAL_WAIT_H__
index 37f9d33..685506c 100644 (file)
@@ -18,6 +18,9 @@
 // CLASS HEADER
 #include <dali/devel-api/common/mutex.h>
 
+// INTERNAL INCLUDES
+#include <dali/internal/common/mutex-impl.h>
+
 // EXTERNAL INCLUDES
 #include <pthread.h>
 
@@ -53,14 +56,14 @@ bool Mutex::IsLocked()
 Mutex::ScopedLock::ScopedLock( Mutex& mutex )
 : mMutex( mutex )
 {
-  pthread_mutex_lock( &mMutex.mImpl->mutex );
+  Internal::Mutex::Lock( &mMutex.mImpl->mutex );
   mMutex.mImpl->locked = true;
 }
 
 Mutex::ScopedLock::~ScopedLock()
 {
   mMutex.mImpl->locked = false;
-  pthread_mutex_unlock( &mMutex.mImpl->mutex );
+  Internal::Mutex::Unlock( &mMutex.mImpl->mutex );
 }
 
 } // namespace Dali
index 5186f52..fcd9d16 100644 (file)
@@ -3,6 +3,7 @@
 devel_api_src_files = \
   $(devel_api_src_dir)/animation/animation-data.cpp \
   $(devel_api_src_dir)/animation/path-constrainer.cpp \
+  $(devel_api_src_dir)/common/conditional-wait.cpp \
   $(devel_api_src_dir)/common/hash.cpp \
   $(devel_api_src_dir)/common/mutex.cpp \
   $(devel_api_src_dir)/events/hit-test-algorithm.cpp \
@@ -28,6 +29,7 @@ devel_api_core_animation_header_files = \
 devel_api_core_common_header_files = \
   $(devel_api_src_dir)/common/hash.h \
   $(devel_api_src_dir)/common/map-wrapper.h \
+  $(devel_api_src_dir)/common/conditional-wait.h \
   $(devel_api_src_dir)/common/mutex.h \
   $(devel_api_src_dir)/common/ref-counted-dali-vector.h \
     $(devel_api_src_dir)/common/set-wrapper.h
diff --git a/dali/internal/common/mutex-impl.cpp b/dali/internal/common/mutex-impl.cpp
new file mode 100644 (file)
index 0000000..9c57c7f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ */
+
+// HEADER
+#include <dali/internal/common/mutex-impl.h>
+
+
+#ifdef LOCK_BACKTRACE_ENABLED
+// EXTERNAL INCLUDES
+#include <cstdlib>
+#include <execinfo.h>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/integration-api/debug.h>
+
+#endif // LOCK_BACKTRACE_ENABLED
+
+namespace Dali
+{
+#ifdef LOCK_BACKTRACE_ENABLED
+extern std::string Demangle( const char* symbol );
+#endif // LOCK_BACKTRACE_ENABLED
+
+namespace Internal
+{
+
+namespace Mutex
+{
+
+namespace
+{
+#ifdef LOCK_BACKTRACE_ENABLED
+
+// Constants
+const unsigned int MAX_NUM_STACK_FRAMES = 4;
+const unsigned int MAX_LOCK_SUPPORT = 5;
+
+struct BackTraceInfo
+{
+  void * frameArray[ MAX_NUM_STACK_FRAMES ]; ///< Stores the frame array where the lock occurred
+  int size;                                  ///< Number of frames in the frame array (can be less than MAX_NUM_STACK_FRAMES)
+};
+
+__thread BackTraceInfo gBackTraceInfo[ MAX_LOCK_SUPPORT ]; ///< Thread local storage for the backtrace of the locks
+
+#endif // LOCK_BACKTRACE_ENABLED
+
+__thread unsigned int gThreadLockCount = 0; ///<
+} // unnamed namespace
+
+void Lock( pthread_mutex_t* mutex )
+{
+  ++gThreadLockCount;
+
+#ifdef LOCK_BACKTRACE_ENABLED
+
+  if( gThreadLockCount <= MAX_LOCK_SUPPORT )
+  {
+    // Store the frame array for this lock
+    int backTraceIndex = gThreadLockCount - 1;
+    gBackTraceInfo[ backTraceIndex ].size = backtrace( gBackTraceInfo[ backTraceIndex ].frameArray, MAX_NUM_STACK_FRAMES );
+  }
+  else
+  {
+    DALI_LOG_ERROR( "Reached Maximum lock backtrace support. Previous Locks:\n" );
+  }
+
+  // If we've got more than one lock, then show a warning with a backtrace for all locks that we currently hold
+  if( gThreadLockCount > 1 )
+  {
+    for( unsigned int i = 0; ( i < gThreadLockCount ) && ( i < MAX_LOCK_SUPPORT ); ++i )
+    {
+      DALI_LOG_WARNING( "[Lock %d]\n", i+1 );
+      char** symbols = backtrace_symbols( gBackTraceInfo[ i ].frameArray, gBackTraceInfo[ i ].size );
+      for( int j = 1; j < gBackTraceInfo[ i ].size; ++j )
+      {
+        std::string demangled_symbol = Demangle( symbols[ j ] );
+        DALI_LOG_WARNING( "  [%02d] %s\n", j, demangled_symbol.c_str() );
+      }
+      free(symbols);
+    }
+    DALI_LOG_WARNING( "====================================\n" );
+  }
+
+#else
+
+  // TODO: Uncomment when locks held per thread at any given time are 1
+  //DALI_ASSERT_DEBUG( gThreadLockCount == 1 );
+
+#endif // LOCK_BACKTRACE_ENABLED
+
+  pthread_mutex_lock( mutex );
+}
+
+void Unlock( pthread_mutex_t* mutex )
+{
+  pthread_mutex_unlock( mutex );
+  --gThreadLockCount;
+}
+
+} // namespace Mutex
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/dali/internal/common/mutex-impl.h b/dali/internal/common/mutex-impl.h
new file mode 100644 (file)
index 0000000..bcf07e9
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef __DALI_INTERNAL_MUTEX_H__
+#define __DALI_INTERNAL_MUTEX_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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <pthread.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+/**
+ * @brief Namespace to ensure mutex locking is done correctly.
+ *
+ * Displays warnings if two mutex locks are not held by the same thread at any given time which can lead to deadlock.
+ * This can lead to deadlock and should be avoided.
+ *
+ * @note lock backtrace needs to be enabled to see the warnings.
+ */
+namespace Mutex
+{
+
+/**
+ * @brief Locks the given mutex.
+ *
+ * Increments a thread-local storage counter.
+ *
+ * @param A pointer to the mutex that should be locked.
+ *
+ * @note If the counter is > 1 and lock backtrace is enabled, then the backtrace for all locks will be shown as a warning.
+ */
+void Lock( pthread_mutex_t* mutex );
+
+/**
+ * @brief Unlocks the given mutex.
+ *
+ * @param A pointer to the mutex that should be unlocked.
+ *
+ * Decrements a thread-local storage counter.
+ */
+void Unlock( pthread_mutex_t* mutex );
+
+} // namespace Mutex
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_MUTEX_H__
index b872f00..866696d 100644 (file)
@@ -5,6 +5,7 @@ internal_src_files = \
   $(internal_src_dir)/common/core-impl.cpp \
   $(internal_src_dir)/common/internal-constants.cpp \
   $(internal_src_dir)/common/message-buffer.cpp \
+  $(internal_src_dir)/common/mutex-impl.cpp \
   $(internal_src_dir)/common/image-sampler.cpp \
   $(internal_src_dir)/common/image-attributes.cpp \
   $(internal_src_dir)/common/fixed-size-memory-pool.cpp \