0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.2.5057: using gettimeofday() for timeout is very inefficient

Problem:    Using gettimeofday() for timeout is very inefficient.
Solution:   Set a platform dependent timer. (Paul Ollis, closes #10505)
This commit is contained in:
Paul Ollis 2022-06-05 16:55:54 +01:00 committed by Bram Moolenaar
parent 1d97db3d98
commit 6574577cac
29 changed files with 811 additions and 242 deletions

94
src/auto/configure vendored
View File

@ -762,7 +762,6 @@ infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
@ -898,7 +897,6 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
@ -1151,15 +1149,6 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1297,7 +1286,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir runstatedir
libdir localedir mandir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@ -1450,7 +1439,6 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -12737,7 +12725,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@ -12783,7 +12771,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@ -12807,7 +12795,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@ -12852,7 +12840,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@ -12876,7 +12864,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@ -13080,6 +13068,76 @@ $as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for timer_create" >&5
$as_echo_n "checking for timer_create... " >&6; }
save_LIBS="$LIBS"
LIBS="$LIBS -lrt"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include<signal.h>
#include<time.h>
static void set_flag(union sigval) {}
int
main ()
{
struct timespec ts;
struct sigevent action = {0};
timer_t timer_id;
action.sigev_notify = SIGEV_THREAD;
action.sigev_notify_function = set_flag;
timer_create(CLOCK_REALTIME, &action, &timer_id);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; with -lrt" >&5
$as_echo "yes; with -lrt" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h
else
LIBS="$save_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include<signal.h>
#include<time.h>
static void set_flag(union sigval) {}
int
main ()
{
struct timespec ts;
struct sigevent action = {0};
timer_t timer_id;
action.sigev_notify = SIGEV_THREAD;
action.sigev_notify_function = set_flag;
timer_create(CLOCK_REALTIME, &action, &timer_id);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat() ignores a trailing slash" >&5
$as_echo_n "checking whether stat() ignores a trailing slash... " >&6; }
if ${vim_cv_stat_ignores_slash+:} false; then :

View File

@ -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

View File

@ -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([[

View File

@ -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.

View File

@ -3288,3 +3288,15 @@ EXTERN char e_bitshift_ops_must_be_postive[]
EXTERN char e_argument_1_list_item_nr_dictionary_required[]
INIT(= N_("E1284: Argument 1, list item %d: Dictionary required"));
#endif
#ifdef FEAT_RELTIME
EXTERN char e_could_not_clear_timeout_str[]
INIT(= N_("E1285: Could not clear timeout: %s"));
EXTERN char e_could_not_set_timeout_str[]
INIT(= N_("E1286: Could not set timeout: %s"));
EXTERN char e_could_not_set_handler_for_timeout_str[]
INIT(= N_("E1287: Could not set handler for timeout: %s"));
EXTERN char e_could_not_reset_handler_for_timeout_str[]
INIT(= N_("E1288: Could not reset handler for timeout: %s"));
EXTERN char e_could_not_check_for_pending_sigalrm_str[]
INIT(= N_("E1289: Could not check for pending SIGALRM: %s"));
#endif

View File

@ -8439,7 +8439,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
int retval = 0; // default: FAIL
long lnum_stop = 0;
#ifdef FEAT_RELTIME
proftime_T tm;
long time_limit = 0;
#endif
int options = SEARCH_KEEP;
@ -8486,11 +8485,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
}
}
#ifdef FEAT_RELTIME
// Set the time limit, if there is one.
profile_setlimit(time_limit, &tm);
#endif
/*
* This function does not accept SP_REPEAT and SP_RETCOUNT flags.
* Check to make sure only those flags are set.
@ -8509,7 +8503,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
CLEAR_FIELD(sia);
sia.sa_stop_lnum = (linenr_T)lnum_stop;
#ifdef FEAT_RELTIME
sia.sa_tm = &tm;
sia.sa_tm = time_limit;
#endif
// Repeat until {skip} returns FALSE.
@ -8955,19 +8949,11 @@ do_searchpair(
int use_skip = FALSE;
int err;
int options = SEARCH_KEEP;
#ifdef FEAT_RELTIME
proftime_T tm;
#endif
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
#ifdef FEAT_RELTIME
// Set the time limit, if there is one.
profile_setlimit(time_limit, &tm);
#endif
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17);
@ -8998,7 +8984,7 @@ do_searchpair(
CLEAR_FIELD(sia);
sia.sa_stop_lnum = lnum_stop;
#ifdef FEAT_RELTIME
sia.sa_tm = &tm;
sia.sa_tm = time_limit;
#endif
n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
options, RE_SEARCH, &sia);

View File

@ -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))

View File

@ -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,

View File

@ -330,10 +330,6 @@ init_search_hl(win_T *wp, match_T *search_hl)
cur->hl.buf = wp->w_buffer;
cur->hl.lnum = 0;
cur->hl.first_lnum = 0;
# ifdef FEAT_RELTIME
// Set the time limit to 'redrawtime'.
profile_setlimit(p_rdt, &(cur->hl.tm));
# endif
cur = cur->next;
}
search_hl->buf = wp->w_buffer;
@ -424,6 +420,7 @@ next_search_hl(
colnr_T matchcol;
long nmatched;
int called_emsg_before = called_emsg;
int timed_out = FALSE;
// for :{range}s/pat only highlight inside the range
if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
@ -451,7 +448,7 @@ next_search_hl(
{
# ifdef FEAT_RELTIME
// Stop searching after passing the time limit.
if (profile_passed_limit(&(shl->tm)))
if (timed_out)
{
shl->lnum = 0; // no match found in time
break;
@ -494,16 +491,9 @@ next_search_hl(
int regprog_is_copy = (shl != search_hl && cur != NULL
&& shl == &cur->hl
&& cur->match.regprog == cur->hl.rm.regprog);
int timed_out = FALSE;
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
matchcol,
#ifdef FEAT_RELTIME
&(shl->tm), &timed_out
#else
NULL, NULL
#endif
);
matchcol, &timed_out);
// Copy the regprog, in case it got freed and recompiled.
if (regprog_is_copy)
cur->match.regprog = cur->hl.rm.regprog;

View File

@ -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

View File

@ -23,6 +23,13 @@
* X11 header files. */
#define NO_X11_INCLUDES
#include <stdbool.h>
#include <mach/boolean.h>
#include <sys/errno.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
#include "vim.h"
#import <AppKit/AppKit.h>
@ -208,6 +215,175 @@ releasepool:
#endif /* FEAT_CLIPBOARD */
#ifdef FEAT_RELTIME
/*
* The following timer code is based on a Gist by Jorgen Lundman:
*
* https://gist.github.com/lundman
*/
typedef struct macos_timer macos_timer_T;
static void
_timer_cancel(void *arg UNUSED)
{
// This is not currently used, but it might be useful in the future and
// it is non-trivial enough to provide as usable implementation.
# if 0
macos_timer_T *timerid = (macos_timer_T *)arg;
dispatch_release(timerid->tim_timer);
dispatch_release(timerid->tim_queue);
timerid->tim_timer = NULL;
timerid->tim_queue = NULL;
free(timerid);
# endif
}
static void
_timer_handler(void *arg)
{
macos_timer_T *timerid = (macos_timer_T *)arg;
union sigval sv;
sv.sival_ptr = timerid->tim_arg;
if (timerid->tim_func != NULL)
timerid->tim_func(sv);
}
static uint64_t
itime_to_ns(const struct timespec *it)
{
time_t sec = it->tv_sec;
long nsec = it->tv_nsec;
uint64_t ns = NSEC_PER_SEC * sec + nsec;
return ns == 0 ? DISPATCH_TIME_FOREVER : ns;
}
/*
* A partial emulation of the POSIX timer_create function.
*
* The limitations and differences include:
*
* - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid
* values.
* - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time
* source is used internally.
* - The only notification method supported is SIGEV_THREAD.
*/
inline int
timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)
{
macos_timer_T *timer = NULL;
// We only support real time and monotonic clocks; and SIGEV_THREAD
// notification. In practice, there is no difference between the two
// types of clocks on MacOS - we always use the mach_machine_time
// source.
if ( (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
|| sevp->sigev_notify != SIGEV_THREAD)
{
semsg("clockid: %d %d", clockid, CLOCK_REALTIME);
semsg("notify: %d %d", sevp->sigev_notify, SIGEV_THREAD);
errno = ENOTSUP;
return -1;
}
timer = (macos_timer_T *)malloc(sizeof(macos_timer_T));
if (timer == NULL)
{
errno = ENOMEM;
return -1;
}
*timerid = timer;
timer->tim_queue = dispatch_queue_create(
"org.vim.timerqueue", NULL);
if (timer->tim_queue == NULL)
{
errno = ENOMEM;
return -1;
}
timer->tim_timer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue);
if (timer->tim_timer == NULL)
{
errno = ENOMEM;
return -1;
}
timer->tim_func = sevp->sigev_notify_function;
timer->tim_arg = sevp->sigev_value.sival_ptr;
dispatch_set_context(timer->tim_timer, timer);
dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler);
dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel);
dispatch_resume(timer->tim_timer);
return 0;
}
/*
* A partial emulation of the POSIX timer_settime function.
*
* The limitations and differences include:
*
* - The flags argument is ignored. The supplied new_value is therfore
* always treated as a relative time.
* - The old_value argument is ignored.
*/
int
timer_settime(
timer_t timerid,
int unused_flags UNUSED,
const struct itimerspec *new_value,
struct itimerspec *old_value UNUSED)
{
uint64_t first_shot = itime_to_ns(&new_value->it_value);
if (timerid == NULL)
return 0;
if (first_shot == DISPATCH_TIME_FOREVER)
{
dispatch_source_set_timer(
timerid->tim_timer, first_shot, first_shot, 0);
}
else
{
uint64_t interval = itime_to_ns(&new_value->it_interval);
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot);
dispatch_source_set_timer(timerid->tim_timer, start, interval, 0);
}
return 0;
}
/*
* An emulation of the POSIX timer_delete function.
*
* Disabled because it is not currently used, but an implemented provided
* for completeness and possible future use.
*/
#if 0
int
timer_delete(timer_t timerid)
{
/* Calls _timer_cancel() */
if (timerid != NULL)
dispatch_source_cancel(timerid->tim_timer);
return 0;
}
#endif
#endif /* FEAT_RELTIME */
/* Lift the compiler warning suppression. */
#if defined(__clang__) && defined(__STRICT_ANSI__)
# pragma clang diagnostic pop

View File

@ -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

View File

@ -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

View File

@ -72,6 +72,7 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil
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_T *cbd);
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 : */

View File

@ -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 : */

View File

@ -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 : */

View File

@ -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

View File

@ -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);

View File

@ -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;
};

View File

@ -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,20 +3267,13 @@ 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)
{
tm_count = 0;
if (profile_passed_limit(tm))
if (*timeout_flag)
{
if (timed_out != NULL)
*timed_out = TRUE;
status = RA_FAIL;
break;
}
}
#endif
status = RA_CONT;
@ -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,19 +4976,12 @@ 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)
{
tm_count = 0;
if (profile_passed_limit(tm))
if (*timeout_flag)
{
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);
}
/*

View File

@ -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,30 +5649,14 @@ find_match_text(colnr_T startcol, int regstart, char_u *match_text)
* 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 (tm_count >= count)
{
if (profile_passed_limit(nfa_time_limit))
if (*timeout_flag)
{
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;
}
return FALSE;
}
#endif
@ -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

View File

@ -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 **varp)
return NULL; // no error
}

View File

@ -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, pos_T *cur, int direction)
{
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

View File

@ -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

View File

@ -266,9 +266,6 @@ static reg_extmatch_T *next_match_extmatch = NULL;
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 *arg, synpat_T *ci);
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)

View File

@ -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!

View File

@ -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'])

View File

@ -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&

View File

@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
5057,
/**/
5056,
/**/