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:
committed by
Bram Moolenaar
parent
1d97db3d98
commit
6574577cac
176
src/os_macosx.m
176
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 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user