From 6e3411d394aefb0b2ef194bf3bd5cc7f70091d84 Mon Sep 17 00:00:00 2001 From: Xiangyin Ma Date: Wed, 21 Oct 2015 16:14:35 +0100 Subject: [PATCH] Added Thread abstract class Change-Id: Id9be6082ef416bf2ea619bd3be7327a4f2c46f73 --- automated-tests/src/dali-devel/CMakeLists.txt | 1 + .../src/dali-devel/utc-Dali-ConditionalWait.cpp | 126 +++++++++++---------- automated-tests/src/dali-devel/utc-Dali-Mutex.cpp | 34 +++--- automated-tests/src/dali-devel/utc-Dali-Thread.cpp | 79 +++++++++++++ build/tizen/dali-core/Makefile.am | 2 + dali/devel-api/file.list | 15 ++- .../{common => threading}/conditional-wait.cpp | 2 +- .../{common => threading}/conditional-wait.h | 0 dali/devel-api/{common => threading}/mutex.cpp | 2 +- dali/devel-api/{common => threading}/mutex.h | 0 dali/devel-api/threading/thread.cpp | 70 ++++++++++++ dali/devel-api/threading/thread.h | 86 ++++++++++++++ .../internal/event/common/notification-manager.cpp | 2 +- dali/internal/update/manager/update-manager.cpp | 2 +- .../internal/update/queue/update-message-queue.cpp | 2 +- 15 files changed, 336 insertions(+), 87 deletions(-) create mode 100644 automated-tests/src/dali-devel/utc-Dali-Thread.cpp rename dali/devel-api/{common => threading}/conditional-wait.cpp (98%) rename dali/devel-api/{common => threading}/conditional-wait.h (100%) rename dali/devel-api/{common => threading}/mutex.cpp (97%) rename dali/devel-api/{common => threading}/mutex.h (100%) create mode 100644 dali/devel-api/threading/thread.cpp create mode 100644 dali/devel-api/threading/thread.h diff --git a/automated-tests/src/dali-devel/CMakeLists.txt b/automated-tests/src/dali-devel/CMakeLists.txt index 96fa090..e291f77 100644 --- a/automated-tests/src/dali-devel/CMakeLists.txt +++ b/automated-tests/src/dali-devel/CMakeLists.txt @@ -24,6 +24,7 @@ SET(TC_SOURCES utc-Dali-SignalDelegate.cpp utc-Dali-Scripting.cpp utc-Dali-Shader.cpp + utc-Dali-Thread.cpp utc-Dali-WeakHandle.cpp ) diff --git a/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp b/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp index 7946c8a..44b85bd 100644 --- a/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp +++ b/automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp @@ -19,9 +19,11 @@ #include #include #include -#include +#include +#include using Dali::ConditionalWait; +using Dali::Thread; namespace // for local variables to avoid name clashes { @@ -29,35 +31,59 @@ 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 ) +class WorkerThreadNotify : public Thread { - gGlobalValue = -1; - while( gWorkerThreadWait ) // wait till we can exit + virtual void Run() { - gWorkerThreadState = RUN; - usleep( 1 ); // 1 microseconds + 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; } - usleep( 200 ); // give other thread time to get to Wait - gGlobalValue = 1; - gConditionalWait->Notify(); - gWorkerThreadState = TERMINATE; - return NULL; +}; + +volatile int gNotifyCount = 0; +class WorkerThreadNotifyN : public Thread +{ + virtual void Run() + { + while( gNotifyCount > 0 ) + { + gConditionalWait->Notify(); + usleep( 10 ); // 10 microseconds between each notify + } + } +}; + +class WorkerThreadWaitN : public Thread +{ + virtual void Run() + { + gConditionalWait->Wait(); + } +}; + } int UtcConditionalWait1P(void) { tet_infoline("Testing ConditionalWait - scenario: wait - notify with 2 threads"); - pthread_t thread1; + WorkerThreadNotify 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 ); + thread1.Start(); // wait till the thread is in run state while( RUN != gWorkerThreadState ) { @@ -75,8 +101,7 @@ int UtcConditionalWait1P(void) usleep( 1 ); // 1 microsecond } - void* exitValue; - pthread_join( thread1, &exitValue ); + thread1.Join(); delete gConditionalWait; END_TEST; @@ -94,16 +119,6 @@ int UtcConditionalWait2P(void) 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) { @@ -113,8 +128,8 @@ int UtcConditionalWait3P(void) gConditionalWait = new ConditionalWait(); gNotifyCount = 100; - pthread_t thread1; - pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL ); + WorkerThreadNotifyN thread1; + thread1.Start(); while( gNotifyCount > 0 ) { @@ -125,8 +140,7 @@ int UtcConditionalWait3P(void) } DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION ); - void* exitValue; - pthread_join( thread1, &exitValue ); + thread1.Join(); delete gConditionalWait; END_TEST; @@ -140,12 +154,12 @@ int UtcConditionalWait4P(void) 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 ); + WorkerThreadNotifyN thread1; + thread1.Start(); + WorkerThreadNotifyN thread2; + thread2.Start(); + WorkerThreadNotifyN thread3; + thread3.Start(); while( gNotifyCount > 0 ) { @@ -155,22 +169,14 @@ int UtcConditionalWait4P(void) usleep( 10 ); // 10 microseconds between each notify } - void* exitValue; - pthread_join( thread1, &exitValue ); - pthread_join( thread2, &exitValue ); - pthread_join( thread3, &exitValue ); + thread1.Join(); + thread2.Join(); + thread3.Join(); 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"); @@ -178,15 +184,14 @@ int UtcConditionalWait5P(void) // 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 ); - + WorkerThreadWaitN thread1; + thread1.Start(); + WorkerThreadWaitN thread2; + thread2.Start(); + WorkerThreadWaitN thread3; + thread3.Start(); + WorkerThreadWaitN thread4; + thread4.Start(); // wait till all child threads are waiting while( gConditionalWait->GetWaitCount() < 4 ) { } @@ -194,11 +199,10 @@ int UtcConditionalWait5P(void) // 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 ); + thread1.Join(); + thread2.Join(); + thread3.Join(); + thread4.Join(); DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION ); diff --git a/automated-tests/src/dali-devel/utc-Dali-Mutex.cpp b/automated-tests/src/dali-devel/utc-Dali-Mutex.cpp index 290a826..8a1a5b0 100644 --- a/automated-tests/src/dali-devel/utc-Dali-Mutex.cpp +++ b/automated-tests/src/dali-devel/utc-Dali-Mutex.cpp @@ -19,10 +19,12 @@ #include #include #include -#include +#include +#include #include using Dali::Mutex; +using Dali::Thread; int UtcDaliMutexSingleThread(void) { @@ -55,21 +57,24 @@ volatile int gGlobalValue = 0; volatile bool gWorkerThreadWait = true; volatile enum ThreadState { INIT, RUN, LOCKING, TERMINATE } gWorkerThreadState = INIT; Mutex* volatile gGlobalValueMutex; // volatile pointer to a mutex object -} -void* WorkerThread1( void* ptr ) + +class TestThread : public Thread { - gWorkerThreadState = RUN; + virtual void Run() { - Mutex::ScopedLock lock( *gGlobalValueMutex ); - gWorkerThreadState = LOCKING; - gGlobalValue = -1; - while( gWorkerThreadWait ) // wait till we can exit + gWorkerThreadState = RUN; { - usleep( 1 ); // 1 microsecond + Mutex::ScopedLock lock( *gGlobalValueMutex ); + gWorkerThreadState = LOCKING; + gGlobalValue = -1; + while( gWorkerThreadWait ) // wait till we can exit + { + usleep( 1 ); // 1 microsecond + } } + gWorkerThreadState = TERMINATE; } - gWorkerThreadState = TERMINATE; - return NULL; +}; } int UtcDaliMutexMultiThread(void) @@ -78,7 +83,7 @@ int UtcDaliMutexMultiThread(void) gGlobalValueMutex = new Dali::Mutex(); - pthread_t thread1; + TestThread thread1; // initialize values gGlobalValue = 0; gWorkerThreadWait = true; @@ -90,7 +95,7 @@ int UtcDaliMutexMultiThread(void) { Mutex::ScopedLock lock( *gGlobalValueMutex ); DALI_TEST_EQUALS( true, gGlobalValueMutex->IsLocked(), TEST_LOCATION ); - pthread_create( &thread1, NULL, &WorkerThread1, NULL ); + thread1.Start(); // wait till the thread is in run state while( RUN != gWorkerThreadState ) { @@ -119,8 +124,7 @@ int UtcDaliMutexMultiThread(void) usleep( 1 ); // 1 microsecond } DALI_TEST_EQUALS( false, gGlobalValueMutex->IsLocked(), TEST_LOCATION ); - void* exitValue; - pthread_join( thread1, &exitValue ); + thread1.Join(); END_TEST; } diff --git a/automated-tests/src/dali-devel/utc-Dali-Thread.cpp b/automated-tests/src/dali-devel/utc-Dali-Thread.cpp new file mode 100644 index 0000000..a62fde7 --- /dev/null +++ b/automated-tests/src/dali-devel/utc-Dali-Thread.cpp @@ -0,0 +1,79 @@ +/* + * 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 +#include +#include +#include +#include + +using Dali::Thread; + +namespace +{ +volatile bool gRunThreadEntryFunc = false; + +class TestThread : public Thread +{ + virtual void Run() + { + gRunThreadEntryFunc = true; + } +}; +} + +int UtcDaliThreadP(void) +{ + tet_infoline("Testing Dali::Thread"); + + gRunThreadEntryFunc = false; + + TestThread thread; + + thread.Start(); + // wait till the thread is terminated + while( !gRunThreadEntryFunc ) + { + usleep( 1 ); // 1 microsecond + } + DALI_TEST_EQUALS( true, gRunThreadEntryFunc, TEST_LOCATION ); + + thread.Join(); + + // Restart the thread after joined + gRunThreadEntryFunc = false; + thread.Start(); + thread.Join(); + // wait till the thread is terminated + while( !gRunThreadEntryFunc ) + { + usleep( 1 ); // 1 microsecond + } + DALI_TEST_EQUALS( true, gRunThreadEntryFunc, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliThreadNonCopyable(void) +{ + // we want to make sure that mutex is not copyable (its copy constructor is not defined) + // this test will stop compiling if Mutex has compiler generated copy constructor + DALI_COMPILE_TIME_ASSERT( !__has_trivial_copy( Thread ) ); + + DALI_TEST_CHECK( true ); + END_TEST; +} diff --git a/build/tizen/dali-core/Makefile.am b/build/tizen/dali-core/Makefile.am index 823fc93..82f50d7 100644 --- a/build/tizen/dali-core/Makefile.am +++ b/build/tizen/dali-core/Makefile.am @@ -94,6 +94,7 @@ develapiobjectdir = $(develapidir)/object develapirenderingdir = $(develapidir)/rendering develapiscriptingdir = $(develapidir)/scripting develapisignalsdir = $(develapidir)/signals +develapithreadingdir = $(develapidir)/threading develapi_HEADERS = $(devel_api_header_files) develapiactors_HEADERS = $(devel_api_core_actors_header_files) @@ -106,6 +107,7 @@ develapiobject_HEADERS = $(devel_api_core_object_header_files) develapirendering_HEADERS = $(devel_api_core_rendering_header_files) develapiscripting_HEADERS = $(devel_api_core_scripting_header_files) develapisignals_HEADERS = $(devel_api_core_signals_header_files) +develapithreading_HEADERS = $(devel_api_core_threading_header_files) #public api diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index 4c907e2..7528fe1 100644 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -3,9 +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 \ $(devel_api_src_dir)/images/atlas.cpp \ $(devel_api_src_dir)/images/distance-field.cpp \ @@ -18,7 +16,10 @@ devel_api_src_files = \ $(devel_api_src_dir)/rendering/sampler.cpp \ $(devel_api_src_dir)/rendering/shader.cpp \ $(devel_api_src_dir)/scripting/scripting.cpp \ - $(devel_api_src_dir)/signals/signal-delegate.cpp + $(devel_api_src_dir)/signals/signal-delegate.cpp \ + $(devel_api_src_dir)/threading/conditional-wait.cpp \ + $(devel_api_src_dir)/threading/mutex.cpp \ + $(devel_api_src_dir)/threading/thread.cpp # Add devel header files here DALi internal developer files used by Adaptor & Toolkit @@ -29,11 +30,9 @@ 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/owner-container.h \ $(devel_api_src_dir)/common/ref-counted-dali-vector.h \ - $(devel_api_src_dir)/common/set-wrapper.h + $(devel_api_src_dir)/common/set-wrapper.h devel_api_core_events_header_files = \ $(devel_api_src_dir)/events/hit-test-algorithm.h @@ -61,4 +60,8 @@ devel_api_core_signals_header_files = \ devel_api_core_scripting_header_files = \ $(devel_api_src_dir)/scripting/scripting.h +devel_api_core_threading_header_files = \ + $(devel_api_src_dir)/threading/conditional-wait.h \ + $(devel_api_src_dir)/threading/mutex.h \ + $(devel_api_src_dir)/threading/thread.h diff --git a/dali/devel-api/common/conditional-wait.cpp b/dali/devel-api/threading/conditional-wait.cpp similarity index 98% rename from dali/devel-api/common/conditional-wait.cpp rename to dali/devel-api/threading/conditional-wait.cpp index c22bff3..39c7e6f 100644 --- a/dali/devel-api/common/conditional-wait.cpp +++ b/dali/devel-api/threading/conditional-wait.cpp @@ -16,7 +16,7 @@ */ // CLASS HEADER -#include +#include // INTERNAL INCLUDES #include diff --git a/dali/devel-api/common/conditional-wait.h b/dali/devel-api/threading/conditional-wait.h similarity index 100% rename from dali/devel-api/common/conditional-wait.h rename to dali/devel-api/threading/conditional-wait.h diff --git a/dali/devel-api/common/mutex.cpp b/dali/devel-api/threading/mutex.cpp similarity index 97% rename from dali/devel-api/common/mutex.cpp rename to dali/devel-api/threading/mutex.cpp index 685506c..8309b3b 100644 --- a/dali/devel-api/common/mutex.cpp +++ b/dali/devel-api/threading/mutex.cpp @@ -16,7 +16,7 @@ */ // CLASS HEADER -#include +#include // INTERNAL INCLUDES #include diff --git a/dali/devel-api/common/mutex.h b/dali/devel-api/threading/mutex.h similarity index 100% rename from dali/devel-api/common/mutex.h rename to dali/devel-api/threading/mutex.h diff --git a/dali/devel-api/threading/thread.cpp b/dali/devel-api/threading/thread.cpp new file mode 100644 index 0000000..d7bfa0b --- /dev/null +++ b/dali/devel-api/threading/thread.cpp @@ -0,0 +1,70 @@ +/* + * 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 + +// EXTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +struct Thread::ThreadImpl +{ + pthread_t thread; + bool isCreated; +}; + +Thread::Thread() +: mImpl( new ThreadImpl ) +{ + mImpl->isCreated = false; +} + +Thread::~Thread() +{ + delete mImpl; +} + +void Thread::Start() +{ + DALI_ASSERT_DEBUG( !mImpl->isCreated ); + + int error = pthread_create( &(mImpl->thread), NULL, InternalThreadEntryFunc, this ); + DALI_ASSERT_ALWAYS( !error && "Failed to create a new thread" ); + mImpl->isCreated = true; +} + +void Thread::Join() +{ + if( mImpl->isCreated ) + { + mImpl->isCreated = false; + pthread_join( mImpl->thread, NULL ); + } +} + +void* Thread::InternalThreadEntryFunc( void* This ) +{ + ( static_cast( This ) )->Run(); + return NULL; +} + +} // namespace Dali diff --git a/dali/devel-api/threading/thread.h b/dali/devel-api/threading/thread.h new file mode 100644 index 0000000..535e700 --- /dev/null +++ b/dali/devel-api/threading/thread.h @@ -0,0 +1,86 @@ +#ifndef __DALI_THREAD_H__ +#define __DALI_THREAD_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 + + +/** + * The top level DALi namespace + */ +namespace Dali +{ + +/* + * @brief Abstract class for thread functionality. Can be used for worker threads. + */ +class DALI_IMPORT_API Thread +{ +public: + + /** + * @brief Creates a new thread and make it executable. + */ + void Start(); + + /** + * @brief Wait for thread termination. + */ + void Join(); + +protected: + + /** + * @brief Constructor + */ + Thread(); + + /** + * @brief Destructor, virtual as this is used as base class + */ + virtual ~Thread(); + + /** + * The routine that the thread will execute once it is started. + */ + virtual void Run() = 0; + +private: + + /** + * Helper for the thread calling the entry function. + * @param[in] This A pointer to the current RenderThread object + */ + static void* InternalThreadEntryFunc( void* This ); + + // Undefined + Thread( const Thread& ); + // Undefined + const Thread& operator=( const Thread& ); + +private: + + struct ThreadImpl; + ThreadImpl* mImpl; +}; + +} + +#endif /* __DALI_THREAD_H__ */ diff --git a/dali/internal/event/common/notification-manager.cpp b/dali/internal/event/common/notification-manager.cpp index 0b113d1..71011ee 100644 --- a/dali/internal/event/common/notification-manager.cpp +++ b/dali/internal/event/common/notification-manager.cpp @@ -20,8 +20,8 @@ // INTERNAL INCLUDES #include -#include #include +#include #include #include #include diff --git a/dali/internal/update/manager/update-manager.cpp b/dali/internal/update/manager/update-manager.cpp index cc4f379..41e7431 100644 --- a/dali/internal/update/manager/update-manager.cpp +++ b/dali/internal/update/manager/update-manager.cpp @@ -21,8 +21,8 @@ // INTERNAL INCLUDES #include #include -#include #include +#include #include #include diff --git a/dali/internal/update/queue/update-message-queue.cpp b/dali/internal/update/queue/update-message-queue.cpp index bf529a6..0e361f7 100644 --- a/dali/internal/update/queue/update-message-queue.cpp +++ b/dali/internal/update/queue/update-message-queue.cpp @@ -20,7 +20,7 @@ // INTERNAL INCLUDES #include -#include +#include #include #include #include -- 2.7.4