fa0a86c42404a684b840e396734bd1da81174381
[platform/core/uifw/dali-core.git] / dali / devel-api / threading / conditional-wait.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/devel-api/threading/conditional-wait.h>
20
21 // EXTERNAL INCLUDES
22 #include <mutex>
23 #include <condition_variable>
24
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/debug.h>
27 #include <dali/internal/common/mutex-trace.h>
28
29 namespace Dali
30 {
31 /**
32  * Data members for ConditionalWait
33  */
34 struct ConditionalWait::ConditionalWaitImpl
35 {
36   std::mutex mutex;
37   std::condition_variable condition;
38   volatile unsigned int count;
39 };
40
41 /**
42  * Data members for ConditionalWait::ScopedLock
43  */
44 struct ConditionalWait::ScopedLock::ScopedLockImpl
45 {
46   ScopedLockImpl( ConditionalWait& wait )
47   : wait( wait ),
48     lock( wait.mImpl->mutex ) // locks for the lifecycle of this object
49   { }
50   ConditionalWait& wait;
51   std::unique_lock<std::mutex> lock;
52 };
53
54 ConditionalWait::ScopedLock::ScopedLock( ConditionalWait& wait )
55 : mImpl( new ConditionalWait::ScopedLock::ScopedLockImpl( wait ) )
56 {
57   Internal::MutexTrace::Lock(); // matching sequence in mutex.cpp
58 }
59
60 ConditionalWait::ScopedLock::~ScopedLock()
61 {
62   Internal::MutexTrace::Unlock();
63   delete mImpl;
64 }
65
66 ConditionalWait& ConditionalWait::ScopedLock::GetLockedWait() const
67 {
68   return mImpl->wait; // mImpl can never be NULL
69 }
70
71 ConditionalWait::ConditionalWait()
72 : mImpl( new ConditionalWaitImpl )
73 {
74   mImpl->count = 0;
75 }
76
77 ConditionalWait::~ConditionalWait()
78 {
79   delete mImpl;
80 }
81
82 void ConditionalWait::Notify()
83 {
84   // conditional wait requires a lock to be held
85   ScopedLock lock( *this );
86   volatile unsigned int previousCount = mImpl->count;
87   mImpl->count = 0; // change state before broadcast as that may wake clients immediately
88   // notify does nothing if the thread is not waiting but still has a system call overhead
89   // notify all threads to continue
90   if( 0 != previousCount )
91   {
92     mImpl->condition.notify_all(); // no return value
93   }
94 }
95
96 void ConditionalWait::Notify( const ScopedLock& scope )
97 {
98   // Scope must be locked:
99   DALI_ASSERT_DEBUG( &scope.GetLockedWait() == this );
100
101   volatile unsigned int previousCount = mImpl->count;
102   mImpl->count = 0; // change state before broadcast as that may wake clients immediately
103   // notify does nothing if the thread is not waiting but still has a system call overhead
104   // notify all threads to continue
105   if( 0 != previousCount )
106   {
107     mImpl->condition.notify_all(); // no return value
108   }
109 }
110
111 void ConditionalWait::Wait()
112 {
113   // conditional wait requires a lock to be held
114   ScopedLock scope( *this );
115   ++(mImpl->count);
116   // conditional wait may wake up without anyone calling Notify
117   do
118   {
119     // wait while condition changes
120     mImpl->condition.wait( scope.mImpl->lock ); // need to pass in the std::unique_lock
121   }
122   while( 0 != mImpl->count );
123 }
124
125 void ConditionalWait::Wait( const ScopedLock& scope )
126 {
127   // Scope must be locked:
128   DALI_ASSERT_DEBUG( &scope.GetLockedWait() == this );
129
130   ++(mImpl->count);
131
132   // conditional wait may wake up without anyone calling Notify
133   do
134   {
135     // wait while condition changes
136     mImpl->condition.wait( scope.mImpl->lock ); // need to pass in the std::unique_lock
137   }
138   while( 0 != mImpl->count );
139
140   // We return with our mutex locked safe in the knowledge that the ScopedLock
141   // passed in will unlock it in the caller.
142 }
143
144 unsigned int ConditionalWait::GetWaitCount() const
145 {
146   return mImpl->count;
147 }
148
149 } // namespace Dali