From 90f6997ad884a7c9270328ab7c283501e6ed5883 Mon Sep 17 00:00:00 2001 From: fanyang-mono Date: Tue, 14 Apr 2020 17:46:36 -0400 Subject: [PATCH] Move all time functions to mono-time.c --- src/mono/mono/mini/mini-posix.c | 160 ++----------------------------------- src/mono/mono/utils/mono-time.c | 171 ++++++++++++++++++++++++++++++++++++++++ src/mono/mono/utils/mono-time.h | 5 ++ 3 files changed, 182 insertions(+), 154 deletions(-) diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c index 562141a..a0aa2ae 100644 --- a/src/mono/mono/mini/mini-posix.c +++ b/src/mono/mono/mini/mini-posix.c @@ -516,155 +516,6 @@ mono_runtime_cleanup_handlers (void) static volatile gint32 sampling_thread_running; -#ifdef HOST_DARWIN - -static clock_serv_t sampling_clock_service; - -static void -clock_init (MonoProfilerSampleMode mode) -{ - kern_return_t ret; - - do { - ret = host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &sampling_clock_service); - } while (ret == KERN_ABORTED); - - if (ret != KERN_SUCCESS) - g_error ("%s: host_get_clock_service () returned %d", __func__, ret); -} - -static void -clock_cleanup (void) -{ - kern_return_t ret; - - do { - ret = mach_port_deallocate (mach_task_self (), sampling_clock_service); - } while (ret == KERN_ABORTED); - - if (ret != KERN_SUCCESS) - g_error ("%s: mach_port_deallocate () returned %d", __func__, ret); -} - -static void -clock_sleep_ns_abs (guint64 ns_abs) -{ - kern_return_t ret; - mach_timespec_t then, remain_unused; - - then.tv_sec = ns_abs / 1000000000; - then.tv_nsec = ns_abs % 1000000000; - - do { - ret = clock_sleep (sampling_clock_service, TIME_ABSOLUTE, then, &remain_unused); - - if (ret != KERN_SUCCESS && ret != KERN_ABORTED) - g_error ("%s: clock_sleep () returned %d", __func__, ret); - } while (ret == KERN_ABORTED && mono_atomic_load_i32 (&sampling_thread_running)); -} - -#else - -static clockid_t sampling_posix_clock; - -static void -clock_init (MonoProfilerSampleMode mode) -{ - switch (mode) { - case MONO_PROFILER_SAMPLE_MODE_PROCESS: { - /* - * If we don't have clock_nanosleep (), measuring the process time - * makes very little sense as we can only use nanosleep () to sleep on - * real time. - */ -#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__) - struct timespec ts = { 0 }; - - /* - * Some systems (e.g. Windows Subsystem for Linux) declare the - * CLOCK_PROCESS_CPUTIME_ID clock but don't actually support it. For - * those systems, we fall back to CLOCK_MONOTONIC if we get EINVAL. - */ - if (clock_nanosleep (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME, &ts, NULL) != EINVAL) { - sampling_posix_clock = CLOCK_PROCESS_CPUTIME_ID; - break; - } -#endif - - // fallthrough - } - case MONO_PROFILER_SAMPLE_MODE_REAL: sampling_posix_clock = CLOCK_MONOTONIC; break; - default: g_assert_not_reached (); break; - } -} - -static void -clock_cleanup (void) -{ -} - -static void -clock_sleep_ns_abs (guint64 ns_abs) -{ -#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__) - int ret; - struct timespec then; - - then.tv_sec = ns_abs / 1000000000; - then.tv_nsec = ns_abs % 1000000000; - - do { - ret = clock_nanosleep (sampling_posix_clock, TIMER_ABSTIME, &then, NULL); - - if (ret != 0 && ret != EINTR) - g_error ("%s: clock_nanosleep () returned %d", __func__, ret); - } while (ret == EINTR && mono_atomic_load_i32 (&sampling_thread_running)); -#else - int ret; - gint64 diff; - struct timespec req; - - /* - * What follows is a crude attempt at emulating clock_nanosleep () on OSs - * which don't provide it (e.g. FreeBSD). - * - * The problem with nanosleep () is that if it is interrupted by a signal, - * time will drift as a result of having to restart the call after the - * signal handler has finished. For this reason, we avoid using the rem - * argument of nanosleep (). Instead, before every nanosleep () call, we - * check if enough time has passed to satisfy the sleep request. If yes, we - * simply return. If not, we calculate the difference and do another sleep. - * - * This should reduce the amount of drift that happens because we account - * for the time spent executing the signal handler, which nanosleep () is - * not guaranteed to do for the rem argument. - * - * The downside to this approach is that it is slightly expensive: We have - * to make an extra system call to retrieve the current time whenever we're - * going to restart a nanosleep () call. This is unlikely to be a problem - * in practice since the sampling thread won't be receiving many signals in - * the first place (it's a tools thread, so no STW), and because typical - * sleep periods for the thread are many orders of magnitude bigger than - * the time it takes to actually perform that system call (just a few - * nanoseconds). - */ - do { - diff = (gint64) ns_abs - (gint64) mono_clock_get_time_ns (); - - if (diff <= 0) - break; - - req.tv_sec = diff / 1000000000; - req.tv_nsec = diff % 1000000000; - - if ((ret = nanosleep (&req, NULL)) == -1 && errno != EINTR) - g_error ("%s: nanosleep () returned -1, errno = %d", __func__, errno); - } while (ret == -1 && mono_atomic_load_i32 (&sampling_thread_running)); -#endif -} - -#endif - static int profiler_signal; static volatile gint32 sampling_thread_exiting; static MonoOSEvent sampling_thread_exited; @@ -717,16 +568,17 @@ init: goto init; } - clock_init (mode); + mono_clock_init (); + mono_clock_init_for_profiler (mode); - for (guint64 sleep = mono_clock_get_time_ns (); mono_atomic_load_i32 (&sampling_thread_running); clock_sleep_ns_abs (sleep)) { + for (guint64 sleep = mono_clock_get_time_ns (); mono_atomic_load_i32 (&sampling_thread_running); mono_clock_sleep_ns_abs (sleep)) { uint32_t freq; MonoProfilerSampleMode new_mode; mono_profiler_get_sample_mode (NULL, &new_mode, &freq); if (new_mode != mode) { - clock_cleanup (); + mono_clock_cleanup (); goto init; } @@ -748,7 +600,7 @@ init: } FOREACH_THREAD_SAFE_END } - clock_cleanup (); + mono_clock_cleanup (); done: mono_atomic_store_i32 (&sampling_thread_exiting, 1); @@ -778,7 +630,7 @@ mono_runtime_shutdown_stat_profiler (void) * the sleep progresses very slowly as a result of the low CPU activity. * * We fix this by repeatedly sending the profiler signal to the sampler - * thread in order to interrupt the sleep. clock_sleep_ns_abs () will check + * thread in order to interrupt the sleep. mono_clock_sleep_ns_abs () will check * sampling_thread_running upon an interrupt and return immediately if it's * zero. profiler_signal_handler () has a special case to ignore the signal * for the sampler thread. diff --git a/src/mono/mono/utils/mono-time.c b/src/mono/mono/utils/mono-time.c index 2b3d23b..040cb34 100644 --- a/src/mono/mono/utils/mono-time.c +++ b/src/mono/mono/utils/mono-time.c @@ -17,8 +17,10 @@ #ifdef HOST_DARWIN #include +#include #endif + #include #include @@ -237,6 +239,37 @@ mono_100ns_datetime_from_timeval (struct timeval tv) static clock_serv_t sampling_clock_service; +void +mono_clock_init (void) +{ + kern_return_t ret; + + do { + ret = host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &sampling_clock_service); + } while (ret == KERN_ABORTED); + + if (ret != KERN_SUCCESS) + g_error ("%s: host_get_clock_service () returned %d", __func__, ret); +} + +void +mono_clock_init_for_profiler (MonoProfilerSampleMode mode) +{ +} + +static void +mono_clock_cleanup (void) +{ + kern_return_t ret; + + do { + ret = mach_port_deallocate (mach_task_self (), sampling_clock_service); + } while (ret == KERN_ABORTED); + + if (ret != KERN_SUCCESS) + g_error ("%s: mach_port_deallocate () returned %d", __func__, ret); +} + guint64 mono_clock_get_time_ns (void) { @@ -253,10 +286,68 @@ mono_clock_get_time_ns (void) return ((guint64) mach_ts.tv_sec * 1000000000) + (guint64) mach_ts.tv_nsec; } +void +mono_clock_sleep_ns_abs (guint64 ns_abs) +{ + kern_return_t ret; + mach_timespec_t then, remain_unused; + + then.tv_sec = ns_abs / 1000000000; + then.tv_nsec = ns_abs % 1000000000; + + do { + ret = clock_sleep (sampling_clock_service, TIME_ABSOLUTE, then, &remain_unused); + + if (ret != KERN_SUCCESS && ret != KERN_ABORTED) + g_error ("%s: clock_sleep () returned %d", __func__, ret); + } while (ret == KERN_ABORTED && mono_atomic_load_i32 (&sampling_thread_running)); +} + #elif defined(HOST_LINUX) static clockid_t sampling_posix_clock; +void +mono_clock_init (void) +{ +} + +void +mono_clock_init_for_profiler (MonoProfilerSampleMode mode) +{ + switch (mode) { + case MONO_PROFILER_SAMPLE_MODE_PROCESS: { + /* + * If we don't have clock_nanosleep (), measuring the process time + * makes very little sense as we can only use nanosleep () to sleep on + * real time. + */ +#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__) + struct timespec ts = { 0 }; + + /* + * Some systems (e.g. Windows Subsystem for Linux) declare the + * CLOCK_PROCESS_CPUTIME_ID clock but don't actually support it. For + * those systems, we fall back to CLOCK_MONOTONIC if we get EINVAL. + */ + if (clock_nanosleep (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME, &ts, NULL) != EINVAL) { + sampling_posix_clock = CLOCK_PROCESS_CPUTIME_ID; + break; + } +#endif + + // fallthrough + } + case MONO_PROFILER_SAMPLE_MODE_REAL: sampling_posix_clock = CLOCK_MONOTONIC; break; + default: g_assert_not_reached (); break; + } +} + +void +mono_clock_cleanup (void) +{ +} + guint64 mono_clock_get_time_ns (void) { @@ -268,7 +359,82 @@ mono_clock_get_time_ns (void) return ((guint64) ts.tv_sec * 1000000000) + (guint64) ts.tv_nsec; } +void +mono_clock_sleep_ns_abs (guint64 ns_abs) +{ +#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__) + int ret; + struct timespec then; + + then.tv_sec = ns_abs / 1000000000; + then.tv_nsec = ns_abs % 1000000000; + + do { + ret = clock_nanosleep (sampling_posix_clock, TIMER_ABSTIME, &then, NULL); + + if (ret != 0 && ret != EINTR) + g_error ("%s: clock_nanosleep () returned %d", __func__, ret); + } while (ret == EINTR && mono_atomic_load_i32 (&sampling_thread_running)); #else + int ret; + gint64 diff; + struct timespec req; + + /* + * What follows is a crude attempt at emulating clock_nanosleep () on OSs + * which don't provide it (e.g. FreeBSD). + * + * The problem with nanosleep () is that if it is interrupted by a signal, + * time will drift as a result of having to restart the call after the + * signal handler has finished. For this reason, we avoid using the rem + * argument of nanosleep (). Instead, before every nanosleep () call, we + * check if enough time has passed to satisfy the sleep request. If yes, we + * simply return. If not, we calculate the difference and do another sleep. + * + * This should reduce the amount of drift that happens because we account + * for the time spent executing the signal handler, which nanosleep () is + * not guaranteed to do for the rem argument. + * + * The downside to this approach is that it is slightly expensive: We have + * to make an extra system call to retrieve the current time whenever we're + * going to restart a nanosleep () call. This is unlikely to be a problem + * in practice since the sampling thread won't be receiving many signals in + * the first place (it's a tools thread, so no STW), and because typical + * sleep periods for the thread are many orders of magnitude bigger than + * the time it takes to actually perform that system call (just a few + * nanoseconds). + */ + do { + diff = (gint64) ns_abs - (gint64) clock_get_time_ns (); + + if (diff <= 0) + break; + + req.tv_sec = diff / 1000000000; + req.tv_nsec = diff % 1000000000; + + if ((ret = nanosleep (&req, NULL)) == -1 && errno != EINTR) + g_error ("%s: nanosleep () returned -1, errno = %d", __func__, errno); + } while (ret == -1 && mono_atomic_load_i32 (&sampling_thread_running)); +#endif +} + +#else + +void +mono_clock_init (void) +{ +} + +void +mono_clock_init_for_profiler (MonoProfilerSampleMode mode) +{ +} + +void +mono_clock_cleanup (void) +{ +} guint64 mono_clock_get_time_ns (void) @@ -277,4 +443,9 @@ mono_clock_get_time_ns (void) return 0; } +void +mono_clock_sleep_ns_abs (guint64 ns_abs) +{ +} + #endif diff --git a/src/mono/mono/utils/mono-time.h b/src/mono/mono/utils/mono-time.h index 6b4a95e..b7e4200 100644 --- a/src/mono/mono/utils/mono-time.h +++ b/src/mono/mono/utils/mono-time.h @@ -7,6 +7,7 @@ #include #include +#include #ifdef HAVE_SYS_TIME_H #include #endif @@ -29,7 +30,11 @@ gint64 mono_100ns_datetime (void); gint64 mono_100ns_datetime_from_timeval (struct timeval tv); #endif +void mono_clock_init (void); +void mono_clock_init_for_profiler (MonoProfilerSampleMode mode); +void mono_clock_cleanup (void); guint64 mono_clock_get_time_ns (void); +void mono_clock_sleep_ns_abs (guint64 ns_abs); /* Stopwatch class for internal runtime use */ typedef struct { -- 2.7.4