@@ -151,11 +151,72 @@ const MeterClass* const Platform_meterTypes[] = {
151151 NULL
152152};
153153
154+ static uint64_t Platform_nanosecondsPerMachTickNumer = 1 ;
155+ static uint64_t Platform_nanosecondsPerMachTickDenom = 1 ;
156+
154157static double Platform_nanosecondsPerSchedulerTick = -1 ;
155158
156159static mach_port_t iokit_port ; // the mach port used to initiate communication with IOKit
157160
161+ static void Platform_calculateNanosecondsPerMachTick (uint64_t * numer , uint64_t * denom ) {
162+ // Check if we can determine the timebase used on this system.
163+
164+ #ifdef __x86_64__
165+ /* WORKAROUND for `mach_timebase_info` giving incorrect values on M1 under Rosetta 2.
166+ * rdar://FB9546856 http://www.openradar.appspot.com/FB9546856
167+ *
168+ * We don't know exactly what feature/attribute of the M1 chip causes this mistake under Rosetta 2.
169+ * Until we have more Apple ARM chips to compare against, the best we can do is special-case
170+ * the "Apple M1" chip specifically when running under Rosetta 2.
171+ *
172+ * Rosetta 2 only supports x86-64, so skip this workaround when building for other architectures.
173+ */
174+
175+ bool isRunningUnderRosetta2 = Platform_isRunningTranslated ();
176+
177+ // Kernel versions >= 20.0.0 (macOS 11.0 AKA Big Sur) affected
178+ bool isBuggedVersion = 0 <= Platform_CompareKernelVersion ((KernelVersion ) {20 , 0 , 0 });
179+
180+ if (isRunningUnderRosetta2 && isBuggedVersion ) {
181+ // In this case `mach_timebase_info` provides the wrong value, so we hard-code the correct factor,
182+ // as determined from `mach_timebase_info` as if the process was running natively.
183+ * numer = 125 ;
184+ * denom = 3 ;
185+ return ;
186+ }
187+ #endif
188+
189+ #ifdef HAVE_MACH_TIMEBASE_INFO
190+ mach_timebase_info_data_t info = { 0 };
191+ if (mach_timebase_info (& info ) == KERN_SUCCESS ) {
192+ * numer = info .numer ;
193+ * denom = info .denom ;
194+ return ;
195+ }
196+ #endif
197+
198+ // No info on actual timebase found; assume timebase in nanoseconds.
199+ * numer = 1 ;
200+ * denom = 1 ;
201+ }
202+
203+ // Converts ticks in the Mach "timebase" to nanoseconds.
204+ // See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
205+ uint64_t Platform_machTicksToNanoseconds (uint64_t mach_ticks ) {
206+ uint64_t ticks_quot = mach_ticks / Platform_nanosecondsPerMachTickDenom ;
207+ uint64_t ticks_rem = mach_ticks % Platform_nanosecondsPerMachTickDenom ;
208+
209+ uint64_t part1 = ticks_quot * Platform_nanosecondsPerMachTickNumer ;
210+
211+ // When Platform_nanosecondsPerMachTickDenom * Platform_nanosecondsPerMachTickNumer is less than 2^64, ticks_rem *
212+ // Platform_nanosecondsPerMachTickNumer will be less than 2^64 as well, i.e. never overflows.
213+ uint64_t part2 = (ticks_rem * Platform_nanosecondsPerMachTickNumer ) / Platform_nanosecondsPerMachTickDenom ;
214+
215+ return part1 + part2 ;
216+ }
217+
158218bool Platform_init (void ) {
219+ Platform_calculateNanosecondsPerMachTick (& Platform_nanosecondsPerMachTickNumer , & Platform_nanosecondsPerMachTickDenom );
159220
160221 // Determine the number of scheduler clock ticks per second
161222 errno = 0 ;
0 commit comments