patch 8.2.5057: using gettimeofday() for timeout is very inefficient

Problem:    Using gettimeofday() for timeout is very inefficient.
Solution:   Set a platform dependent timer. (Paul Ollis, closes #10505)
diff --git a/src/os_macosx.m b/src/os_macosx.m
index 0299fe4..1fdfbc7 100644
--- a/src/os_macosx.m
+++ b/src/os_macosx.m
@@ -23,6 +23,13 @@
  * X11 header files. */
 #define NO_X11_INCLUDES
 
+#include <stdbool.h>
+#include <mach/boolean.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+
+#include <dispatch/dispatch.h>
+
 #include "vim.h"
 #import <AppKit/AppKit.h>
 
@@ -208,6 +215,175 @@
 
 #endif /* FEAT_CLIPBOARD */
 
+#ifdef FEAT_RELTIME
+/*
+ * The following timer code is based on a Gist by Jorgen Lundman:
+ *
+ *     https://gist.github.com/lundman
+ */
+
+typedef struct macos_timer macos_timer_T;
+
+    static void
+_timer_cancel(void *arg UNUSED)
+{
+    // This is not currently used, but it might be useful in the future and
+    // it is non-trivial enough to provide as usable implementation.
+#   if 0
+    macos_timer_T *timerid = (macos_timer_T *)arg;
+
+    dispatch_release(timerid->tim_timer);
+    dispatch_release(timerid->tim_queue);
+    timerid->tim_timer = NULL;
+    timerid->tim_queue = NULL;
+    free(timerid);
+#   endif
+}
+
+    static void
+_timer_handler(void *arg)
+{
+    macos_timer_T *timerid = (macos_timer_T *)arg;
+    union sigval sv;
+
+    sv.sival_ptr = timerid->tim_arg;
+
+    if (timerid->tim_func != NULL)
+	timerid->tim_func(sv);
+}
+
+    static uint64_t
+itime_to_ns(const struct timespec *it)
+{
+    time_t   sec  = it->tv_sec;
+    long     nsec = it->tv_nsec;
+    uint64_t ns   = NSEC_PER_SEC * sec + nsec;
+
+    return ns == 0 ? DISPATCH_TIME_FOREVER : ns;
+}
+
+/*
+ * A partial emulation of the POSIX timer_create function.
+ *
+ * The limitations and differences include:
+ *
+ * - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid
+ *   values.
+ * - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time
+ *   source is used internally.
+ * - The only notification method supported is SIGEV_THREAD.
+ */
+    inline int
+timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)
+{
+    macos_timer_T *timer = NULL;
+
+    // We only support real time and monotonic clocks; and SIGEV_THREAD
+    // notification. In practice, there is no difference between the two
+    // types of clocks on MacOS - we always use the mach_machine_time
+    // source.
+    if (   (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+        || sevp->sigev_notify != SIGEV_THREAD)
+    {
+	semsg("clockid: %d %d", clockid, CLOCK_REALTIME);
+	semsg("notify:  %d %d", sevp->sigev_notify, SIGEV_THREAD);
+	errno = ENOTSUP;
+	return -1;
+    }
+
+    timer = (macos_timer_T *)malloc(sizeof(macos_timer_T));
+    if (timer == NULL)
+    {
+	errno = ENOMEM;
+	return -1;
+    }
+    *timerid = timer;
+
+    timer->tim_queue = dispatch_queue_create(
+	    "org.vim.timerqueue", NULL);
+    if (timer->tim_queue == NULL)
+    {
+	errno = ENOMEM;
+	return -1;
+    }
+
+    timer->tim_timer = dispatch_source_create(
+	    DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue);
+    if (timer->tim_timer == NULL)
+    {
+	errno = ENOMEM;
+	return -1;
+    }
+
+    timer->tim_func = sevp->sigev_notify_function;
+    timer->tim_arg = sevp->sigev_value.sival_ptr;
+
+    dispatch_set_context(timer->tim_timer, timer);
+    dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler);
+    dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel);
+
+    dispatch_resume(timer->tim_timer);
+
+    return 0;
+}
+
+/*
+ * A partial emulation of the POSIX timer_settime function.
+ *
+ * The limitations and differences include:
+ *
+ * - The flags argument is ignored. The supplied new_value is therfore
+ *   always treated as a relative time.
+ * - The old_value argument is ignored.
+ */
+    int
+timer_settime(
+    timer_t timerid,
+    int unused_flags UNUSED,
+    const struct itimerspec *new_value,
+    struct itimerspec *old_value UNUSED)
+{
+    uint64_t first_shot = itime_to_ns(&new_value->it_value);
+
+    if (timerid == NULL)
+	return 0;
+
+    if (first_shot == DISPATCH_TIME_FOREVER)
+    {
+	dispatch_source_set_timer(
+	    timerid->tim_timer, first_shot, first_shot, 0);
+    }
+    else
+    {
+	uint64_t interval = itime_to_ns(&new_value->it_interval);
+
+	dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot);
+	dispatch_source_set_timer(timerid->tim_timer, start, interval, 0);
+    }
+
+    return 0;
+}
+
+/*
+ * An emulation of the POSIX timer_delete function.
+ *
+ * Disabled because it is not currently used, but an implemented provided
+ * for completeness and possible future use.
+ */
+#if 0
+    int
+timer_delete(timer_t timerid)
+{
+    /* Calls _timer_cancel() */
+    if (timerid != NULL)
+	dispatch_source_cancel(timerid->tim_timer);
+
+    return 0;
+}
+#endif
+
+#endif /* FEAT_RELTIME */
+
 /* Lift the compiler warning suppression. */
 #if defined(__clang__) && defined(__STRICT_ANSI__)
 # pragma clang diagnostic pop