Corrected.
[platform/upstream/glib.git] / gtimer.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include "glib.h"
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif /* HAVE_UNISTD_H */
39 #ifndef G_OS_WIN32
40 #include <sys/time.h>
41 #include <time.h>
42 #include <errno.h>
43 #endif /* G_OS_WIN32 */
44
45 #ifdef G_OS_WIN32
46 #include <windows.h>
47 #endif /* G_OS_WIN32 */
48
49 typedef struct _GRealTimer GRealTimer;
50
51 struct _GRealTimer
52 {
53 #ifdef G_OS_WIN32
54   DWORD start;
55   DWORD end;
56 #else /* !G_OS_WIN32 */
57   struct timeval start;
58   struct timeval end;
59 #endif /* !G_OS_WIN32 */
60
61   guint active : 1;
62 };
63
64 #ifdef G_OS_WIN32
65 #  define GETTIME(v) \
66      v = GetTickCount ()
67 #else /* !G_OS_WIN32 */
68 #  define GETTIME(v) \
69      gettimeofday (&v, NULL)
70 #endif /* !G_OS_WIN32 */
71
72 GTimer*
73 g_timer_new (void)
74 {
75   GRealTimer *timer;
76
77   timer = g_new (GRealTimer, 1);
78   timer->active = TRUE;
79
80   GETTIME (timer->start);
81
82   return ((GTimer*) timer);
83 }
84
85 void
86 g_timer_destroy (GTimer *timer)
87 {
88   g_return_if_fail (timer != NULL);
89
90   g_free (timer);
91 }
92
93 void
94 g_timer_start (GTimer *timer)
95 {
96   GRealTimer *rtimer;
97
98   g_return_if_fail (timer != NULL);
99
100   rtimer = (GRealTimer*) timer;
101   rtimer->active = TRUE;
102
103   GETTIME (rtimer->start);
104 }
105
106 void
107 g_timer_stop (GTimer *timer)
108 {
109   GRealTimer *rtimer;
110
111   g_return_if_fail (timer != NULL);
112
113   rtimer = (GRealTimer*) timer;
114   rtimer->active = FALSE;
115
116   GETTIME(rtimer->end);
117 }
118
119 void
120 g_timer_reset (GTimer *timer)
121 {
122   GRealTimer *rtimer;
123
124   g_return_if_fail (timer != NULL);
125
126   rtimer = (GRealTimer*) timer;
127
128   GETTIME (rtimer->start);
129 }
130
131 gdouble
132 g_timer_elapsed (GTimer *timer,
133                  gulong *microseconds)
134 {
135   GRealTimer *rtimer;
136   gdouble total;
137 #ifndef G_OS_WIN32
138   struct timeval elapsed;
139 #endif /* G_OS_WIN32 */
140
141   g_return_val_if_fail (timer != NULL, 0);
142
143   rtimer = (GRealTimer*) timer;
144
145 #ifdef G_OS_WIN32
146   if (rtimer->active)
147     rtimer->end = GetTickCount ();
148
149   /* Check for wraparound, which happens every 49.7 days. */
150   if (rtimer->end < rtimer->start)
151     total = (UINT_MAX - (rtimer->start - rtimer->end)) / 1000.0;
152   else
153     total = (rtimer->end - rtimer->start) / 1000.0;
154
155   if (microseconds)
156     {
157       if (rtimer->end < rtimer->start)
158         *microseconds =
159           ((UINT_MAX - (rtimer->start - rtimer->end)) % 1000) * 1000;
160       else
161         *microseconds =
162           ((rtimer->end - rtimer->start) % 1000) * 1000;
163     }
164 #else /* !G_OS_WIN32 */
165   if (rtimer->active)
166     gettimeofday (&rtimer->end, NULL);
167
168   if (rtimer->start.tv_usec > rtimer->end.tv_usec)
169     {
170       rtimer->end.tv_usec += G_USEC_PER_SEC;
171       rtimer->end.tv_sec--;
172     }
173
174   elapsed.tv_usec = rtimer->end.tv_usec - rtimer->start.tv_usec;
175   elapsed.tv_sec = rtimer->end.tv_sec - rtimer->start.tv_sec;
176
177   total = elapsed.tv_sec + ((gdouble) elapsed.tv_usec / 1e6);
178   if (total < 0)
179     {
180       total = 0;
181
182       if (microseconds)
183         *microseconds = 0;
184     }
185   else if (microseconds)
186     *microseconds = elapsed.tv_usec;
187
188 #endif /* !G_OS_WIN32 */
189
190   return total;
191 }
192
193 void
194 g_usleep (gulong microseconds)
195 {
196 #ifdef G_OS_WIN32
197   Sleep (microseconds / 1000);
198 #else /* !G_OS_WIN32 */
199 # ifdef HAVE_NANOSLEEP
200   struct timespec request, remaining;
201   request.tv_sec = microseconds / G_USEC_PER_SEC;
202   request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
203   while (nanosleep (&request, &remaining) == EINTR)
204     request = remaining;
205 # else /* !HAVE_NANOSLEEP */
206   if (g_thread_supported ())
207     {
208       static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
209       static GCond* cond = NULL;
210       GTimeVal end_time;
211       
212       g_get_current_time (&end_time);
213       if (microseconds > G_MAXLONG)
214         {
215           microseconds -= G_MAXLONG;
216           g_time_val_add (&end_time, G_MAXLONG);
217         }
218       g_time_val_add (&end_time, microseconds);
219
220       g_static_mutex_lock (&mutex);
221       
222       if (!cond)
223         cond = g_cond_new ();
224       
225       while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex), 
226                                 &end_time))
227         /* do nothing */;
228       
229       g_static_mutex_unlock (&mutex);
230     }
231   else
232     {
233       struct timeval tv;
234       tv.tv_sec = microseconds / G_USEC_PER_SEC;
235       tv.tv_usec = microseconds % G_USEC_PER_SEC;
236       select(0, NULL, NULL, NULL, &tv);
237     }
238 # endif /* !HAVE_NANOSLEEP */
239 #endif /* !G_OS_WIN32 */
240 }
241
242 /**
243  * g_time_val_add:
244  * @time: a #GTimeVal
245  * @microseconds: number of microseconds to add to @time
246  *
247  * Adds the given number of microseconds to @time. @microseconds can
248  * also be negative to decrease the value of @time.
249  **/
250 void 
251 g_time_val_add (GTimeVal *time, glong microseconds)
252 {
253   g_return_if_fail (time->tv_usec >= 0 && time->tv_usec < G_USEC_PER_SEC);
254
255   if (microseconds >= 0)
256     {
257       time->tv_usec += microseconds % G_USEC_PER_SEC;
258       time->tv_sec += microseconds / G_USEC_PER_SEC;
259       if (time->tv_usec >= G_USEC_PER_SEC)
260        {
261          time->tv_usec -= G_USEC_PER_SEC;
262          time->tv_sec++;
263        }
264     }
265   else
266     {
267       microseconds *= -1;
268       time->tv_usec -= microseconds % G_USEC_PER_SEC;
269       time->tv_sec -= microseconds / G_USEC_PER_SEC;
270       if (time->tv_usec < 0)
271        {
272          time->tv_usec += G_USEC_PER_SEC;
273          time->tv_sec--;
274        }      
275     }
276 }