VirtualBox

source: vbox/trunk/src/VBox/Runtime/timesup.cpp@ 4968

Last change on this file since 4968 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 7.1 KB
Line 
1/* $Id: timesup.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Time using SUPLib.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP RTLOGGROUP_TIME
23#include <iprt/time.h>
24#include <iprt/asm.h>
25#include <iprt/assert.h>
26#include <iprt/err.h>
27#include <VBox/sup.h>
28#include "internal/time.h"
29
30
31/*******************************************************************************
32* Global Variables *
33*******************************************************************************/
34#ifndef IN_GUEST
35/** The previously returned nano TS.
36 * This handles TSC drift on SMP systems and expired interval.
37 * This is a valid range u64NanoTS to u64NanoTS + 1000000000 (ie. 1sec).
38 */
39static uint64_t volatile s_u64PrevNanoTS = 0;
40/**
41 * Number of times we've had to resort to 1ns walking. */
42static uint32_t volatile g_c1nsSteps = 0;
43#endif
44
45
46/**
47 * Calculate NanoTS using the information in the global information page (GIP)
48 * which the support library (SUPLib) exports.
49 *
50 * This function guarantees that the returned timestamp is later (in time) than
51 * any previous calls in the same thread.
52 *
53 * @returns Nanosecond timestamp.
54 *
55 * @remark The way the ever increasing time guarantee is currently implemented means
56 * that if you call this function at a freqency higher than 1GHz you're in for
57 * trouble. We currently assume that no idiot will do that for real life purposes.
58 */
59DECLINLINE(uint64_t) rtTimeNanoTSInternal(void)
60{
61#ifndef IN_GUEST
62 uint64_t u64Delta;
63 uint32_t u32NanoTSFactor0;
64 uint64_t u64TSC;
65 uint64_t u64NanoTS;
66 uint32_t u32UpdateIntervalTSC;
67 uint32_t u32TransactionId;
68 PSUPGLOBALINFOPAGE pGip;
69
70 /*
71 * Read the data.
72 */
73 for (;;)
74 {
75 pGip = g_pSUPGlobalInfoPage;
76#ifdef IN_RING3
77 if (!pGip || pGip->u32Magic != SUPGLOBALINFOPAGE_MAGIC)
78 return RTTimeSystemNanoTS();
79#endif
80
81 if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
82 {
83 u32TransactionId = pGip->aCPUs[0].u32TransactionId;
84#ifdef RT_OS_L4
85 Assert((u32TransactionId & 1) == 0);
86#endif
87 u32UpdateIntervalTSC = pGip->aCPUs[0].u32UpdateIntervalTSC;
88 u64NanoTS = pGip->aCPUs[0].u64NanoTS;
89 u64TSC = pGip->aCPUs[0].u64TSC;
90 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
91 u64Delta = ASMReadTSC();
92 if (RT_UNLIKELY( pGip->aCPUs[0].u32TransactionId != u32TransactionId
93 || (u32TransactionId & 1)))
94 continue;
95 }
96 else
97 {
98 /* SUPGIPMODE_ASYNC_TSC */
99 PSUPGIPCPU pGipCpu;
100
101 uint8_t u8ApicId = ASMGetApicId();
102 if (RT_LIKELY(u8ApicId < RT_ELEMENTS(pGip->aCPUs)))
103 pGipCpu = &pGip->aCPUs[u8ApicId];
104 else
105 {
106 AssertMsgFailed(("%x\n", u8ApicId));
107 pGipCpu = &pGip->aCPUs[0];
108 }
109
110 u32TransactionId = pGipCpu->u32TransactionId;
111#ifdef RT_OS_L4
112 Assert((u32TransactionId & 1) == 0);
113#endif
114 u32UpdateIntervalTSC = pGipCpu->u32UpdateIntervalTSC;
115 u64NanoTS = pGipCpu->u64NanoTS;
116 u64TSC = pGipCpu->u64TSC;
117 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
118 u64Delta = ASMReadTSC();
119 if (RT_UNLIKELY(u8ApicId != ASMGetApicId()))
120 continue;
121 if (RT_UNLIKELY( pGipCpu->u32TransactionId != u32TransactionId
122 || (u32TransactionId & 1)))
123 continue;
124 }
125 break;
126 }
127
128 /*
129 * Calc NanoTS delta.
130 */
131 u64Delta -= u64TSC;
132 if (u64Delta > u32UpdateIntervalTSC)
133 {
134 /*
135 * We've expired the interval. Do 1ns per call until we've
136 * got valid TSC deltas again (s_u64PrevNanoTS takes care of this).
137 */
138 u64Delta = u32UpdateIntervalTSC;
139 }
140#if !defined(_MSC_VER) || defined(RT_ARCH_AMD64) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
141 u64Delta = ASMMult2xU32RetU64((uint32_t)u64Delta, u32NanoTSFactor0);
142 u64Delta = ASMDivU64ByU32RetU32(u64Delta, u32UpdateIntervalTSC);
143#else
144 __asm
145 {
146 mov eax, dword ptr [u64Delta]
147 mul dword ptr [u32NanoTSFactor0]
148 div dword ptr [u32UpdateIntervalTSC]
149 mov dword ptr [u64Delta], eax
150 xor edx, edx
151 mov dword ptr [u64Delta + 4], edx
152 }
153#endif
154
155 /*
156 * The most frequent case is that the delta is either too old
157 * or that our timestamp is higher (relative to u64NanoTS) than it.
158 */
159 uint64_t u64;
160 uint64_t u64PrevNanoTS = ASMAtomicReadU64(&s_u64PrevNanoTS);
161 uint64_t u64DeltaPrev = u64PrevNanoTS - u64NanoTS;
162 if ( u64DeltaPrev > 1000000000 /* (invalid prev) */
163 || (uint32_t)u64DeltaPrev < (uint32_t)u64Delta) /* (we're later) */
164 {
165 u64 = u64Delta + u64NanoTS;
166 if (ASMAtomicCmpXchgU64(&s_u64PrevNanoTS, u64, u64PrevNanoTS))
167 return u64;
168 }
169 else
170 {
171 /*
172 * Our timestamp is lower than the last returned timestamp;
173 * advance 1ns beyond that.
174 */
175 u64Delta = u64DeltaPrev + 1;
176 u64 = u64Delta + u64NanoTS;
177 ASMAtomicIncU32(&g_c1nsSteps);
178 }
179
180 /*
181 * Attempt updating the previous value.
182 * u64 == timestamp, u64Delta == delta relative to u64NanoTS.
183 */
184 for (int cTries = 100;;)
185 {
186 u64PrevNanoTS = ASMAtomicReadU64(&s_u64PrevNanoTS);
187 u64DeltaPrev = u64PrevNanoTS - u64NanoTS;
188 if (u64DeltaPrev > u64Delta)
189 break;
190 if (ASMAtomicCmpXchgU64(&s_u64PrevNanoTS, u64, u64PrevNanoTS))
191 break;
192 if (--cTries <= 0)
193 {
194 AssertBreakpoint(); /* (recursion) */
195 break;
196 }
197 }
198
199 return u64;
200#else /* IN_GUEST */
201 return RTTimeSystemNanoTS();
202#endif /* IN_GUEST */
203}
204
205
206/**
207 * Gets the current nanosecond timestamp.
208 *
209 * @returns nanosecond timestamp.
210 */
211RTDECL(uint64_t) RTTimeNanoTS(void)
212{
213 return rtTimeNanoTSInternal();
214}
215
216
217/**
218 * Gets the current millisecond timestamp.
219 *
220 * @returns millisecond timestamp.
221 */
222RTDECL(uint64_t) RTTimeMilliTS(void)
223{
224 return rtTimeNanoTSInternal() / 1000000;
225}
226
227
228#ifndef IN_GUEST
229/**
230 * Debugging the time api.
231 *
232 * @returns the number of 1ns steps which has been applied by rtTimeNanoTSInternal().
233 */
234RTDECL(uint32_t) RTTime1nsSteps(void)
235{
236 return g_c1nsSteps;
237}
238#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette