1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef META_API_TIMER_H
17 #define META_API_TIMER_H
18 
19 #include <meta/api/deferred_callback.h>
20 #include <meta/api/make_callback.h>
21 #include <meta/api/threading/mutex.h>
22 #include <meta/interface/intf_task_queue.h>
23 #include <meta/interface/intf_task_queue_registry.h>
24 
META_BEGIN_NAMESPACE()25 META_BEGIN_NAMESPACE()
26 
27 /**
28  * @brief Helper class to launch timers, this type is not copyable, only movable. Destructor will stop the timer.
29  */
30 class Timer {
31 public:
32     /**
33      * @brief Type of the timer, single shot happens only once when as recurring keeps triggering after given timeout
34      */
35     enum TimerType { SINGLE_SHOT, RECURRING };
36 
37     Timer() = default;
38     Timer(const Timer&) = delete;
39     Timer(Timer&& t) : control_(BASE_NS::move(t.control_)) {}
40     ~Timer()
41     {
42         Stop();
43     }
44 
45     /**
46      * @brief Constructor that calls corresponding Start function (See Start below).
47      */
48     template<typename Func>
49     Timer(const TimeSpan& interval, Func func, TimerType type, const ITaskQueue::Ptr& queue)
50     {
51         Start(interval, BASE_NS::move(func), type, queue);
52     }
53 
54     /**
55      * @brief Constructor that calls corresponding Start function (See Start below).
56      */
57     template<typename Func>
58     Timer(const TimeSpan& interval, Func func, TimerType type, const BASE_NS::Uid& queueId)
59     {
60         Start(interval, BASE_NS::move(func), type, queueId);
61     }
62 
63     Timer& operator=(const Timer&) = delete;
64     Timer& operator=(Timer&& t)
65     {
66         Stop();
67         control_ = BASE_NS::move(t.control_);
68         return *this;
69     }
70 
71     /**
72      * @brief Start timer.
73      * @param interval Time interval which after this timer triggers.
74      * @param func Callable entity (e.g. lambda) which is called.
75      * @param type Type of the timer.
76      * @param queue Queue to which the timer task is posted to (this dictates on what thread the func is called).
77      */
78     template<typename Func>
79     bool Start(const TimeSpan& interval, Func func, TimerType type, const ITaskQueue::Ptr& queue)
80     {
81         if (!queue) {
82             return false;
83         }
84         Stop();
85         control_ = CreateShared<Control>();
86         control_->queue = queue;
87         control_->token =
88             queue->AddTask(MakeCallback<ITaskQueueTask>([control = control_, type, callback = BASE_NS::move(func)] {
89                 callback();
90                 bool ret = type == RECURRING;
91                 if (!ret) {
92                     if (control) {
93                         CORE_NS::UniqueLock lock { control->mutex };
94                         control->token = {};
95                     }
96                 }
97                 return ret;
98             }),
99                 interval);
100         return true;
101     }
102 
103     /**
104      * @brief Start timer.
105      * @param interval Time interval which after this timer triggers.
106      * @param func Callable entity (e.g. lambda) which is called.
107      * @param type Type of the timer.
108      * @param queueId Uid of queue to which the timer task is posted to (this dictates on what thread the func is
109      * called).
110      */
111     template<typename Func>
112     bool Start(const TimeSpan& interval, Func func, TimerType type, const BASE_NS::Uid& queueId)
113     {
114         return Start(interval, BASE_NS::move(func), type, GetTaskQueueRegistry().GetTaskQueue(queueId));
115     }
116 
117     /**
118      * @brief Stop timer
119      */
120     void Stop()
121     {
122         if (control_) {
123             CORE_NS::UniqueLock lock { control_->mutex };
124             if (control_->queue && control_->token) {
125                 control_->queue->CancelTask(control_->token);
126             }
127         }
128         control_.reset();
129     }
130 
131     /**
132      * @brief Detach timer, this can be used if you don't want destructor to stop the timer
133      */
134     ITaskQueue::Token Detach()
135     {
136         ITaskQueue::Token ret {};
137         if (control_) {
138             CORE_NS::UniqueLock lock { control_->mutex };
139             ret = control_->token;
140         }
141         control_.reset();
142         return ret;
143     }
144 
145     /**
146      * @brief Returns true if the timer is currently running
147      */
148     bool IsRunning() const
149     {
150         if (control_) {
151             CORE_NS::UniqueLock lock { control_->mutex };
152             return control_->token != ITaskQueue::Token {};
153         }
154         return false;
155     }
156 
157 private:
158     struct Control {
159         mutable CORE_NS::Mutex mutex;
160         ITaskQueue::Ptr queue;
161         ITaskQueue::Token token {};
162     };
163     BASE_NS::shared_ptr<Control> control_;
164 };
165 
166 /**
167  * @brief Start single shot timer, returns the token that can be used to cancel it (which can be just ignored).
168  * @param interval Time interval which after this timer triggers.
169  * @param func Callable entity (e.g. lambda) which is called.
170  * @param queue Queue to which the timer task is posted to (this dictates on what thread the func is called).
171  */
172 template<typename Func>
SingleShotTimer(const TimeSpan & interval,Func func,const ITaskQueue::Ptr & queue)173 inline ITaskQueue::Token SingleShotTimer(const TimeSpan& interval, Func func, const ITaskQueue::Ptr& queue)
174 {
175     Timer t;
176     t.Start(interval, BASE_NS::move(func), Timer::SINGLE_SHOT, queue);
177     return t.Detach();
178 }
179 
180 /**
181  * @brief Start single shot timer, returns the token that can be used to cancel it (which can be just ignored).
182  * @param interval Time interval which after this timer triggers.
183  * @param func Callable entity (e.g. lambda) which is called.
184  * @param queueId Uid of queue to which the timer task is posted to (this dictates on what thread the func is called).
185  */
186 template<typename Func>
SingleShotTimer(const TimeSpan & interval,Func func,const BASE_NS::Uid & queueId)187 inline ITaskQueue::Token SingleShotTimer(const TimeSpan& interval, Func func, const BASE_NS::Uid& queueId)
188 {
189     Timer t;
190     t.Start(interval, BASE_NS::move(func), Timer::SINGLE_SHOT, queueId);
191     return t.Detach();
192 }
193 
194 META_END_NAMESPACE()
195 
196 #endif
197