0
0
mirror of https://github.com/vim/vim.git synced 2025-11-01 10:07:16 -04:00

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)
This commit is contained in:
Paul Ollis
2022-06-05 16:55:54 +01:00
committed by Bram Moolenaar
parent 1d97db3d98
commit 6574577cac
29 changed files with 811 additions and 242 deletions

View File

@@ -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 @@ releasepool:
#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