sync with tizen_2.0
[platform/framework/native/appfw.git] / src / base / runtime / FBaseRt_TimerImpl.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @file        FBaseRt_TimerImpl.cpp
20  * @brief       This is the implementation file for the _TimerImpl class.
21  *
22  */
23
24 #include <stdint.h>
25 #include <unistd.h>
26 #include <sys/timerfd.h>
27 #include <glib.h>
28 #include <errno.h>
29
30 #include <FBaseRtTimer.h>
31
32 #include "FApp_AppInfo.h"
33 #include <FBaseSysLog.h>
34 #include "FBaseRt_EventDispatcher.h"
35 #include "FBaseRt_TimerImpl.h"
36
37
38 namespace Tizen { namespace Base { namespace Runtime
39 {
40
41 _TimerImpl::_TimerImpl(void)
42         : __pTimer(null)
43         , __timerFd(-1)
44         , __pTimerIo(null)
45         , __pTimerSource(null)
46         , __pTimerEventListener(null)
47         , __status(TIMER_STATUS_NOT_ACTIVATED)
48 {
49
50 }
51
52 _TimerImpl::~_TimerImpl(void)
53 {
54         if (__pTimerSource != null)
55         {
56                 g_source_destroy(__pTimerSource);
57                 g_source_unref(__pTimerSource);
58         }
59
60         if (__pTimerIo != null)
61         {
62                 g_io_channel_unref(__pTimerIo);
63         }
64 }
65
66 result
67 _TimerImpl::Construct(const Timer& timer, ITimerEventListener& listener)
68 {
69         result r = E_SUCCESS;
70         int timerFd = -1;
71         GIOChannel* pGIOChannel = null;
72         GSource* pGSource = null;
73         _EventDispatcher* pEventDispatcher = null;
74         GMainContext* pGMainContext = null;
75
76         pEventDispatcher = _EventDispatcher::GetCurrentEventDispatcher();
77         SysTryReturnResult(NID_BASE_RT, pEventDispatcher != null, E_SYSTEM,
78                                 "Called by a worker thread.");
79
80         pGMainContext = pEventDispatcher->GetGMainContext();
81         SysTryReturnResult(NID_BASE_RT, pGMainContext != null, E_SYSTEM,
82                                 "Can't get GMainContext.");
83
84         timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
85         SysTryReturnResult(NID_BASE_RT, timerFd != -1, E_SYSTEM, "failed to create a timerfd.");
86
87         pGIOChannel = g_io_channel_unix_new(timerFd);
88         SysTryCatch(NID_BASE_RT, pGIOChannel != null, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Failed to create a GIOChannel for timerfd.");
89
90         g_io_channel_set_close_on_unref(pGIOChannel, TRUE);
91         __timerFd = timerFd;
92         timerFd = -1;
93
94         pGSource = g_io_create_watch(pGIOChannel, G_IO_IN);
95         SysTryCatch(NID_BASE_RT, pGSource != null, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Failed to create a GSource for timerfd.");
96
97         g_source_set_callback(pGSource, (GSourceFunc) _TimerImpl::OnTimerExpired, this, null);
98         g_source_attach(pGSource, pGMainContext);
99
100         __pTimerIo = pGIOChannel;
101         __pTimerSource = pGSource;
102
103         __pTimer = const_cast <Timer*>(&timer);
104         __pTimerEventListener = &listener;
105
106         return E_SUCCESS;
107
108 CATCH:
109         if (pGIOChannel != null)
110         {
111                 g_io_channel_unref(pGIOChannel);
112         }
113
114         if (timerFd != -1)
115         {
116                 close(timerFd);
117         }
118
119         return r;
120 }
121
122 result
123 _TimerImpl::Construct(const Timer& timer, ITimerEventListener& listener, bool awake)
124 {
125         return Construct(timer, listener);
126 }
127
128 gboolean
129 _TimerImpl::OnTimerExpired(GIOChannel* pGIOChannel, GIOCondition condition, gpointer data)
130 {
131         uint64_t exp = 0;
132         _TimerImpl* pTimerImpl = (_TimerImpl*) data;
133
134         if (condition & G_IO_IN)
135         {
136                 ssize_t ret = read(pTimerImpl->__timerFd, &exp, sizeof(uint64_t));
137                 SysTryLog(NID_BASE_RT, ret >= 0, "Timer read failure : %s.", strerror(errno));
138
139                 if (pTimerImpl->__status != TIMER_STATUS_ACTIVATED_REPEATABLE)
140                 {
141                         pTimerImpl->__status = TIMER_STATUS_EXPIRED;
142                 }
143                 else
144                 {
145                         pTimerImpl->__status = TIMER_STATUS_ACTIVATED_REPEATABLE;
146                 }               
147                 pTimerImpl->__pTimerEventListener->OnTimerExpired(*pTimerImpl->__pTimer);
148         }
149
150         return TRUE;
151 }
152
153 result
154 _TimerImpl::Start(int timeout)
155 {
156         int ret = -1;
157         struct timespec now;
158         struct itimerspec newTimeout;
159
160         SysTryReturnResult(NID_BASE_RT, (__status != TIMER_STATUS_ACTIVATED) && (__status != TIMER_STATUS_ACTIVATED_REPEATABLE), E_INVALID_STATE, "Timer is already started.");
161         SysTryReturnResult(NID_BASE_RT, __pTimerEventListener != null, E_INVALID_STATE, "not initialized.");
162
163         if (Tizen::App::_AppInfo::GetApiVersion() == _API_VERSION_2_0 && Tizen::App::_AppInfo::IsOspCompat())
164         {
165                 SysTryReturnResult(NID_BASE_RT, timeout > 0, E_INVALID_ARG, "timeout should be greater than 0.");
166         }
167         else
168         {
169                 SysTryReturnResult(NID_BASE_RT, (timeout > 0) || (timeout == 0), E_INVALID_ARG, "timeout should not be less than 0.");
170         }
171
172         clock_gettime(CLOCK_MONOTONIC, &now);
173
174         if (timeout != 0)
175         {
176                 newTimeout.it_value.tv_sec = now.tv_sec + timeout / 1000;
177                 newTimeout.it_value.tv_nsec = now.tv_nsec + (timeout % 1000) * 1000000;
178         }
179         else
180         {
181                 newTimeout.it_value.tv_sec = now.tv_sec;
182                 newTimeout.it_value.tv_nsec = now.tv_nsec + 1;
183         }
184
185         if (newTimeout.it_value.tv_nsec >= 1000000000)
186         {
187                 newTimeout.it_value.tv_sec += 1;
188                 newTimeout.it_value.tv_nsec -= 1000000000;
189         }
190
191         newTimeout.it_interval.tv_sec = 0;
192         newTimeout.it_interval.tv_nsec = 0;
193
194         ret = timerfd_settime(__timerFd, TFD_TIMER_ABSTIME, &newTimeout, null);
195         SysTryReturnResult(NID_BASE_RT, ret != -1, E_SYSTEM, "Failed to set timeout.");
196
197         __status = TIMER_STATUS_ACTIVATED;
198
199         return E_SUCCESS;
200 }
201
202 result
203 _TimerImpl::StartAsRepeatable(int interval)
204 {
205         int ret = -1;
206         struct timespec now;
207         struct itimerspec newTimeout;
208
209         SysTryReturnResult(NID_BASE_RT, (__status != TIMER_STATUS_ACTIVATED) && (__status != TIMER_STATUS_ACTIVATED_REPEATABLE), E_INVALID_STATE, "Timer is already started.");
210         SysTryReturnResult(NID_BASE_RT, interval > 0, E_INVALID_ARG, "interval should greater than 0.");
211         SysTryReturnResult(NID_BASE_RT, __pTimerEventListener != null, E_INVALID_STATE, "not initialized.");
212
213         clock_gettime(CLOCK_MONOTONIC, &now);
214
215         newTimeout.it_value.tv_sec = now.tv_sec + interval / 1000;
216         newTimeout.it_value.tv_nsec = now.tv_nsec + (interval % 1000) * 1000000;
217         if (newTimeout.it_value.tv_nsec >= 1000000000)
218         {
219                 newTimeout.it_value.tv_sec += 1;
220                 newTimeout.it_value.tv_nsec -= 1000000000;
221         }
222
223         newTimeout.it_interval.tv_sec = interval / 1000;
224         newTimeout.it_interval.tv_nsec = (interval % 1000)*1000000;
225
226         ret = timerfd_settime(__timerFd, TFD_TIMER_ABSTIME, &newTimeout, null);
227         SysTryReturnResult(NID_BASE_RT, ret != -1, E_SYSTEM, "Failed to set interval.");
228
229         __status = TIMER_STATUS_ACTIVATED_REPEATABLE;
230
231         return E_SUCCESS;
232 }
233
234 result
235 _TimerImpl::Cancel(void)
236 {
237         int ret = -1;
238         struct itimerspec newTimeout;
239         SysTryReturnResult(NID_BASE_RT, (__status == TIMER_STATUS_ACTIVATED) || (__status == TIMER_STATUS_ACTIVATED_REPEATABLE), E_INVALID_STATE, "Timer is not started.");
240
241         newTimeout.it_value.tv_sec = 0;
242         newTimeout.it_value.tv_nsec = 0;
243         newTimeout.it_interval.tv_sec = 0;
244         newTimeout.it_interval.tv_nsec = 0;
245
246         ret = timerfd_settime(__timerFd, TFD_TIMER_ABSTIME, &newTimeout, null);
247         SysTryReturnResult(NID_BASE_RT, ret != -1, E_SYSTEM, "Failed to unset timeout.");
248
249         __status = TIMER_STATUS_CANCELED;
250
251         return E_SUCCESS;
252 }
253
254 } } } // Tizen::Runtime