vim-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 vim/vim#8873, closes vim/vim#8875)
0a7984af56

In Nvim Test_checktime_fast() is also flaky. Add a delay to avoid that.
This commit is contained in:
zeertzjq 2022-02-13 21:33:28 +08:00
parent 6f5fae08a3
commit 03348e5b9d
5 changed files with 58 additions and 12 deletions

View File

@ -587,7 +587,9 @@ struct file_buffer {
// where invoked // where invoked
long b_mtime; // last change time of original file 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; // last change time when reading
long b_mtime_read_ns; // nanoseconds of last read time
uint64_t b_orig_size; // size of original file in bytes uint64_t b_orig_size; // size of original file in bytes
int b_orig_mode; // mode of original file int b_orig_mode; // mode of original file
time_t b_last_used; // time when the buffer was last used; used time_t b_last_used; // time when the buffer was last used; used

View File

@ -4539,6 +4539,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"mouse", "mouse",
"multi_byte", "multi_byte",
"multi_lang", "multi_lang",
"nanotime",
"num64", "num64",
"packages", "packages",
"path_extra", "path_extra",

View File

@ -405,6 +405,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
if (os_fileinfo((char *)fname, &file_info)) { if (os_fileinfo((char *)fname, &file_info)) {
buf_store_file_info(curbuf, &file_info); buf_store_file_info(curbuf, &file_info);
curbuf->b_mtime_read = curbuf->b_mtime; curbuf->b_mtime_read = curbuf->b_mtime;
curbuf->b_mtime_read_ns = curbuf->b_mtime_ns;
#ifdef UNIX #ifdef UNIX
/* /*
* Use the protection bits of the original file for the swap file. * Use the protection bits of the original file for the swap file.
@ -421,7 +422,9 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
#endif #endif
} else { } else {
curbuf->b_mtime = 0; curbuf->b_mtime = 0;
curbuf->b_mtime_ns = 0;
curbuf->b_mtime_read = 0; curbuf->b_mtime_read = 0;
curbuf->b_mtime_read_ns = 0;
curbuf->b_orig_size = 0; curbuf->b_orig_size = 0;
curbuf->b_orig_mode = 0; curbuf->b_orig_mode = 0;
} }
@ -3695,11 +3698,12 @@ nofail:
msg_puts_attr(_("don't quit the editor until the file is successfully written!"), msg_puts_attr(_("don't quit the editor until the file is successfully written!"),
attr | MSG_HIST); attr | MSG_HIST);
/* Update the timestamp to avoid an "overwrite changed file" // Update the timestamp to avoid an "overwrite changed file"
* prompt when writing again. */ // prompt when writing again.
if (os_fileinfo((char *)fname, &file_info_old)) { if (os_fileinfo((char *)fname, &file_info_old)) {
buf_store_file_info(buf, &file_info_old); buf_store_file_info(buf, &file_info_old);
buf->b_mtime_read = buf->b_mtime; buf->b_mtime_read = buf->b_mtime;
buf->b_mtime_read_ns = buf->b_mtime_ns;
} }
} }
} }
@ -3893,8 +3897,7 @@ static void msg_add_eol(void)
static int check_mtime(buf_T *buf, FileInfo *file_info) static int check_mtime(buf_T *buf, FileInfo *file_info)
{ {
if (buf->b_mtime_read != 0 if (buf->b_mtime_read != 0
&& time_differs(file_info->stat.st_mtim.tv_sec, && time_differs(file_info, buf->b_mtime_read, buf->b_mtime_read_ns)) {
buf->b_mtime_read)) {
msg_scroll = true; // Don't overwrite messages here. msg_scroll = true; // Don't overwrite messages here.
msg_silent = 0; // Must give this prompt. msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers. // Don't use emsg() here, don't want to flush the buffers.
@ -3908,19 +3911,17 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
return OK; return OK;
} }
/// Return true if the times differ static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST
///
/// @param t1 first time
/// @param t2 second time
static bool time_differs(long t1, long t2) FUNC_ATTR_CONST
{ {
#if defined(__linux__) || defined(MSWIN) #if defined(__linux__) || defined(MSWIN)
// On a FAT filesystem, esp. under Linux, there are only 5 bits to store // 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 // the seconds. Since the roundoff is done when flushing the inode, the
// time may change unexpectedly by one second!!! // time may change unexpectedly by one second!!!
return t1 - t2 > 1 || t2 - t1 > 1; return (long)file_info->stat.st_mtim.tv_sec - mtime > 1
|| mtime - (long)file_info->stat.st_mtim.tv_sec > 1
|| (long)file_info->stat.st_mtim.tv_nsec != mtime_ns;
#else #else
return t1 != t2; return (long)file_info->stat.st_mtim.tv_sec != mtime;
#endif #endif
} }
@ -4943,7 +4944,7 @@ int buf_check_timestamp(buf_T *buf)
if (!(buf->b_flags & BF_NOTEDITED) if (!(buf->b_flags & BF_NOTEDITED)
&& buf->b_mtime != 0 && buf->b_mtime != 0
&& (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info)) && (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info))
|| time_differs(file_info.stat.st_mtim.tv_sec, buf->b_mtime) || time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns)
|| (int)file_info.stat.st_mode != buf->b_orig_mode)) { || (int)file_info.stat.st_mode != buf->b_orig_mode)) {
const long prev_b_mtime = buf->b_mtime; const long prev_b_mtime = buf->b_mtime;
@ -5034,6 +5035,7 @@ int buf_check_timestamp(buf_T *buf)
// Only timestamp changed, store it to avoid a warning // Only timestamp changed, store it to avoid a warning
// in check_mtime() later. // in check_mtime() later.
buf->b_mtime_read = buf->b_mtime; buf->b_mtime_read = buf->b_mtime;
buf->b_mtime_read_ns = buf->b_mtime_ns;
} }
} }
} }
@ -5262,6 +5264,7 @@ void buf_store_file_info(buf_T *buf, FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
buf->b_mtime = file_info->stat.st_mtim.tv_sec; buf->b_mtime = file_info->stat.st_mtim.tv_sec;
buf->b_mtime_ns = file_info->stat.st_mtim.tv_nsec;
buf->b_orig_size = os_fileinfo_size(file_info); buf->b_orig_size = os_fileinfo_size(file_info);
buf->b_orig_mode = (int)file_info->stat.st_mode; buf->b_orig_mode = (int)file_info->stat.st_mode;
} }

View File

@ -704,11 +704,14 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino); long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino);
buf_store_file_info(buf, &file_info); buf_store_file_info(buf, &file_info);
buf->b_mtime_read = buf->b_mtime; buf->b_mtime_read = buf->b_mtime;
buf->b_mtime_read_ns = buf->b_mtime_ns;
} else { } else {
long_to_char(0L, b0p->b0_mtime); long_to_char(0L, b0p->b0_mtime);
long_to_char(0L, b0p->b0_ino); long_to_char(0L, b0p->b0_ino);
buf->b_mtime = 0; buf->b_mtime = 0;
buf->b_mtime_ns = 0;
buf->b_mtime_read = 0; buf->b_mtime_read = 0;
buf->b_mtime_read_ns = 0;
buf->b_orig_size = 0; buf->b_orig_size = 0;
buf->b_orig_mode = 0; buf->b_orig_mode = 0;
} }
@ -1720,6 +1723,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
FileInfo file_info; FileInfo file_info;
if (!os_fileinfo((char *)buf->b_ffname, &file_info) if (!os_fileinfo((char *)buf->b_ffname, &file_info)
|| file_info.stat.st_mtim.tv_sec != buf->b_mtime_read || file_info.stat.st_mtim.tv_sec != buf->b_mtime_read
|| file_info.stat.st_mtim.tv_nsec != buf->b_mtime_read_ns
|| os_fileinfo_size(&file_info) != buf->b_orig_size) { || os_fileinfo_size(&file_info) != buf->b_orig_size) {
ml_preserve(buf, false, do_fsync); ml_preserve(buf, false, do_fsync);
did_check_timestamps = false; did_check_timestamps = false;

View File

@ -1,5 +1,7 @@
" Tests for stat functions and checktime " Tests for stat functions and checktime
source check.vim
func CheckFileTime(doSleep) func CheckFileTime(doSleep)
let fnames = ['Xtest1.tmp', 'Xtest2.tmp', 'Xtest3.tmp'] let fnames = ['Xtest1.tmp', 'Xtest2.tmp', 'Xtest3.tmp']
let times = [] let times = []
@ -74,6 +76,40 @@ func Test_checktime()
call delete(fname) call delete(fname)
endfunc 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'
sleep 10m " make test less flaky in Nvim
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() func Test_autoread_file_deleted()
new Xautoread new Xautoread
set autoread set autoread