# HG changeset patch # User Bram Moolenaar # Date 1634243404 -7200 # Node ID d7e1cf30728cc8a3111b2cc1f1e7aa6248392f7b # Parent 7eded0585ee007f492f24c3ab1fd58bfa7ce07ce patch 8.2.3510: changes are only detected with one second accuracy Commit: https://github.com/vim/vim/commit/0a7984af5601323fae7b3398f05a48087db7b767 Author: Leah Neukirchen Date: Thu Oct 14 21:27:55 2021 +0100 patch 8.2.3510: changes are only detected with one second accuracy Problem: Changes are only detected with one second accuracy. Solution: Use the nanosecond time if possible. (Leah Neukirchen, closes #8873, closes #8875) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -12236,6 +12236,7 @@ multi_byte_encoding 'encoding' is set to multi_byte_ime Compiled with support for IME input method. multi_lang Compiled with support for multiple languages. mzscheme Compiled with MzScheme interface |mzscheme|. +nanotime Compiled with sub-second time stamp checks. netbeans_enabled Compiled with support for |netbeans| and connected. netbeans_intg Compiled with support for |netbeans|. num64 Compiled with 64-bit |Number| support. diff --git a/src/auto/configure b/src/auto/configure --- a/src/auto/configure +++ b/src/auto/configure @@ -13149,6 +13149,52 @@ if test "x$vim_cv_stat_ignores_slash" = fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanoseconds field of struct stat" >&5 +$as_echo_n "checking for nanoseconds field of struct stat... " >&6; } +if ${ac_cv_struct_st_mtim_nsec+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CPPFLAGS="$CPPFLAGS" + ac_cv_struct_st_mtim_nsec=no + # st_mtim.tv_nsec -- the usual case + # st_mtim._tv_nsec -- Solaris 2.6, if + # (defined _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED == 1 + # && !defined __EXTENSIONS__) + # st_mtim.st__tim.tv_nsec -- UnixWare 2.1.2 + # st_mtime_n -- AIX 5.2 and above + # st_mtimespec.tv_nsec -- Darwin (Mac OSX) + for ac_val in st_mtim.tv_nsec st_mtim._tv_nsec st_mtim.st__tim.tv_nsec st_mtime_n st_mtimespec.tv_nsec; do + CPPFLAGS="$ac_save_CPPFLAGS -DST_MTIM_NSEC=$ac_val" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +struct stat s; s.ST_MTIM_NSEC; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_struct_st_mtim_nsec=$ac_val; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$ac_save_CPPFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_st_mtim_nsec" >&5 +$as_echo "$ac_cv_struct_st_mtim_nsec" >&6; } +if test $ac_cv_struct_st_mtim_nsec != no; then + +cat >>confdefs.h <<_ACEOF +#define ST_MTIM_NSEC $ac_cv_struct_st_mtim_nsec +_ACEOF + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv_open()" >&5 $as_echo_n "checking for iconv_open()... " >&6; } save_LIBS="$LIBS" diff --git a/src/bufwrite.c b/src/bufwrite.c --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -527,7 +527,7 @@ buf_write_bytes(struct bw_info *ip) check_mtime(buf_T *buf, stat_T *st) { if (buf->b_mtime_read != 0 - && time_differs((long)st->st_mtime, buf->b_mtime_read)) + && time_differs(st, buf->b_mtime_read, buf->b_mtime_read_ns)) { msg_scroll = TRUE; // don't overwrite messages here msg_silent = 0; // must give this prompt @@ -2558,6 +2558,7 @@ nofail: { buf_store_time(buf, &st_old, fname); buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; } } } diff --git a/src/config.h.in b/src/config.h.in --- a/src/config.h.in +++ b/src/config.h.in @@ -144,6 +144,9 @@ /* Define if stat() ignores a trailing slash */ #undef STAT_IGNORES_SLASH +/* Define to nanoseconds field of struct stat */ +#undef ST_MTIM_NSEC + /* Define if tgetstr() has a second argument that is (char *) */ #undef TGETSTR_CHAR_P diff --git a/src/configure.ac b/src/configure.ac --- a/src/configure.ac +++ b/src/configure.ac @@ -3843,6 +3843,31 @@ main() {struct stat st; exit(stat("conf if test "x$vim_cv_stat_ignores_slash" = "xyes" ; then AC_DEFINE(STAT_IGNORES_SLASH) fi + +dnl nanoseconds field of struct stat +AC_CACHE_CHECK([for nanoseconds field of struct stat], + ac_cv_struct_st_mtim_nsec, + [ac_save_CPPFLAGS="$CPPFLAGS" + ac_cv_struct_st_mtim_nsec=no + # st_mtim.tv_nsec -- the usual case + # st_mtim._tv_nsec -- Solaris 2.6, if + # (defined _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED == 1 + # && !defined __EXTENSIONS__) + # st_mtim.st__tim.tv_nsec -- UnixWare 2.1.2 + # st_mtime_n -- AIX 5.2 and above + # st_mtimespec.tv_nsec -- Darwin (Mac OSX) + for ac_val in st_mtim.tv_nsec st_mtim._tv_nsec st_mtim.st__tim.tv_nsec st_mtime_n st_mtimespec.tv_nsec; do + CPPFLAGS="$ac_save_CPPFLAGS -DST_MTIM_NSEC=$ac_val" + AC_TRY_COMPILE([#include +#include ], [struct stat s; s.ST_MTIM_NSEC;], + [ac_cv_struct_st_mtim_nsec=$ac_val; break]) + done + CPPFLAGS="$ac_save_CPPFLAGS" +]) +if test $ac_cv_struct_st_mtim_nsec != no; then + AC_DEFINE_UNQUOTED([ST_MTIM_NSEC], [$ac_cv_struct_st_mtim_nsec], + [Define if struct stat contains a nanoseconds field]) +fi dnl Link with iconv for charset translation, if not found without library. dnl check for iconv() requires including iconv.h diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5479,6 +5479,13 @@ f_has(typval_T *argvars, typval_T *rettv 0 #endif }, + {"nanotime", +#ifdef ST_MTIM_NSEC + 1 +#else + 0 +#endif + }, {"num64", 1}, {"ole", #ifdef FEAT_OLE diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -408,6 +408,7 @@ readfile( { buf_store_time(curbuf, &st, fname); curbuf->b_mtime_read = curbuf->b_mtime; + curbuf->b_mtime_read_ns = curbuf->b_mtime_ns; filesize_disk = st.st_size; #ifdef UNIX /* @@ -432,7 +433,9 @@ readfile( else { curbuf->b_mtime = 0; + curbuf->b_mtime_ns = 0; curbuf->b_mtime_read = 0; + curbuf->b_mtime_read_ns = 0; curbuf->b_orig_size = 0; curbuf->b_orig_mode = 0; } @@ -2569,6 +2572,7 @@ failed: { buf_store_time(curbuf, &st, fname); curbuf->b_mtime_read = curbuf->b_mtime; + curbuf->b_mtime_read_ns = curbuf->b_mtime_ns; } #endif } @@ -3115,15 +3119,19 @@ msg_add_eol(void) } int -time_differs(long t1, long t2) +time_differs(stat_T *st, long mtime, long mtime_ns UNUSED) { #if defined(__linux__) || defined(MSWIN) // On a FAT filesystem, esp. under Linux, there are only 5 bits to store // the seconds. Since the roundoff is done when flushing the inode, the // time may change unexpectedly by one second!!! - return (t1 - t2 > 1 || t2 - t1 > 1); + return (long)st->st_mtime - mtime > 1 || mtime - (long)st->st_mtime > 1 +# ifdef ST_MTIM_NSEC + || (long)st->ST_MTIM_NSEC != mtime_ns +# endif + ; #else - return (t1 != t2); + return (long)st->st_mtime != mtime; #endif } @@ -4072,7 +4080,7 @@ buf_check_timestamp( if ( !(buf->b_flags & BF_NOTEDITED) && buf->b_mtime != 0 && ((stat_res = mch_stat((char *)buf->b_ffname, &st)) < 0 - || time_differs((long)st.st_mtime, buf->b_mtime) + || time_differs(&st, buf->b_mtime, buf->b_mtime_ns) || st.st_size != buf->b_orig_size #ifdef HAVE_ST_MODE || (int)st.st_mode != buf->b_orig_mode @@ -4187,9 +4195,12 @@ buf_check_timestamp( mesg2 = _("See \":help W16\" for more info."); } else + { // Only timestamp changed, store it to avoid a warning // in check_mtime() later. buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; + } } } } @@ -4468,6 +4479,11 @@ buf_reload(buf_T *buf, int orig_mode) buf_store_time(buf_T *buf, stat_T *st, char_u *fname UNUSED) { buf->b_mtime = (long)st->st_mtime; +#ifdef ST_MTIM_NSEC + buf->b_mtime_ns = (long)st->ST_MTIM_NSEC; +#else + buf->b_mtime_ns = 0; +#endif buf->b_orig_size = st->st_size; #ifdef HAVE_ST_MODE buf->b_orig_mode = (int)st->st_mode; diff --git a/src/memline.c b/src/memline.c --- a/src/memline.c +++ b/src/memline.c @@ -1032,6 +1032,7 @@ set_b0_fname(ZERO_BL *b0p, buf_T *buf) #endif buf_store_time(buf, &st, buf->b_ffname); buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; } else { @@ -1040,7 +1041,9 @@ set_b0_fname(ZERO_BL *b0p, buf_T *buf) long_to_char(0L, b0p->b0_ino); #endif buf->b_mtime = 0; + buf->b_mtime_ns = 0; buf->b_mtime_read = 0; + buf->b_mtime_read_ns = 0; buf->b_orig_size = 0; buf->b_orig_mode = 0; } @@ -2436,6 +2439,9 @@ ml_sync_all(int check_file, int check_ch */ if (mch_stat((char *)buf->b_ffname, &st) == -1 || st.st_mtime != buf->b_mtime_read +#ifdef ST_MTIM_NSEC + || st.ST_MTIM_NSEC != buf->b_mtime_read_ns +#endif || st.st_size != buf->b_orig_size) { ml_preserve(buf, FALSE); diff --git a/src/netbeans.c b/src/netbeans.c --- a/src/netbeans.c +++ b/src/netbeans.c @@ -1760,7 +1760,10 @@ nb_do_cmd( if (buf == NULL || buf->bufp == NULL) nbdebug((" invalid buffer identifier in setModtime\n")); else + { buf->bufp->b_mtime = atoi((char *)args); + buf->bufp->b_mtime_ns = 0; + } // ===================================================================== } else if (streq((char *)cmd, "setReadOnly")) diff --git a/src/proto/fileio.pro b/src/proto/fileio.pro --- a/src/proto/fileio.pro +++ b/src/proto/fileio.pro @@ -12,7 +12,7 @@ void msg_add_fname(buf_T *buf, char_u *f int msg_add_fileformat(int eol_type); void msg_add_lines(int insert_space, long lnum, off_T nchars); void msg_add_eol(void); -int time_differs(long t1, long t2); +int time_differs(stat_T *st, long mtime, long mtime_ns); int need_conversion(char_u *fenc); int get_fio_flags(char_u *ptr); int get_win_fio_flags(char_u *ptr); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -2724,7 +2724,9 @@ struct file_buffer wininfo_T *b_wininfo; // list of last used info for each window long b_mtime; // last change time of original file + long b_mtime_ns; // nanoseconds of last change time long b_mtime_read; // last change time when reading + long b_mtime_read_ns; // nanoseconds of last read time off_T b_orig_size; // size of original file in bytes int b_orig_mode; // mode of original file #ifdef FEAT_VIMINFO diff --git a/src/testdir/test_stat.vim b/src/testdir/test_stat.vim --- a/src/testdir/test_stat.vim +++ b/src/testdir/test_stat.vim @@ -76,6 +76,39 @@ func Test_checktime() call delete(fname) endfunc +func Test_checktime_fast() + CheckFeature nanotime + + let fname = 'Xtest.tmp' + + let fl = ['Hello World!'] + call writefile(fl, fname) + set autoread + exec 'e' fname + let fl = readfile(fname) + let fl[0] .= ' - checktime' + call writefile(fl, fname) + checktime + call assert_equal(fl[0], getline(1)) + + call delete(fname) +endfunc + +func Test_autoread_fast() + CheckFeature nanotime + + new Xautoread + set autoread + call setline(1, 'foo') + + w! + silent !echo bar > Xautoread + checktime + + call assert_equal('bar', trim(getline(1))) + call delete('Xautoread') +endfunc + func Test_autoread_file_deleted() new Xautoread set autoread diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3510, +/**/ 3509, /**/ 3508,