Move all time functions to mono-time.c
authorfanyang-mono <yangfan@microsoft.com>
Tue, 14 Apr 2020 21:46:36 +0000 (17:46 -0400)
committerfanyang-mono <yangfan@microsoft.com>
Tue, 14 Apr 2020 21:46:36 +0000 (17:46 -0400)
src/mono/mono/mini/mini-posix.c
src/mono/mono/utils/mono-time.c
src/mono/mono/utils/mono-time.h

index 562141a..a0aa2ae 100644 (file)
@@ -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.
index 2b3d23b..040cb34 100644 (file)
 
 #ifdef HOST_DARWIN
 #include <mach/clock.h>
+#include <mach/mach.h>
 #endif
 
+
 #include <mono/utils/mono-time.h>
 #include <mono/utils/atomic.h>
 
@@ -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
index 6b4a95e..b7e4200 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <mono/utils/mono-compiler.h>
 #include <glib.h>
+#include <mono/metadata/profiler.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #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 {