summaryrefslogtreecommitdiff
path: root/src/lib/ecore/ecore_time.c
blob: d3bac94310cd23d6044ffafdd43fd336c65c69bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <sys/time.h>

#ifdef HAVE_EVIL
# include <Evil.h>
#endif

#if defined(__APPLE__) && defined(__MACH__)
# include <mach/mach_time.h>
#endif

#include <time.h>

#include "Ecore.h"
#include "ecore_private.h"

#if defined (HAVE_CLOCK_GETTIME) || defined (EXOTIC_PROVIDE_CLOCK_GETTIME)
static clockid_t _ecore_time_clock_id = -1;
#elif defined(__APPLE__) && defined(__MACH__)
static double _ecore_time_clock_conversion = 1e-9;
#endif
double _ecore_time_loop_time = -1.0;

/**
 * @addtogroup Ecore_Time_Group
 *
 * @{
 */

/**
 * Retrieves the current system time as a floating point value in seconds.
 *
 * This uses a monotonic clock and thus never goes back in time while
 * machine is live (even if user changes time or timezone changes,
 * however it may be reset whenever the machine is restarted).
 *
 * @see ecore_loop_time_get().
 * @see ecore_time_unix_get().
 *
 * @return The number of seconds. Start time is not defined (it may be
 *         when the machine was booted, unix time, etc), all it is
 *         defined is that it never goes backwards (unless you got big critical
 *         messages when the application started).
 */
EAPI double
ecore_time_get(void)
{
#if defined (HAVE_CLOCK_GETTIME) || defined (EXOTIC_PROVIDE_CLOCK_GETTIME) 
   struct timespec t;

   if (EINA_UNLIKELY(_ecore_time_clock_id < 0))
     return ecore_time_unix_get();

   if (EINA_UNLIKELY(clock_gettime(_ecore_time_clock_id, &t)))
     {
        CRIT("Cannot get current time.");
        /* Try to at least return the latest value retrieved*/
        return _ecore_time_loop_time;
     }

   return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
#elif defined(HAVE_EVIL)
   return evil_time_get();
#elif defined(__APPLE__) && defined(__MACH__)
   return _ecore_time_clock_conversion * (double)mach_absolute_time();
#else
   return ecore_time_unix_get();
#endif
}

/**
 * Retrieves the current UNIX time as a floating point value in seconds.
 *
 * @see ecore_time_get().
 * @see ecore_loop_time_get().
 *
 * @return  The number of seconds since 12.00AM 1st January 1970.
 */
EAPI double
ecore_time_unix_get(void)
{
#ifdef HAVE_GETTIMEOFDAY
   struct timeval timev;

   gettimeofday(&timev, NULL);
   return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
#else
# error "Your platform isn't supported yet"
#endif
}

/**
 * Retrieves the time at which the last loop stopped waiting for timeouts or
 * events.
 *
 * This gets the time that the main loop ceased waiting for timouts and/or
 * events to come in or for signals or any other interrupt source. This should
 * be considered a reference point for all time based activity that should
 * calculate its timepoint from the return of ecore_loop_time_get(). Use this
 * UNLESS you absolutely must get the current actual timepoint - then use
 * ecore_time_get(). Note that this time is meant to be used as relative to
 * other times obtained on this run. If you need absolute time references, use
 * ecore_time_unix_get() instead.
 *
 * This function can be called before any loop has ever been run, but either
 * ecore_init() or ecore_time_get() must have been called once.
 *
 * @return The number of seconds. Start time is not defined (it may be
 *         when the machine was booted, unix time, etc), all it is
 *         defined is that it never goes backwards (unless you got big critical
 *         messages when the application started).
 */
EAPI double
ecore_loop_time_get(void)
{
   return _ecore_time_loop_time;
}

/**
 * @}
 */

/**********************   Internal methods   ********************************/

/* TODO: Documentation says "All  implementations  support  the  system-wide
 * real-time clock, which is identified by CLOCK_REALTIME. Check if the fallback
 * to unix time (without specifying the resolution) might be removed
 */
void
_ecore_time_init(void)
{
#if defined (HAVE_CLOCK_GETTIME) || defined (EXOTIC_PROVIDE_CLOCK_GETTIME)
   struct timespec t;

   if (_ecore_time_clock_id != -1) return;

   if (!clock_gettime(CLOCK_MONOTONIC, &t))
     {
        _ecore_time_clock_id = CLOCK_MONOTONIC;
        DBG("using CLOCK_MONOTONIC.");
     }
   else if (!clock_gettime(CLOCK_REALTIME, &t))
     {
        /* may go backwards */
         _ecore_time_clock_id = CLOCK_REALTIME;
         WRN("CLOCK_MONOTONIC not available. Fallback to CLOCK_REALTIME.");
     }
   else
     {
        _ecore_time_clock_id = -2;
        CRIT("Cannot get a valid clock_gettime() clock id! "
             "Fallback to unix time.");
     }
#else
# ifndef HAVE_EVIL
#  if defined(__APPLE__) && defined(__MACH__)
   mach_timebase_info_data_t info;
   kern_return_t err = mach_timebase_info(&info);
   if (err == 0)
     {
        _ecore_time_clock_conversion = 1e-9 * (double)info.numer / (double)info.denom;
     }
   else
     {
        WRN("Unable to get timebase info. Fallback to nanoseconds.");
     }
#  else
#  warning "Your platform isn't supported yet"
   CRIT("Platform does not support clock_gettime. "
        "Fallback to unix time.");
#  endif
# endif
#endif

   _ecore_time_loop_time = ecore_time_get();
}