changeset 29071:b90bca860b5a v8.2.5057

patch 8.2.5057: using gettimeofday() for timeout is very inefficient Commit: https://github.com/vim/vim/commit/6574577cacd393ab7591fc776ea060eebc939e55 Author: Paul Ollis <paul@cleversheep.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 05 Jun 2022 18:00:08 +0200
parents 8bf8001ef6d5
children 44b520fb2185
files src/auto/configure src/config.h.in src/configure.ac src/drawscreen.c src/errors.h src/evalfunc.c src/ex_cmds.c src/ex_getln.c src/match.c src/os_mac.h src/os_macosx.m src/os_unix.c src/os_win32.c src/proto/os_unix.pro src/proto/os_win32.pro src/proto/regexp.pro src/quickfix.c src/regexp.c src/regexp.h src/regexp_bt.c src/regexp_nfa.c src/screen.c src/search.c src/structs.h src/syntax.c src/testdir/test_hlsearch.vim src/testdir/test_search.vim src/testdir/test_syntax.vim src/version.c
diffstat 29 files changed, 811 insertions(+), 242 deletions(-) [+]
line wrap: on
line diff
--- 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<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 :
--- 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
--- 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([[
--- 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.
--- 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
--- 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);
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -4006,7 +4006,7 @@ ex_substitute(exarg_T *eap)
 		); ++lnum)
     {
 	nmatch = vim_regexec_multi(&regmatch, 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(&regmatch, 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(&regmatch, 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(&regmatch, 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(&regmatch, 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))
--- 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,
--- 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;
--- 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
--- 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
--- 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
--- 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
--- 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 : */
--- 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 : */
--- 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 : */
--- 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
--- 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);
--- 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;
 };
 
--- 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);
 }
 
 /*
--- 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
--- 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
 }
-
--- 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)), &regmatch) == 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(&regmatch, 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(&regmatch,
 					      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(&regmatch,
 					      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(&regmatch, 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
--- 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
--- 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)
--- 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!
--- 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'])
--- 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 \<C-L>"
   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'
--- 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,