diff options
author | Paul Ollis <paul@cleversheep.org> | 2022-06-05 16:55:54 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-06-05 16:55:54 +0100 |
commit | 6574577cacd393ab7591fc776ea060eebc939e55 (patch) | |
tree | f583ca9957280e7086b8d14ef44127302829fd40 | |
parent | 1d97db3d987c05af88c30ad20f537bcf3024f9c1 (diff) |
patch 8.2.5057: using gettimeofday() for timeout is very inefficientv8.2.5057
Problem: Using gettimeofday() for timeout is very inefficient.
Solution: Set a platform dependent timer. (Paul Ollis, closes #10505)
-rwxr-xr-x | src/auto/configure | 94 | ||||
-rw-r--r-- | src/config.h.in | 1 | ||||
-rw-r--r-- | src/configure.ac | 40 | ||||
-rw-r--r-- | src/drawscreen.c | 8 | ||||
-rw-r--r-- | src/errors.h | 12 | ||||
-rw-r--r-- | src/evalfunc.c | 18 | ||||
-rw-r--r-- | src/ex_cmds.c | 10 | ||||
-rw-r--r-- | src/ex_getln.c | 8 | ||||
-rw-r--r-- | src/match.c | 16 | ||||
-rw-r--r-- | src/os_mac.h | 52 | ||||
-rw-r--r-- | src/os_macosx.m | 176 | ||||
-rw-r--r-- | src/os_unix.c | 214 | ||||
-rw-r--r-- | src/os_win32.c | 86 | ||||
-rw-r--r-- | src/proto/os_unix.pro | 4 | ||||
-rw-r--r-- | src/proto/os_win32.pro | 4 | ||||
-rw-r--r-- | src/proto/regexp.pro | 4 | ||||
-rw-r--r-- | src/quickfix.c | 2 | ||||
-rw-r--r-- | src/regexp.c | 31 | ||||
-rw-r--r-- | src/regexp.h | 2 | ||||
-rw-r--r-- | src/regexp_bt.c | 54 | ||||
-rw-r--r-- | src/regexp_nfa.c | 46 | ||||
-rw-r--r-- | src/screen.c | 5 | ||||
-rw-r--r-- | src/search.c | 82 | ||||
-rw-r--r-- | src/structs.h | 5 | ||||
-rw-r--r-- | src/syntax.c | 25 | ||||
-rw-r--r-- | src/testdir/test_hlsearch.vim | 11 | ||||
-rw-r--r-- | src/testdir/test_search.vim | 26 | ||||
-rw-r--r-- | src/testdir/test_syntax.vim | 15 | ||||
-rw-r--r-- | src/version.c | 2 |
29 files changed, 811 insertions, 242 deletions
diff --git a/src/auto/configure b/src/auto/configure index 8ed46c85f4..b261631ef4 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -762,7 +762,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -898,7 +897,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' @@ -1151,15 +1149,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1297,7 +1286,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1450,7 +1439,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -12737,7 +12725,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -12783,7 +12771,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -12807,7 +12795,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -12852,7 +12840,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -12876,7 +12864,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13080,6 +13068,76 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for timer_create" >&5 +$as_echo_n "checking for timer_create... " >&6; } +save_LIBS="$LIBS" +LIBS="$LIBS -lrt" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include<signal.h> +#include<time.h> +static void set_flag(union sigval) {} + +int +main () +{ + + struct timespec ts; + struct sigevent action = {0}; + timer_t timer_id; + + action.sigev_notify = SIGEV_THREAD; + action.sigev_notify_function = set_flag; + timer_create(CLOCK_REALTIME, &action, &timer_id); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; with -lrt" >&5 +$as_echo "yes; with -lrt" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h + +else + LIBS="$save_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include<signal.h> +#include<time.h> +static void set_flag(union sigval) {} + +int +main () +{ + + struct timespec ts; + struct sigevent action = {0}; + timer_t timer_id; + + action.sigev_notify = SIGEV_THREAD; + action.sigev_notify_function = set_flag; + timer_create(CLOCK_REALTIME, &action, &timer_id); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat() ignores a trailing slash" >&5 $as_echo_n "checking whether stat() ignores a trailing slash... " >&6; } if ${vim_cv_stat_ignores_slash+:} false; then : diff --git a/src/config.h.in b/src/config.h.in index 2653e8dda5..1786f2ff96 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -231,6 +231,7 @@ #undef HAVE_UTIME #undef HAVE_BIND_TEXTDOMAIN_CODESET #undef HAVE_MBLEN +#undef HAVE_TIMER_CREATE /* Define, if needed, for accessing large files. */ #undef _LARGE_FILES diff --git a/src/configure.ac b/src/configure.ac index 913129f665..a6a926e681 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -1,7 +1,8 @@ dnl configure.ac: autoconf script for Vim -dnl Process this file with autoconf 2.12 or 2.13 to produce "configure". -dnl Should also work with autoconf 2.54 and later. +dnl Process this file with autoconf 2.69 to produce "configure". +dnl This should also work with other versions of autoconf, but 2.70 and later +dnl generate lots of hard to fix "obsolete" warnings. AC_INIT(vim.h) AC_CONFIG_HEADER(auto/config.h:config.h.in) @@ -3812,6 +3813,41 @@ AC_TRY_COMPILE( AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ST_BLKSIZE), AC_MSG_RESULT(no)) +dnl Check for timer_create. It probably requires the 'rt' library. +AC_MSG_CHECKING([for timer_create]) +save_LIBS="$LIBS" +LIBS="$LIBS -lrt" +AC_TRY_LINK([ +#include<signal.h> +#include<time.h> +static void set_flag(union sigval) {} +], [ + struct timespec ts; + struct sigevent action = {0}; + timer_t timer_id; + + action.sigev_notify = SIGEV_THREAD; + action.sigev_notify_function = set_flag; + timer_create(CLOCK_REALTIME, &action, &timer_id); + ], + AC_MSG_RESULT(yes; with -lrt); AC_DEFINE(HAVE_TIMER_CREATE), + LIBS="$save_LIBS" + AC_TRY_LINK([ +#include<signal.h> +#include<time.h> +static void set_flag(union sigval) {} + ], [ + struct timespec ts; + struct sigevent action = {0}; + timer_t timer_id; + + action.sigev_notify = SIGEV_THREAD; + action.sigev_notify_function = set_flag; + timer_create(CLOCK_REALTIME, &action, &timer_id); + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_TIMER_CREATE), + AC_MSG_RESULT(no))) + AC_CACHE_CHECK([whether stat() ignores a trailing slash], [vim_cv_stat_ignores_slash], [ AC_RUN_IFELSE([AC_LANG_SOURCE([[ diff --git a/src/drawscreen.c b/src/drawscreen.c index ed8927127e..2c184bde0f 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -1474,9 +1474,6 @@ win_update(win_T *wp) #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) int save_got_int; #endif -#ifdef SYN_TIME_LIMIT - proftime_T syntax_tm; -#endif #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD) // This needs to be done only for the first window when update_screen() is @@ -2182,8 +2179,7 @@ win_update(win_T *wp) #endif #ifdef SYN_TIME_LIMIT // Set the time limit to 'redrawtime'. - profile_setlimit(p_rdt, &syntax_tm); - syn_set_timeout(&syntax_tm); + init_regexp_timeout(p_rdt); #endif #ifdef FEAT_FOLDING win_foldinfo.fi_level = 0; @@ -2695,7 +2691,7 @@ win_update(win_T *wp) } #ifdef SYN_TIME_LIMIT - syn_set_timeout(NULL); + disable_regexp_timeout(); #endif // Reset the type of redrawing required, the window has been updated. diff --git a/src/errors.h b/src/errors.h index 8ca2a3e2f7..109d9e8348 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3288,3 +3288,15 @@ EXTERN char e_bitshift_ops_must_be_postive[] EXTERN char e_argument_1_list_item_nr_dictionary_required[] INIT(= N_("E1284: Argument 1, list item %d: Dictionary required")); #endif +#ifdef FEAT_RELTIME +EXTERN char e_could_not_clear_timeout_str[] + INIT(= N_("E1285: Could not clear timeout: %s")); +EXTERN char e_could_not_set_timeout_str[] + INIT(= N_("E1286: Could not set timeout: %s")); +EXTERN char e_could_not_set_handler_for_timeout_str[] + INIT(= N_("E1287: Could not set handler for timeout: %s")); +EXTERN char e_could_not_reset_handler_for_timeout_str[] + INIT(= N_("E1288: Could not reset handler for timeout: %s")); +EXTERN char e_could_not_check_for_pending_sigalrm_str[] + INIT(= N_("E1289: Could not check for pending SIGALRM: %s")); +#endif diff --git a/src/evalfunc.c b/src/evalfunc.c index 75c3456a8f..ff30756dda 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -8439,7 +8439,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int retval = 0; // default: FAIL long lnum_stop = 0; #ifdef FEAT_RELTIME - proftime_T tm; long time_limit = 0; #endif int options = SEARCH_KEEP; @@ -8486,11 +8485,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } } -#ifdef FEAT_RELTIME - // Set the time limit, if there is one. - profile_setlimit(time_limit, &tm); -#endif - /* * This function does not accept SP_REPEAT and SP_RETCOUNT flags. * Check to make sure only those flags are set. @@ -8509,7 +8503,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) CLEAR_FIELD(sia); sia.sa_stop_lnum = (linenr_T)lnum_stop; #ifdef FEAT_RELTIME - sia.sa_tm = &tm; + sia.sa_tm = time_limit; #endif // Repeat until {skip} returns FALSE. @@ -8955,19 +8949,11 @@ do_searchpair( int use_skip = FALSE; int err; int options = SEARCH_KEEP; -#ifdef FEAT_RELTIME - proftime_T tm; -#endif // Make 'cpoptions' empty, the 'l' flag should not be used here. save_cpo = p_cpo; p_cpo = empty_option; -#ifdef FEAT_RELTIME - // Set the time limit, if there is one. - profile_setlimit(time_limit, &tm); -#endif - // Make two search patterns: start/end (pat2, for in nested pairs) and // start/middle/end (pat3, for the top pair). pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17); @@ -8998,7 +8984,7 @@ do_searchpair( CLEAR_FIELD(sia); sia.sa_stop_lnum = lnum_stop; #ifdef FEAT_RELTIME - sia.sa_tm = &tm; + sia.sa_tm = time_limit; #endif n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, options, RE_SEARCH, &sia); diff --git a/src/ex_cmds.c b/src/ex_cmds.c index e01d067275..34a740da24 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -4006,7 +4006,7 @@ ex_substitute(exarg_T *eap) ); ++lnum) { nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL, NULL); + (colnr_T)0, NULL); if (nmatch) { colnr_T copycol; @@ -4663,7 +4663,7 @@ skip: || nmatch_tl > 0 || (nmatch = vim_regexec_multi(®match, curwin, curbuf, sub_firstlnum, - matchcol, NULL, NULL)) == 0 + matchcol, NULL)) == 0 || regmatch.startpos[0].lnum > 0) { if (new_start != NULL) @@ -4728,7 +4728,7 @@ skip: } if (nmatch == -1 && !lastone) nmatch = vim_regexec_multi(®match, curwin, curbuf, - sub_firstlnum, matchcol, NULL, NULL); + sub_firstlnum, matchcol, NULL); /* * 5. break if there isn't another match in this line @@ -4992,7 +4992,7 @@ ex_global(exarg_T *eap) { lnum = curwin->w_cursor.lnum; match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL, NULL); + (colnr_T)0, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) global_exe_one(cmd, lnum); } @@ -5005,7 +5005,7 @@ ex_global(exarg_T *eap) { // a match on this line? match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL, NULL); + (colnr_T)0, NULL); if (regmatch.regprog == NULL) break; // re-compiling regprog failed if ((type == 'g' && match) || (type == 'v' && !match)) diff --git a/src/ex_getln.c b/src/ex_getln.c index f75d8c7a96..20d9520ed1 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -417,7 +417,6 @@ may_do_incsearch_highlighting( int found; // do_search() result pos_T end_pos; #ifdef FEAT_RELTIME - proftime_T tm; searchit_arg_T sia; #endif int next_char; @@ -484,10 +483,6 @@ may_do_incsearch_highlighting( cursor_off(); // so the user knows we're busy out_flush(); ++emsg_off; // so it doesn't beep if bad expr -#ifdef FEAT_RELTIME - // Set the time limit to half a second. - profile_setlimit(500L, &tm); -#endif if (!p_hls) search_flags += SEARCH_KEEP; if (search_first_line != 0) @@ -495,7 +490,8 @@ may_do_incsearch_highlighting( ccline.cmdbuff[skiplen + patlen] = NUL; #ifdef FEAT_RELTIME CLEAR_FIELD(sia); - sia.sa_tm = &tm; + // Set the time limit to half a second. + sia.sa_tm = 500; #endif found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim, ccline.cmdbuff + skiplen, count, search_flags, diff --git a/src/match.c b/src/match.c index 8280da7d57..72276527d3 100644 --- a/src/match.c +++ b/src/match.c @@ -330,10 +330,6 @@ init_search_hl(win_T *wp, match_T *search_hl) cur->hl.buf = wp->w_buffer; cur->hl.lnum = 0; cur->hl.first_lnum = 0; -# ifdef FEAT_RELTIME - // Set the time limit to 'redrawtime'. - profile_setlimit(p_rdt, &(cur->hl.tm)); -# endif cur = cur->next; } search_hl->buf = wp->w_buffer; @@ -424,6 +420,7 @@ next_search_hl( colnr_T matchcol; long nmatched; int called_emsg_before = called_emsg; + int timed_out = FALSE; // for :{range}s/pat only highlight inside the range if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL) @@ -451,7 +448,7 @@ next_search_hl( { # ifdef FEAT_RELTIME // Stop searching after passing the time limit. - if (profile_passed_limit(&(shl->tm))) + if (timed_out) { shl->lnum = 0; // no match found in time break; @@ -494,16 +491,9 @@ next_search_hl( int regprog_is_copy = (shl != search_hl && cur != NULL && shl == &cur->hl && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = FALSE; nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, - matchcol, -#ifdef FEAT_RELTIME - &(shl->tm), &timed_out -#else - NULL, NULL -#endif - ); + matchcol, &timed_out); // Copy the regprog, in case it got freed and recompiled. if (regprog_is_copy) cur->match.regprog = cur->hl.rm.regprog; diff --git a/src/os_mac.h b/src/os_mac.h index dbc0086e0d..f6ed1216ca 100644 --- a/src/os_mac.h +++ b/src/os_mac.h @@ -6,6 +6,9 @@ * Do ":help credits" in Vim to see a list of people who contributed. */ +#ifndef OS_MAC__H +#define OS_MAC__H + // Before Including the MacOS specific files, // let's set the OPAQUE_TOOLBOX_STRUCTS to 0 so we // can access the internal structures. @@ -266,3 +269,52 @@ // A Mac constant causing big problem to syntax highlighting #define UNKNOWN_CREATOR '\?\?\?\?' + +#ifdef FEAT_RELTIME + +# include <dispatch/dispatch.h> + +# if !defined(MAC_OS_X_VERSION_10_12) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12) +typedef int clockid_t; +# endif +# ifndef CLOCK_REALTIME +# define CLOCK_REALTIME 0 +# endif +# ifndef CLOCK_MONOTONIC +# define CLOCK_MONOTONIC 1 +# endif + +struct itimerspec +{ + struct timespec it_interval; // timer period + struct timespec it_value; // initial expiration +}; + +struct sigevent; + +struct macos_timer +{ + dispatch_queue_t tim_queue; + dispatch_source_t tim_timer; + void (*tim_func)(union sigval); + void *tim_arg; +}; + +typedef struct macos_timer *timer_t; + +extern int timer_create( + clockid_t clockid, + struct sigevent *sevp, + timer_t *timerid); + +extern int timer_delete(timer_t timerid); + +extern int timer_settime( + timer_t timerid, int flags, + const struct itimerspec *new_value, + struct itimerspec *unused); + +#endif // FEAT_RELTIME + +#endif // OS_MAC__H diff --git a/src/os_macosx.m b/src/os_macosx.m index 0299fe4134..1fdfbc70dd 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 @@ 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 diff --git a/src/os_unix.c b/src/os_unix.c index b786f8df07..28113fcb3b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -8256,3 +8256,217 @@ xsmp_close(void) } } #endif // USE_XSMP + +#if defined(FEAT_RELTIME) || defined(PROTO) +# if defined(HAVE_TIMER_CREATE) || defined(MACOS_X) +/* + * Implement timeout with timer_create() and timer_settime(). + */ +static int timeout_flag = FALSE; +static timer_t timer_id; +static int timer_created = FALSE; + +/* + * Callback for when the timer expires. + */ + static void +set_flag(union sigval _unused UNUSED) +{ + timeout_flag = TRUE; +} + +/* + * Stop any active timeout. + */ + void +stop_timeout(void) +{ + static struct itimerspec disarm = {{0, 0}, {0, 0}}; + + if (timer_created) + { + int ret = timer_settime(timer_id, 0, &disarm, NULL); + + if (ret < 0) + semsg(_(e_could_not_clear_timeout_str), strerror(errno)); + } + + // Clear the current timeout flag; any previous timeout should be + // considered _not_ triggered. + timeout_flag = FALSE; +} + +/* + * Start the timeout timer. + * + * The return value is a pointer to a flag that is initialised to FALSE. If the + * timeout expires, the flag is set to TRUE. This will only return pointers to + * static memory; i.e. any pointer returned by this function may always be + * safely dereferenced. + * + * This function is not expected to fail, but if it does it will still return a + * valid flag pointer; the flag will remain stuck as FALSE . + */ + const int * +start_timeout(long msec) +{ + struct itimerspec interval = { + {0, 0}, // Do not repeat. + {msec / 1000, (msec % 1000) * 1000000}}; // Timeout interval + int ret; + + // This is really the caller's responsibility, but let's make sure the + // previous timer has been stopped. + stop_timeout(); + timeout_flag = FALSE; + + if (!timer_created) + { + struct sigevent action = {0}; + + action.sigev_notify = SIGEV_THREAD; + action.sigev_notify_function = set_flag; + ret = timer_create(CLOCK_MONOTONIC, &action, &timer_id); + if (ret < 0) + { + semsg(_(e_could_not_set_timeout_str), strerror(errno)); + return &timeout_flag; + } + timer_created = TRUE; + } + + ret = timer_settime(timer_id, 0, &interval, NULL); + if (ret < 0) + semsg(_(e_could_not_set_timeout_str), strerror(errno)); + + return &timeout_flag; +} + +# else + +/* + * Implement timeout with setitimer() + */ +static struct itimerval prev_interval; +static struct sigaction prev_sigaction; +static int timeout_flag = FALSE; +static int timer_active = FALSE; +static int timer_handler_active = FALSE; +static int alarm_pending = FALSE; + +/* + * Handle SIGALRM for a timeout. + */ + static RETSIGTYPE +set_flag SIGDEFARG(sigarg) +{ + if (alarm_pending) + alarm_pending = FALSE; + else + timeout_flag = TRUE; +} + +/* + * Stop any active timeout. + */ + void +stop_timeout(void) +{ + static struct itimerval disarm = {{0, 0}, {0, 0}}; + int ret; + + if (timer_active) + |