Merge "Remove useless KeyFrame if we need" into devel/master
[platform/core/uifw/dali-core.git] / dali / devel-api / threading / semaphore.h
1 #pragma once
2
3 /*
4  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 // INTERNAL INCLUDES
21 #include <dali/public-api/common/dali-common.h>
22
23 // EXTERNAL INCLUDES
24 #include <condition_variable>
25 #include <limits>
26 #include <mutex>
27 #include <sstream>
28 #include <stdexcept>
29
30 namespace Dali
31 {
32 /**
33  * @brief  Class that implements a C++20 counting_semaphore like interface
34  */
35 template<std::ptrdiff_t LeastMaxValue = std::numeric_limits<std::ptrdiff_t>::max()>
36 class Semaphore
37 {
38 public:
39   /**
40    * @brief Returns the internal counter's maximum possible value,
41    *        which is greater than or equal to LeastMaxValue.
42    *
43    * @return the maximum value of the semaphore
44    */
45   static constexpr std::ptrdiff_t Max() noexcept
46   {
47     return LeastMaxValue;
48   }
49
50   /**
51    * @brief class constructor
52    *
53    * @param[in] desired the desired initial value of the semaphore
54    */
55   explicit Semaphore(std::ptrdiff_t desired)
56   : mCount(desired)
57   {
58     if(mCount < 0 || mCount > Max())
59     {
60       ThrowInvalidParamException(desired);
61     }
62   }
63
64   /**
65    * @brief Atomically increments the internal counter by the value of update.
66    *
67    * Any thread waiting for the counter to be greater than 0 will subsequently
68    * be unlocked.
69    *
70    * @param[in] update value to increment the semaphore
71    */
72   void Release(std::ptrdiff_t update = 1)
73   {
74     std::lock_guard<std::mutex> lock(mLock);
75     if(update < 0 || update > Max() - mCount)
76     {
77       ThrowInvalidParamException(update);
78     }
79
80     mCount += update;
81     while(update--)
82     {
83       mCondVar.notify_one();
84     }
85   }
86
87   /**
88    * @brief Atomically decrements the internal counter by one if it is greater
89    *        than zero; otherwise blocks until it is greater than zero and can
90    *        successfully decrement the internal counter.
91    */
92   void Acquire()
93   {
94     std::unique_lock<std::mutex> lock(mLock);
95     while(mCount == 0)
96     {
97       mCondVar.wait(lock);
98     }
99     --mCount;
100   }
101
102   /**
103    * @brief Tries to atomically decrement the internal counter by one if it is
104    *        greater than zero; no blocking occurs regardless.
105    *
106    * @return true if it decremented the counter, otherwise false.
107    */
108   bool TryAcquire()
109   {
110     std::lock_guard<std::mutex> lock(mLock);
111     if(mCount)
112     {
113       --mCount;
114       return true;
115     }
116
117     return false;
118   }
119
120   /**
121    * @brief Tries to atomically decrement the internal counter by one if it is greater
122    *        than zero; otherwise blocks until it is greater than zero can successfully
123    *        decrement the internal counter, or the relTime duration has been exceeded.
124    *
125    * @param[in] relTime the minimum duration the function must wait for to fail
126    *
127    * @return true if it decremented the internal counter, otherwise false
128    */
129   template<typename Rep, typename Period>
130   bool TryAcquireFor(const std::chrono::duration<Rep, Period>& relTime)
131   {
132     std::unique_lock<std::mutex> lock(mLock);
133     while(mCount == 0)
134     {
135       if(mCondVar.wait_for(lock, relTime) == std::cv_status::timeout)
136       {
137         return false;
138       }
139     }
140     --mCount;
141     return true;
142   }
143
144   /**
145    * @brief Tries to atomically decrement the internal counter by one if it is greater
146    *        than zero; otherwise blocks until it is greater than zero can successfully
147    *        decrement the internal counter, or the absTime duration point has been passed.
148    *
149    * @param[in] absTime the earliest time the function must wait until in order to fail
150    *
151    * @return true if it decremented the internal counter, otherwise false
152    */
153   template<typename Clock, typename Duration>
154   bool TryAcquireUntil(const std::chrono::time_point<Clock, Duration>& absTime)
155   {
156     std::unique_lock<std::mutex> lock(mLock);
157     while(mCount == 0)
158     {
159       if(mCondVar.wait_until(lock, absTime) == std::cv_status::timeout)
160       {
161         return false;
162       }
163     }
164     --mCount;
165     return true;
166   }
167
168 private:
169   void ThrowInvalidParamException(std::ptrdiff_t param) const
170   {
171     std::stringstream ss("Invalid parameter value ");
172     ss << param;
173     throw std::invalid_argument(ss.str());
174   }
175
176   std::condition_variable mCondVar;
177   std::mutex              mLock;
178   std::ptrdiff_t          mCount;
179 };
180 } // namespace Dali