summaryrefslogtreecommitdiffstats
path: root/osdep/timer-darwin.c
blob: a5b3516699e7623289ea830595ef9b2fd0f5d9bc (plain)
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
/*
 * Precise timer routines using Mach kernel-space timing.
 *
 * It reports to be accurate by ~20us, unless the task is preempted. 
 *
 * (C) 2003 Dan Christiansen
 *
 * Released into the public domain.
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <mach/mach_time.h>
#include <mach/mach.h>
#include <mach/clock.h>

#include "../config.h"
#include "../mp_msg.h"
#include "timer.h"

/* Utility macros for mach_timespec_t - it uses nsec rather than usec */

/* returns time from t1 to t2, in seconds (as float) */
#define diff_time(t1, t2)						\
  (((t2).tv_sec - (t1).tv_sec) +					\
   ((t2).tv_nsec - (t1).tv_nsec) / 1e9)

/* returns time from t1 to t2, in microseconds (as integer) */
#define udiff_time(t1, t2)						\
  (((t2).tv_sec - (t1).tv_sec) * 1000000 +				\
   ((t2).tv_nsec - (t1).tv_nsec) / 1000)

/* returns float value of t, in seconds */
#define time_to_float(t)						\
  ((t).tv_sec + (t).tv_nsec / 1e9)

/* returns integer value of t, in microseconds */
#define time_to_usec(t)							\
  ((t).tv_sec * 1000000 + (t).tv_nsec / 1000)

/* sets ts to the value of f, in seconds */
#define float_to_time(f, ts)						\
  do {									\
    (ts).tv_sec = (unsigned int)(f);					\
    (ts).tv_nsec = (int)(((f) - (ts).sec) / 1000000000.0);		\
  } while (0)

/* sets ts to the value of i, in microseconds */
#define usec_to_time(i, ts)						\
  do {									\
    (ts).tv_sec = (i) / 1000000;					\
    (ts).tv_nsec = (i) % 1000000 * 1000;				\
  } while (0)

#define time_uadd(i, ts)						\
  do {									\
    (ts).tv_sec += (i) / 1000000;					\
    (ts).tv_nsec += (i) % 1000000 * 1000;				\
    while ((ts).tv_nsec > 1000000000) {					\
      (ts).tv_sec++;							\
      (ts).tv_nsec -= 1000000000;					\
    }									\
  } while (0)


/* global variables */
static double relative_time, startup_time;
static double timebase_ratio;
static mach_port_t clock_port;


/* sleep usec_delay microseconds */
int usec_sleep(int usec_delay)
{
  mach_timespec_t start_time, end_time;

  clock_get_time(clock_port, &start_time);

  end_time = start_time;
  time_uadd(usec_delay, end_time);

  clock_sleep(clock_port, TIME_ABSOLUTE, end_time, NULL);

  clock_get_time(clock_port, &end_time);

  return usec_delay - udiff_time(start_time, end_time);
}


/* Returns current time in microseconds */
unsigned int GetTimer()
{
  return (unsigned int)((mach_absolute_time() * timebase_ratio - startup_time)
			* 1e6);
}  

/* Returns current time in milliseconds */
unsigned int GetTimerMS()
{
  return (unsigned int)(GetTimer() / 1000);
}

/* Returns time spent between now and last call in seconds */
float GetRelativeTime()
{
  double last_time;

  if (!startup_time)
    InitTimer();
  
  last_time = relative_time;
  
  relative_time = mach_absolute_time() * timebase_ratio;

  return (float)(relative_time-last_time);
}

/* Initialize timer, must be called at least once at start */
void InitTimer()
{
  struct mach_timebase_info timebase;

  /* get base for mach_absolute_time() */
  mach_timebase_info(&timebase);
  timebase_ratio = (double)timebase.numer / (double)timebase.denom 
    * (double)1e-9;
  
  /* get mach port for the clock */
  host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &clock_port);
  
  /* prepare for GetRelativeTime() */
  relative_time = startup_time = 
    (double)(mach_absolute_time() * timebase_ratio);
}


#if 0
int main()
{
  const long delay = 0.001*1e6;
  const unsigned short attempts = 100;
  int i,j[attempts],t[attempts],r[attempts];
  double sqtotal;
  double total;
  
  InitTimer();

  for (i = 0; i < attempts; i++) {
    t[i] = j[i] = GetTimer();
    r[i] = usec_sleep(delay);
      j[i] = delay-(GetTimer() - j[i]);
      fflush(stdout);
  }

  for (i = 0; i < attempts; i++) {
    sqtotal += j[i]*j[i];
    total += j[i];
    printf("%2i=%0.06g  \tr: %9i\tj: %9i\tr - j:%9i\n",
	   i, t[i] / 1e6, r[i], j[i], r[i] - j[i]);
  }
  
  printf("attempts: %i\ttotal=%g\trms=%g\tavg=%g\n", attempts, total, 
	 sqrt(sqtotal/attempts),total/attempts);
  
  return 0;
}
#endif