# HG changeset patch # User Bram Moolenaar # Date 1654444808 -7200 # Node ID b90bca860b5a4681db8d6f0a0c5bec7de1c67063 # Parent 8bf8001ef6d5bd0c7256bb94bc3b98c8262d1e75 patch 8.2.5057: using gettimeofday() for timeout is very inefficient Commit: https://github.com/vim/vim/commit/6574577cacd393ab7591fc776ea060eebc939e55 Author: Paul Ollis Date: Sun Jun 5 16:55:54 2022 +0100 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 https://github.com/vim/vim/issues/10505) diff --git a/src/auto/configure b/src/auto/configure --- 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 director --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_obj 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_obj 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 @@ else 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 +#include +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 +#include +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 --- 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 --- 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 +#include +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 +#include +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 --- 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 --- a/src/errors.h +++ b/src/errors.h @@ -3288,3 +3288,15 @@ EXTERN char e_bitshift_ops_must_be_posti 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 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -8439,7 +8439,6 @@ search_cmn(typval_T *argvars, pos_T *mat 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 *mat } } -#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 *mat 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 --- 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 --- 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 --- a/src/match.c +++ b/src/match.c @@ -330,10 +330,6 @@ init_search_hl(win_T *wp, match_T *searc 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 --- 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 + +# 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 --- a/src/os_macosx.m +++ b/src/os_macosx.m @@ -23,6 +23,13 @@ * X11 header files. */ #define NO_X11_INCLUDES +#include +#include +#include +#include + +#include + #include "vim.h" #import @@ -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 --- 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) + { + timer_active = FALSE; + ret = setitimer(ITIMER_REAL, &disarm, &prev_interval); + if (ret < 0) + // Should only get here as a result of coding errors. + semsg(_(e_could_not_clear_timeout_str), strerror(errno)); + } + + if (timer_handler_active) + { + timer_handler_active = FALSE; + ret = sigaction(SIGALRM, &prev_sigaction, NULL); + if (ret < 0) + // Should only get here as a result of coding errors. + semsg(_(e_could_not_reset_handler_for_timeout_str), + strerror(errno)); + } + timeout_flag = 0; +} + +/* + * 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 itimerval interval = { + {0, 0}, // Do not repeat. + {msec / 1000, (msec % 1000) * 1000}}; // Timeout interval + struct sigaction handle_alarm; + int ret; + sigset_t sigs; + sigset_t saved_sigs; + + // This is really the caller's responsibility, but let's make sure the + // previous timer has been stopped. + stop_timeout(); + + // There is a small chance that SIGALRM is pending and so the handler must + // ignore it on the first call. + alarm_pending = FALSE; + ret = sigemptyset(&sigs); + ret = ret == 0 ? sigaddset(&sigs, SIGALRM) : ret; + ret = ret == 0 ? sigprocmask(SIG_BLOCK, &sigs, &saved_sigs) : ret; + timeout_flag = FALSE; + ret = ret == 0 ? sigpending(&sigs) : ret; + if (ret == 0) + { + alarm_pending = sigismember(&sigs, SIGALRM); + ret = ret == 0 ? sigprocmask(SIG_SETMASK, &saved_sigs, NULL) : ret; + } + if (unlikely(ret != 0 || alarm_pending < 0)) + { + // Just catching coding errors. Write an error message, but carry on. + semsg(_(e_could_not_check_for_pending_sigalrm_str), strerror(errno)); + alarm_pending = FALSE; + } + + // Set up the alarm handler first. + ret = sigemptyset(&handle_alarm.sa_mask); + handle_alarm.sa_handler = set_flag; + handle_alarm.sa_flags = 0; + ret = ret == 0 ? sigaction(SIGALRM, &handle_alarm, &prev_sigaction) : ret; + if (ret < 0) + { + // Should only get here as a result of coding errors. + semsg(_(e_could_not_set_handler_for_timeout_str), strerror(errno)); + return &timeout_flag; + } + timer_handler_active = TRUE; + + // Set up the interval timer once the alarm handler is in place. + ret = setitimer(ITIMER_REAL, &interval, &prev_interval); + if (ret < 0) + { + // Should only get here as a result of coding errors. + semsg(_(e_could_not_set_timeout_str), strerror(errno)); + stop_timeout(); + return &timeout_flag; + } + + timer_active = TRUE; + return &timeout_flag; +} +# endif // HAVE_TIMER_CREATE +#endif // FEAT_RELTIME diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -100,6 +100,8 @@ typedef char * LPCSTR; typedef char * LPWSTR; typedef int ACCESS_MASK; typedef int BOOL; +typedef int BOOLEAN; +typedef int CALLBACK; typedef int COLORREF; typedef int CONSOLE_CURSOR_INFO; typedef int COORD; @@ -7327,6 +7329,7 @@ typedef struct _FILE_EA_INFORMATION_ { ULONG EaSize; } FILE_EA_INFORMATION_, *PFILE_EA_INFORMATION_; +#ifndef PROTO typedef NTSTATUS (NTAPI *PfnNtOpenFile)( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, @@ -7367,6 +7370,7 @@ PfnNtSetEaFile pNtSetEaFile = NULL; PfnNtQueryEaFile pNtQueryEaFile = NULL; PfnNtQueryInformationFile pNtQueryInformationFile = NULL; PfnRtlInitUnicodeString pRtlInitUnicodeString = NULL; +#endif /* * Load ntdll.dll functions. @@ -8315,3 +8319,85 @@ GetWin32Error(void) } return msg; } + +#if defined(FEAT_RELTIME) || defined(PROTO) +static HANDLE timer_handle; +static int timer_active = FALSE; + +/* + * Calls to start_timeout alternate the return value pointer between the two + * entries in timeout_flags. If the previously active timeout is very close to + * expiring when start_timeout() is called then a race condition means that the + * set_flag() function may still be invoked after the previous timer is + * deleted. Ping-ponging between the two flags prevents this causing 'fake' + * timeouts. + */ +static int timeout_flags[2]; +static int flag_idx = 0; +static int *timeout_flag = &timeout_flags[0]; + + + static void CALLBACK +set_flag(void *param, BOOLEAN unused2) +{ + int *timeout_flag = (int *)param; + + *timeout_flag = TRUE; +} + +/* + * Stop any active timeout. + */ + void +stop_timeout(void) +{ + if (timer_active) + { + BOOL ret = DeleteTimerQueueTimer(NULL, timer_handle, NULL); + timer_active = FALSE; + if (!ret && GetLastError() != ERROR_IO_PENDING) + { + semsg(_(e_could_not_clear_timeout_str), GetWin32Error()); + } + } + *timeout_flag = FALSE; +} + +/* + * Start the timeout timer. + * + * The period is defined in milliseconds. + * + * The return value is a pointer to a flag that is initialised to 0. If the + * timeout expires, the flag is set to 1. 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 still returns a + * valid flag pointer; the flag will remain stuck at zero. + */ + const int * +start_timeout(long msec) +{ + UINT interval = (UINT)msec; + BOOL ret; + + timeout_flag = &timeout_flags[flag_idx]; + + stop_timeout(); + ret = CreateTimerQueueTimer( + &timer_handle, NULL, set_flag, timeout_flag, + (DWORD)msec, 0, WT_EXECUTEDEFAULT); + if (!ret) + { + semsg(_(e_could_not_set_timeout_str), GetWin32Error()); + } + else + { + flag_idx = (flag_idx + 1) % 2; + timer_active = TRUE; + *timeout_flag = FALSE; + } + return timeout_flag; +} +#endif diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -72,6 +72,7 @@ int mch_expand_wildcards(int num_pat, ch int mch_has_exp_wildcard(char_u *p); int mch_has_wildcard(char_u *p); int mch_rename(const char *src, const char *dest); +int gpm_available(void); int gpm_enabled(void); int mch_libcall(char_u *libname, char_u *funcname, char_u *argstring, int argint, char_u **string_result, int *number_result); void setup_term_clip(void); @@ -85,5 +86,6 @@ void clip_xterm_set_selection(Clipboard_ int xsmp_handle_requests(void); void xsmp_init(void); void xsmp_close(void); -int gpm_available(void); +void stop_timeout(void); +const int *start_timeout(long msec); /* vim: set ft=c : */ diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -83,5 +83,7 @@ int get_conpty_type(void); int is_conpty_stable(void); int get_conpty_fix_type(void); void resize_console_buf(void); -char * GetWin32Error(void); +char *GetWin32Error(void); +void stop_timeout(void); +const int *start_timeout(long msec); /* vim: set ft=c : */ diff --git a/src/proto/regexp.pro b/src/proto/regexp.pro --- a/src/proto/regexp.pro +++ b/src/proto/regexp.pro @@ -1,4 +1,6 @@ /* regexp.c */ +void init_regexp_timeout(long msec); +void disable_regexp_timeout(void); int re_multiline(regprog_T *prog); char_u *skip_regexp(char_u *startp, int delim, int magic); char_u *skip_regexp_err(char_u *startp, int delim, int magic); @@ -18,5 +20,5 @@ int regprog_in_use(regprog_T *prog); int vim_regexec_prog(regprog_T **prog, int ignore_case, char_u *line, colnr_T col); int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col); int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col); -long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col, proftime_T *tm, int *timed_out); +long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col, int *timed_out); /* vim: set ft=c : */ diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -5990,7 +5990,7 @@ vgr_match_buflines( { // Regular expression match while (vim_regexec_multi(regmatch, curwin, buf, lnum, - col, NULL, NULL) > 0) + col, NULL) > 0) { // Pass the buffer number so that it gets used even for a // dummy buffer, unless duplicate_name is set, then the diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -20,6 +20,11 @@ # define BT_REGEXP_DEBUG_LOG_NAME "bt_regexp_debug.log" #endif +#ifdef FEAT_RELTIME +static int dummy_timeout_flag = 0; +static const int *timeout_flag = &dummy_timeout_flag; +#endif + /* * Magic characters have a special meaning, they don't match literally. * Magic characters are negative. This separates them from literal characters @@ -45,6 +50,20 @@ toggle_Magic(int x) return Magic(x); } +#ifdef FEAT_RELTIME + void +init_regexp_timeout(long msec) +{ + timeout_flag = start_timeout(msec); +} + + void +disable_regexp_timeout(void) +{ + stop_timeout(); +} +#endif + /* * The first byte of the BT regexp internal "program" is actually this magic * number; the start node begins in the second byte. It's used to catch the @@ -1944,8 +1963,9 @@ vim_regsub_both( #ifdef FEAT_EVAL // To make sure that the length doesn't change between checking the // length and copying the string, and to speed up things, the - // resulting string is saved from the call with "flags & REGSUB_COPY" - // == 0 to the // call with "flags & REGSUB_COPY" != 0. + // resulting string is saved from the call with + // "flags & REGSUB_COPY" == 0 to the call with + // "flags & REGSUB_COPY" != 0. if (copy) { if (eval_result != NULL) @@ -1960,7 +1980,7 @@ vim_regsub_both( int prev_can_f_submatch = can_f_submatch; regsubmatch_T rsm_save; - vim_free(eval_result); + VIM_CLEAR(eval_result); // The expression may contain substitute(), which calls us // recursively. Make sure submatch() gets the text from the first @@ -2905,7 +2925,6 @@ vim_regexec_multi( buf_T *buf, // buffer in which to search linenr_T lnum, // nr of line to start looking for match colnr_T col, // column to start looking for match - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag is set when timeout limit reached { int result; @@ -2926,7 +2945,7 @@ vim_regexec_multi( rex_in_use = TRUE; result = rmp->regprog->engine->regexec_multi( - rmp, win, buf, lnum, col, tm, timed_out); + rmp, win, buf, lnum, col, timed_out); rmp->regprog->re_in_use = FALSE; // NFA engine aborted because it's very slow. @@ -2966,7 +2985,7 @@ vim_regexec_multi( rmp->regprog->re_in_use = TRUE; result = rmp->regprog->engine->regexec_multi( - rmp, win, buf, lnum, col, tm, timed_out); + rmp, win, buf, lnum, col, timed_out); rmp->regprog->re_in_use = FALSE; } vim_free(pat); diff --git a/src/regexp.h b/src/regexp.h --- a/src/regexp.h +++ b/src/regexp.h @@ -173,7 +173,7 @@ struct regengine // bt_regexec_nl or nfa_regexec_nl int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, int); // bt_regexec_mult or nfa_regexec_mult - long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, proftime_T *, int *); + long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, int *); //char_u *expr; }; diff --git a/src/regexp_bt.c b/src/regexp_bt.c --- a/src/regexp_bt.c +++ b/src/regexp_bt.c @@ -3228,7 +3228,6 @@ restore_subexpr(regbehind_T *bp) static int regmatch( char_u *scan, // Current node. - proftime_T *tm UNUSED, // timeout limit or NULL int *timed_out UNUSED) // flag set on timeout or NULL { char_u *next; // Next node. @@ -3237,9 +3236,6 @@ regmatch( regitem_T *rp; int no; int status; // one of the RA_ values: -#ifdef FEAT_RELTIME - int tm_count = 0; -#endif // Make "regstack" and "backpos" empty. They are allocated and freed in // bt_regexec_both() to reduce malloc()/free() calls. @@ -3271,19 +3267,12 @@ regmatch( break; } #ifdef FEAT_RELTIME - // Check for timeout once in 250 times to avoid excessive overhead from - // reading the clock. The value has been picked to check about once - // per msec on a modern CPU. - if (tm != NULL && ++tm_count == 250) + if (*timeout_flag) { - tm_count = 0; - if (profile_passed_limit(tm)) - { - if (timed_out != NULL) - *timed_out = TRUE; - status = RA_FAIL; - break; - } + if (timed_out != NULL) + *timed_out = TRUE; + status = RA_FAIL; + break; } #endif status = RA_CONT; @@ -3315,7 +3304,7 @@ regmatch( op = OP(scan); // Check for character class with NL added. if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI - && *rex.input == NUL && rex.lnum <= rex.reg_maxline) + && *rex.input == NUL && rex.lnum <= rex.reg_maxline) { reg_nextline(); } @@ -4732,7 +4721,6 @@ regmatch( regtry( bt_regprog_T *prog, colnr_T col, - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag set on timeout or NULL { rex.input = rex.line + col; @@ -4742,7 +4730,7 @@ regtry( rex.need_clear_zsubexpr = (prog->reghasz == REX_SET); #endif - if (regmatch(prog->program + 1, tm, timed_out) == 0) + if (regmatch(prog->program + 1, timed_out) == 0) return 0; cleanup_subexpr(); @@ -4817,7 +4805,6 @@ regtry( bt_regexec_both( char_u *line, colnr_T col, // column to start looking for match - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag set on timeout or NULL { bt_regprog_T *prog; @@ -4940,15 +4927,12 @@ bt_regexec_both( && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) || (c < 255 && prog->regstart < 255 && MB_TOLOWER(prog->regstart) == MB_TOLOWER(c))))) - retval = regtry(prog, col, tm, timed_out); + retval = regtry(prog, col, timed_out); else retval = 0; } else { -#ifdef FEAT_RELTIME - int tm_count = 0; -#endif // Messy cases: unanchored match. while (!got_int) { @@ -4975,7 +4959,7 @@ bt_regexec_both( break; } - retval = regtry(prog, col, tm, timed_out); + retval = regtry(prog, col, timed_out); if (retval > 0) break; @@ -4992,18 +4976,11 @@ bt_regexec_both( else ++col; #ifdef FEAT_RELTIME - // Check for timeout once in 500 times to avoid excessive overhead - // from reading the clock. The value has been picked to check - // about once per msec on a modern CPU. - if (tm != NULL && ++tm_count == 500) + if (*timeout_flag) { - tm_count = 0; - if (profile_passed_limit(tm)) - { - if (timed_out != NULL) - *timed_out = TRUE; - break; - } + if (timed_out != NULL) + *timed_out = TRUE; + break; } #endif } @@ -5067,7 +5044,7 @@ bt_regexec_nl( rex.reg_icombine = FALSE; rex.reg_maxcol = 0; - return bt_regexec_both(line, col, NULL, NULL); + return bt_regexec_both(line, col, NULL); } /* @@ -5085,11 +5062,10 @@ bt_regexec_multi( buf_T *buf, // buffer in which to search linenr_T lnum, // nr of line to start looking for match colnr_T col, // column to start looking for match - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag set on timeout or NULL { init_regexec_multi(rmp, win, buf, lnum); - return bt_regexec_both(NULL, col, tm, timed_out); + return bt_regexec_both(NULL, col, timed_out); } /* diff --git a/src/regexp_nfa.c b/src/regexp_nfa.c --- a/src/regexp_nfa.c +++ b/src/regexp_nfa.c @@ -4051,7 +4051,6 @@ pim_info(nfa_pim_T *pim) // Used during execution: whether a match has been found. static int nfa_match; #ifdef FEAT_RELTIME -static proftime_T *nfa_time_limit; static int *nfa_timed_out; #endif @@ -5650,29 +5649,13 @@ find_match_text(colnr_T startcol, int re * To reduce overhead, only check one in "count" times. */ static int -nfa_did_time_out(int count) +nfa_did_time_out(void) { - static int tm_count = 0; - - // Check for timeout once in "count" times to avoid excessive overhead from - // reading the clock. - if (nfa_time_limit != NULL) + if (*timeout_flag) { - if (tm_count >= count) - { - if (profile_passed_limit(nfa_time_limit)) - { - if (nfa_timed_out != NULL) - *nfa_timed_out = TRUE; - tm_count = 99999; - return TRUE; - } - // Only reset the count when not timed out, so that when it did - // timeout it keeps timing out until the time limit is changed. - tm_count = 0; - } - else - ++tm_count; + if (nfa_timed_out != NULL) + *nfa_timed_out = TRUE; + return TRUE; } return FALSE; } @@ -5726,7 +5709,7 @@ nfa_regmatch( return FALSE; #ifdef FEAT_RELTIME // Check relatively often here, since this is the toplevel matching. - if (nfa_did_time_out(100)) + if (nfa_did_time_out()) return FALSE; #endif @@ -5880,8 +5863,7 @@ nfa_regmatch( if (got_int) break; #ifdef FEAT_RELTIME - // do not check very often here, since this is a loop in a loop - if (nfa_did_time_out(2000)) + if (nfa_did_time_out()) break; #endif t = &thislist->t[listidx]; @@ -7127,8 +7109,8 @@ nextchar: if (got_int) break; #ifdef FEAT_RELTIME - // check regularly but not too often here - if (nfa_did_time_out(800)) + // Check for timeout once in a twenty times to avoid overhead. + if (nfa_did_time_out()) break; #endif } @@ -7160,7 +7142,6 @@ theend: nfa_regtry( nfa_regprog_T *prog, colnr_T col, - proftime_T *tm UNUSED, // timeout limit or NULL int *timed_out UNUSED) // flag set on timeout or NULL { int i; @@ -7173,7 +7154,6 @@ nfa_regtry( rex.input = rex.line + col; #ifdef FEAT_RELTIME - nfa_time_limit = tm; nfa_timed_out = timed_out; #endif @@ -7301,7 +7281,6 @@ nfa_regtry( nfa_regexec_both( char_u *line, colnr_T startcol, // column to start looking for match - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag set on timeout or NULL { nfa_regprog_T *prog; @@ -7397,7 +7376,7 @@ nfa_regexec_both( prog->state[i].lastlist[1] = 0; } - retval = nfa_regtry(prog, col, tm, timed_out); + retval = nfa_regtry(prog, col, timed_out); #ifdef DEBUG nfa_regengine.expr = NULL; @@ -7577,7 +7556,7 @@ nfa_regexec_nl( rex.reg_ic = rmp->rm_ic; rex.reg_icombine = FALSE; rex.reg_maxcol = 0; - return nfa_regexec_both(line, col, NULL, NULL); + return nfa_regexec_both(line, col, NULL); } @@ -7613,11 +7592,10 @@ nfa_regexec_multi( buf_T *buf, // buffer in which to search linenr_T lnum, // nr of line to start looking for match colnr_T col, // column to start looking for match - proftime_T *tm, // timeout limit or NULL int *timed_out) // flag set on timeout or NULL { init_regexec_multi(rmp, win, buf, lnum); - return nfa_regexec_both(NULL, col, tm, timed_out); + return nfa_regexec_both(NULL, col, timed_out); } #ifdef DEBUG diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -1760,10 +1760,6 @@ start_search_hl(void) end_search_hl(); // just in case it wasn't called before last_pat_prog(&screen_search_hl.rm); screen_search_hl.attr = HL_ATTR(HLF_L); -# ifdef FEAT_RELTIME - // Set the time limit to 'redrawtime'. - profile_setlimit(p_rdt, &screen_search_hl.tm); -# endif } } @@ -5029,4 +5025,3 @@ set_chars_option(win_T *wp, char_u **var return NULL; // no error } - diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -658,19 +658,8 @@ searchit( int break_loop = FALSE; #endif linenr_T stop_lnum = 0; // stop after this line number when != 0 -#ifdef FEAT_RELTIME - proftime_T *tm = NULL; // timeout limit or NULL - int *timed_out = NULL; // set when timed out or NULL -#endif - - if (extra_arg != NULL) - { - stop_lnum = extra_arg->sa_stop_lnum; -#ifdef FEAT_RELTIME - tm = extra_arg->sa_tm; - timed_out = &extra_arg->sa_timed_out; -#endif - } + int unused_timeout_flag = FALSE; + int *timed_out = &unused_timeout_flag; // set when timed out. if (search_regcomp(pat, RE_SEARCH, pat_use, (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) @@ -680,6 +669,18 @@ searchit( return FAIL; } + if (extra_arg != NULL) + { + stop_lnum = extra_arg->sa_stop_lnum; +#ifdef FEAT_RELTIME + if (extra_arg->sa_tm > 0) + { + init_regexp_timeout(extra_arg->sa_tm); + timed_out = &extra_arg->sa_timed_out; + } +#endif + } + /* * find the string */ @@ -753,11 +754,9 @@ searchit( if (stop_lnum != 0 && (dir == FORWARD ? lnum > stop_lnum : lnum < stop_lnum)) break; -#ifdef FEAT_RELTIME - // Stop after passing the "tm" time limit. - if (tm != NULL && profile_passed_limit(tm)) + // Stop after passing the time limit. + if (*timed_out) break; -#endif /* * Look for a match somewhere in line "lnum". @@ -765,22 +764,12 @@ searchit( col = at_first_line && (options & SEARCH_COL) ? pos->col : (colnr_T)0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, col, -#ifdef FEAT_RELTIME - tm, timed_out -#else - NULL, NULL -#endif - ); + lnum, col, timed_out); // vim_regexec_multi() may clear "regprog" if (regmatch.regprog == NULL) break; // Abort searching on an error (e.g., out of stack). - if (called_emsg > called_emsg_before -#ifdef FEAT_RELTIME - || (timed_out != NULL && *timed_out) -#endif - ) + if (called_emsg > called_emsg_before || *timed_out) break; if (nmatched > 0) { @@ -863,13 +852,7 @@ searchit( if (ptr[matchcol] == NUL || (nmatched = vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, - matchcol, -#ifdef FEAT_RELTIME - tm, timed_out -#else - NULL, NULL -#endif - )) == 0) + matchcol, timed_out)) == 0) { match_ok = FALSE; break; @@ -974,21 +957,13 @@ searchit( if (ptr[matchcol] == NUL || (nmatched = vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, - matchcol, -#ifdef FEAT_RELTIME - tm, timed_out -#else - NULL, NULL -#endif - )) == 0) + matchcol, timed_out)) == 0) { -#ifdef FEAT_RELTIME // If the search timed out, we did find a match // but it might be the wrong one, so that's not // OK. - if (timed_out != NULL && *timed_out) + if (*timed_out) match_ok = FALSE; -#endif break; } // vim_regexec_multi() may clear "regprog" @@ -1097,10 +1072,7 @@ searchit( * twice. */ if (!p_ws || stop_lnum != 0 || got_int - || called_emsg > called_emsg_before -#ifdef FEAT_RELTIME - || (timed_out != NULL && *timed_out) -#endif + || called_emsg > called_emsg_before || *timed_out #ifdef FEAT_SEARCH_EXTRA || break_loop #endif @@ -1124,10 +1096,7 @@ searchit( if (extra_arg != NULL) extra_arg->sa_wrapped = TRUE; } - if (got_int || called_emsg > called_emsg_before -#ifdef FEAT_RELTIME - || (timed_out != NULL && *timed_out) -#endif + if (got_int || called_emsg > called_emsg_before || *timed_out #ifdef FEAT_SEARCH_EXTRA || break_loop #endif @@ -1136,6 +1105,9 @@ searchit( } while (--count > 0 && found); // stop after count matches or no match +# ifdef FEAT_RELTIME + disable_regexp_timeout(); +# endif vim_regfree(regmatch.regprog); if (!found) // did not find it @@ -2915,7 +2887,7 @@ is_zero_width(char_u *pattern, int move, { regmatch.startpos[0].col++; nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, NULL, NULL); + pos.lnum, regmatch.startpos[0].col, NULL); if (nmatched != 0) break; } while (regmatch.regprog != NULL diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3329,9 +3329,6 @@ typedef struct // matchaddpos(). TRUE/FALSE char has_cursor; // TRUE if the cursor is inside the match, used for // CurSearch -#ifdef FEAT_RELTIME - proftime_T tm; // for a time limit -#endif } match_T; // number of positions supported by matchaddpos() @@ -4419,7 +4416,7 @@ typedef struct { linenr_T sa_stop_lnum; // stop after this line number when != 0 #ifdef FEAT_RELTIME - proftime_T *sa_tm; // timeout limit or NULL + long sa_tm; // timeout limit or zero int sa_timed_out; // set when timed out #endif int sa_wrapped; // search wrapped around diff --git a/src/syntax.c b/src/syntax.c --- a/src/syntax.c +++ b/src/syntax.c @@ -266,9 +266,6 @@ static reg_extmatch_T *next_match_extmat static win_T *syn_win; // current window for highlighting static buf_T *syn_buf; // current buffer for highlighting static synblock_T *syn_block; // current buffer for highlighting -#ifdef FEAT_RELTIME -static proftime_T *syn_tm; // timeout limit -#endif static linenr_T current_lnum = 0; // lnum of current state static colnr_T current_col = 0; // column of current state static int current_state_stored = 0; // TRUE if stored current state @@ -350,18 +347,6 @@ static char_u *get_syn_pattern(char_u *a static int get_id_list(char_u **arg, int keylen, short **list, int skip); static void syn_combine_list(short **clstr1, short **clstr2, int list_op); -#if defined(FEAT_RELTIME) || defined(PROTO) -/* - * Set the timeout used for syntax highlighting. - * Use NULL to reset, no timeout. - */ - void -syn_set_timeout(proftime_T *tm) -{ - syn_tm = tm; -} -#endif - /* * Start the syntax recognition for a line. This function is normally called * from the screen updating, once for each displayed line. @@ -3166,9 +3151,7 @@ syn_regexec( syn_time_T *st UNUSED) { int r; -#ifdef FEAT_RELTIME int timed_out = FALSE; -#endif #ifdef FEAT_PROFILE proftime_T pt; @@ -3183,13 +3166,7 @@ syn_regexec( return FALSE; rmp->rmm_maxcol = syn_buf->b_p_smc; - r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, -#ifdef FEAT_RELTIME - syn_tm, &timed_out -#else - NULL, NULL -#endif - ); + r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, &timed_out); #ifdef FEAT_PROFILE if (syn_time_on) diff --git a/src/testdir/test_hlsearch.vim b/src/testdir/test_hlsearch.vim --- a/src/testdir/test_hlsearch.vim +++ b/src/testdir/test_hlsearch.vim @@ -37,6 +37,15 @@ endfunc func Test_hlsearch_hangs() CheckFunction reltimefloat + " So, it turns out the Windows 7 implements TimerQueue timers differently + " and they can expire *before* the requested time has elapsed. So allow for + " the timeout occurring after 80 ms (5 * 16 (the typical clock tick)). + if has("win32") + let min_timeout = 0.08 + else + let min_timeout = 0.1 + endif + " This pattern takes a long time to match, it should timeout. new call setline(1, ['aaa', repeat('abc ', 1000), 'ccc']) @@ -45,7 +54,7 @@ func Test_hlsearch_hangs() let @/ = '\%#=1a*.*X\@<=b*' redraw let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed > 0.1) + call assert_true(elapsed > min_timeout) call assert_true(elapsed < 1.0) set nohlsearch redrawtime& bwipe! diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim --- a/src/testdir/test_search.vim +++ b/src/testdir/test_search.vim @@ -1550,6 +1550,32 @@ func Test_search_errors() bwipe! endfunc +func Test_search_timeout() + new + let pattern = '\%#=1a*.*X\@<=b*' + let search_timeout = 0.02 + let slow_target_timeout = search_timeout * 15.0 + + for n in range(40, 400, 30) + call setline(1, ['aaa', repeat('abc ', n), 'ccc']) + let start = reltime() + call search(pattern, '', 0) + let elapsed = reltimefloat(reltime(start)) + if elapsed > slow_target_timeout + break + endif + endfor + call assert_true(elapsed > slow_target_timeout) + + let max_time = elapsed / 2.0 + let start = reltime() + call search(pattern, '', 0, float2nr(search_timeout * 1000)) + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed < max_time) + + bwipe! +endfunc + func Test_search_display_pattern() new call setline(1, ['foo', 'bar', 'foobar']) diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim --- a/src/testdir/test_syntax.vim +++ b/src/testdir/test_syntax.vim @@ -527,6 +527,15 @@ func Test_syntax_hangs() CheckFunction reltimefloat CheckFeature syntax + " So, it turns out the Windows 7 implements TimerQueue timers differently + " and they can expire *before* the requested time has elapsed. So allow for + " the timeout occurring after 80 ms (5 * 16 (the typical clock tick)). + if has("win32") + let min_timeout = 0.08 + else + let min_timeout = 0.1 + endif + " This pattern takes a long time to match, it should timeout. new call setline(1, ['aaa', repeat('abc ', 1000), 'ccc']) @@ -535,7 +544,7 @@ func Test_syntax_hangs() syn match Error /\%#=1a*.*X\@<=b*/ redraw let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed > 0.1) + call assert_true(elapsed > min_timeout) call assert_true(elapsed < 1.0) " second time syntax HL is disabled @@ -549,7 +558,7 @@ func Test_syntax_hangs() exe "normal \" redraw let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed > 0.1) + call assert_true(elapsed > min_timeout) call assert_true(elapsed < 1.0) set redrawtime& @@ -642,7 +651,7 @@ func Test_syntax_c() \ "\tNote: asdf", \ '}', \ ], 'Xtest.c') - + " This makes the default for 'background' use "dark", check that the " response to t_RB corrects it to "light". let $COLORFGBG = '15;0' diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -735,6 +735,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 5057, +/**/ 5056, /**/ 5055,