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
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
--- /dev/null
+/*
+ * 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;
+}
+
+
[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
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
Data Dir (Read Only): $dataReadOnlyDir
Emscripten: $enable_emscripten
Backtrace: $enable_backtrace
+ ScopedLock Backtrace: $enable_lock_backtrace
"
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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__
// CLASS HEADER
#include <dali/devel-api/common/mutex.h>
+// INTERNAL INCLUDES
+#include <dali/internal/common/mutex-impl.h>
+
// EXTERNAL INCLUDES
#include <pthread.h>
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
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 \
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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__
$(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 \