1/*---------------------------------------------------------------------------*
2 *  ptimer.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
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 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
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
21
22#include "pmemory.h"
23#include "ptimer.h"
24#include "pmutex.h"
25
26#ifdef _WIN32
27
28/*
29  Note that this implementation assumes that QueryPerformanceCounter is
30  available (requires NT 3.1 and above) and that 64 bit arithmetic is
31  available (requires VC)
32*/
33
34struct PTimer_t
35{
36  LARGE_INTEGER PerformanceFreq;
37  LARGE_INTEGER RefTime;
38  LARGE_INTEGER elapsed;
39};
40
41
42
43/**
44 * Creates a new timer object.
45 **/
46ESR_ReturnCode PTimerCreate(PTimer **timer)
47{
48  PTimer *tmp = NULL;
49
50  if (timer == NULL)
51    return ESR_INVALID_ARGUMENT;
52  tmp = NEW(PTimer, "PTimer");
53  if (tmp == NULL)
54    return ESR_OUT_OF_MEMORY;
55
56  if (QueryPerformanceFrequency(&tmp->PerformanceFreq) == 0)
57  {
58    FREE(tmp);
59    return ESR_NOT_SUPPORTED;
60  }
61  tmp->PerformanceFreq.QuadPart /= 1000;
62
63  tmp->RefTime.QuadPart = 0;
64  tmp->elapsed.QuadPart = 0;
65  *timer = tmp;
66  return ESR_SUCCESS;
67}
68
69ESR_ReturnCode PTimerDestroy(PTimer *timer)
70{
71  if (timer == NULL) return ESR_INVALID_ARGUMENT;
72  FREE(timer);
73  return ESR_SUCCESS;
74}
75
76/**
77 * Starts the timer. This sets the reference time from which all new elapsed
78 * time are computed.  This does not reset the elapsed time to 0.  This is
79 * useful to pause the timer.
80 **/
81ESR_ReturnCode PTimerStart(PTimer *timer)
82{
83  if (timer == NULL) return ESR_INVALID_ARGUMENT;
84  return (QueryPerformanceCounter(&timer->RefTime) ?
85          ESR_SUCCESS :
86          ESR_NOT_SUPPORTED);
87}
88
89/**
90 * Stops the timer.
91 **/
92ESR_ReturnCode PTimerStop(PTimer *timer)
93{
94  if (timer == NULL) return ESR_INVALID_ARGUMENT;
95  if (timer->RefTime.QuadPart != 0)
96  {
97    LARGE_INTEGER now;
98    if (!QueryPerformanceCounter(&now)) return ESR_NOT_SUPPORTED;
99    timer->elapsed.QuadPart += now.QuadPart - timer->RefTime.QuadPart;
100    timer->RefTime.QuadPart = 0;
101  }
102  return ESR_SUCCESS;
103}
104
105/**
106 * Returns the timer elapsed time.  If the Timer is in the stopped state,
107 * successive calls to getElapsed() will always return the same value.  If
108 * the Timer is in the started state, successive calls will return the
109 * elapsed time since the last time PTimerStart() was called.
110 */
111ESR_ReturnCode PTimerGetElapsed(PTimer *timer, asr_uint32_t* elapsed)
112{
113  if (timer == NULL || elapsed == NULL)
114    return ESR_INVALID_ARGUMENT;
115
116  if (timer->RefTime.QuadPart != 0)
117  {
118    LARGE_INTEGER now;
119    if (!QueryPerformanceCounter(&now)) return ESR_NOT_SUPPORTED;
120    *elapsed = (asr_uint32_t) ((timer->elapsed.QuadPart + (now.QuadPart - timer->RefTime.QuadPart))
121                           / timer->PerformanceFreq.QuadPart);
122  }
123  else
124    *elapsed = (asr_uint32_t) (timer->elapsed.QuadPart / timer->PerformanceFreq.QuadPart);
125
126  return ESR_SUCCESS;
127}
128
129
130/**
131 * Resets the elapsed time to 0 and resets the reference time of the Timer.
132 * This effectively reset the timer in the same state it was right after creation.
133 **/
134ESR_ReturnCode PTimerReset(PTimer *timer)
135{
136  if (timer == NULL) return ESR_INVALID_ARGUMENT;
137  timer->RefTime.QuadPart = 0;
138  timer->elapsed.QuadPart = 0;
139  return ESR_SUCCESS;
140}
141
142#elif defined(POSIX)
143#include "ptrd.h"
144/*
145POSIX has a timer
146*/
147/* Clocks and timers: clock_settime, clock_gettime, clock_getres, timer_xxx and nanosleep */
148#ifndef _POSIX_TIMERS
149#ifndef __vxworks /* __vxworks does not define it! */
150#error "Timer is not defined!"
151#endif /* __vxworks */
152#endif /* _POSIX_TIMERS */
153
154#define TIMER_MAX_VAL 10000
155
156struct PTimer_t
157{
158  timer_t  timer;
159  asr_uint32_t elapsed;
160};
161
162/**
163* Creates a new timer object.
164**/
165ESR_ReturnCode PTimerCreate(PTimer **timer)
166{
167  PTimer *tmp = NULL;
168
169  if (timer == NULL) return ESR_INVALID_ARGUMENT;
170  tmp = NEW(PTimer, "PTimer");
171  if (tmp == NULL) return ESR_OUT_OF_MEMORY;
172
173  *timer = tmp;
174  if (timer_create(CLOCK_REALTIME, NULL, &(tmp->timer)) < 0)
175    return ESR_NOT_SUPPORTED;
176
177  return ESR_SUCCESS;
178}
179
180ESR_ReturnCode PTimerDestroy(PTimer *timer)
181{
182  if (timer == NULL) return ESR_INVALID_ARGUMENT;
183  timer_delete(timer->timer);
184  FREE(timer);
185
186  return ESR_SUCCESS;
187}
188
189/**
190* Starts the timer. This sets the reference time from which all new elapsed
191* time are computed.  This does not reset the elapsed time to 0.  This is
192* useful to pause the timer.
193**/
194ESR_ReturnCode PTimerStart(PTimer *timer)
195{
196  struct itimerspec expire_time;
197
198  if (timer == NULL) return ESR_INVALID_ARGUMENT;
199
200  expire_time.it_value.tv_sec = TIMER_MAX_VAL; /* set a large time for the timer */
201  expire_time.it_value.tv_nsec = 0;
202  return (timer_settime(timer->timer, 0, &expire_time, NULL) == 0 ?
203          ESR_SUCCESS :
204          ESR_NOT_SUPPORTED);
205}
206
207/**
208* Stops the timer.
209**/
210ESR_ReturnCode PTimerStop(PTimer *timer)
211{
212  struct itimerspec remaining;
213
214  if (timer == NULL) return ESR_INVALID_ARGUMENT;
215
216  if (timer_gettime(timer->timer, &remaining) != 0) return ESR_NOT_SUPPORTED;
217#if defined(__vxworks)
218  timer_cancel(timer->timer);
219#endif
220  timer->elapsed = (asr_uint32_t) ((TIMER_MAX_VAL - remaining.it_value.tv_sec) * SECOND2MSECOND
221						- remaining.it_value.tv_nsec / MSECOND2NSECOND);
222
223  return ESR_SUCCESS;
224}
225
226/**
227* Returns the timer elapsed time.  If the Timer is in the stopped state,
228* successive calls to getElapsed() will always return the same value.  If
229* the Timer is in the started state, successive calls will return the
230* elapsed time since the last time PTimerStart() was called.
231*/
232ESR_ReturnCode PTimerGetElapsed(PTimer *timer, asr_uint32_t* elapsed)
233{
234  if (timer == NULL || elapsed == NULL)
235    return ESR_INVALID_ARGUMENT;
236
237  if (timer->elapsed == 0) /* stop is not called */
238  {
239    struct itimerspec remaining;
240    if (timer_gettime(timer->timer, &remaining) != 0) return ESR_NOT_SUPPORTED;
241    *elapsed = (asr_uint32_t) ((TIMER_MAX_VAL - remaining.it_value.tv_sec) * SECOND2MSECOND
242						- remaining.it_value.tv_nsec / MSECOND2NSECOND);
243  }
244  else
245    *elapsed = timer->elapsed;
246
247  return ESR_SUCCESS;
248}
249
250
251/**
252* Resets the elapsed time to 0 and resets the reference time of the Timer.
253* This effectively reset the timer in the same state it was right after creation.
254**/
255ESR_ReturnCode PTimerReset(PTimer *timer)
256{
257  if (timer == NULL) return ESR_INVALID_ARGUMENT;
258  timer->elapsed = 0;
259  return ESR_SUCCESS;
260}
261
262#else
263#error "Ptimer not implemented for this platform."
264#endif
265