[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / devel-api / threading / conditional-wait.cpp
1 /*
2  * Copyright (c) 2020 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 <condition_variable>
23 #include <mutex>
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   }
51   ConditionalWait&             wait;
52   std::unique_lock<std::mutex> lock;
53 };
54
55 ConditionalWait::ScopedLock::ScopedLock(ConditionalWait& wait)
56 : mImpl(new ConditionalWait::ScopedLock::ScopedLockImpl(wait))
57 {
58   Internal::MutexTrace::Lock(); // matching sequence in mutex.cpp
59 }
60
61 ConditionalWait::ScopedLock::~ScopedLock()
62 {
63   Internal::MutexTrace::Unlock();
64   delete mImpl;
65 }
66
67 ConditionalWait& ConditionalWait::ScopedLock::GetLockedWait() const
68 {
69   return mImpl->wait; // mImpl can never be NULL
70 }
71
72 ConditionalWait::ConditionalWait()
73 : mImpl(new ConditionalWaitImpl)
74 {
75   mImpl->count = 0;
76 }
77
78 ConditionalWait::~ConditionalWait()
79 {
80   delete mImpl;
81 }
82
83 void ConditionalWait::Notify()
84 {
85   // conditional wait requires a lock to be held
86   ScopedLock            lock(*this);
87   volatile unsigned int previousCount = mImpl->count;
88
89   mImpl->count = 0; // change state before broadcast as that may wake clients immediately
90   // notify does nothing if the thread is not waiting but still has a system call overhead
91   // notify all threads to continue
92   if(0 != previousCount)
93   {
94     mImpl->condition.notify_all(); // no return value
95   }
96 }
97
98 void ConditionalWait::Notify(const ScopedLock& scope)
99 {
100   // Scope must be locked:
101   DALI_ASSERT_DEBUG(&scope.GetLockedWait() == this);
102
103   volatile unsigned int previousCount = mImpl->count;
104
105   mImpl->count = 0; // change state before broadcast as that may wake clients immediately
106   // notify does nothing if the thread is not waiting but still has a system call overhead
107   // notify all threads to continue
108   if(0 != previousCount)
109   {
110     mImpl->condition.notify_all(); // no return value
111   }
112 }
113
114 void ConditionalWait::Wait()
115 {
116   // conditional wait requires a lock to be held
117   ScopedLock scope(*this);
118   ++(mImpl->count);
119   // conditional wait may wake up without anyone calling Notify
120   do
121   {
122     // wait while condition changes
123     mImpl->condition.wait(scope.mImpl->lock); // need to pass in the std::unique_lock
124   } while(0 != mImpl->count);
125 }
126
127 void ConditionalWait::Wait(const ScopedLock& scope)
128 {
129   // Scope must be locked:
130   DALI_ASSERT_DEBUG(&scope.GetLockedWait() == this);
131
132   ++(mImpl->count);
133
134   // conditional wait may wake up without anyone calling Notify
135   do
136   {
137     // wait while condition changes
138     mImpl->condition.wait(scope.mImpl->lock); // need to pass in the std::unique_lock
139   } while(0 != mImpl->count);
140
141   // We return with our mutex locked safe in the knowledge that the ScopedLock
142   // passed in will unlock it in the caller.
143 }
144
145 unsigned int ConditionalWait::GetWaitCount() const
146 {
147   return mImpl->count;
148 }
149
150 } // namespace Dali