diff options
| author | Po Lu | 2023-08-08 16:02:49 +0800 |
|---|---|---|
| committer | Po Lu | 2023-08-08 16:02:49 +0800 |
| commit | 27113c22f77b7a409c33b956a1a8d8be2d5bc673 (patch) | |
| tree | 663e42ad962f3383873213e6923e842da47e3d7f /src | |
| parent | 27a57f4cca55fd64a61eb8952b6422712f27b0af (diff) | |
| download | emacs-27113c22f77b7a409c33b956a1a8d8be2d5bc673.tar.gz emacs-27113c22f77b7a409c33b956a1a8d8be2d5bc673.zip | |
Minor improvements to write-region heuristic
* src/androidvfs.c (android_saf_stat): Set STATB->st_dev.
(android_fstat): Likewise.
(NATIVE_NAME): Seek to start of file after truncation.
* src/fileio.c (write_region): Use stat instead of open+fstat
to obtain updated mtime.
Diffstat (limited to 'src')
| -rw-r--r-- | src/androidvfs.c | 15 | ||||
| -rw-r--r-- | src/fileio.c | 68 |
2 files changed, 49 insertions, 34 deletions
diff --git a/src/androidvfs.c b/src/androidvfs.c index 93927ccc86b..8e742f8b26f 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c | |||
| @@ -4013,6 +4013,7 @@ android_saf_stat (const char *uri_name, const char *id_name, | |||
| 4013 | memset (statb, 0, sizeof *statb); | 4013 | memset (statb, 0, sizeof *statb); |
| 4014 | statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size)); | 4014 | statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size)); |
| 4015 | statb->st_mode = mode; | 4015 | statb->st_mode = mode; |
| 4016 | statb->st_dev = -4; | ||
| 4016 | #ifdef STAT_TIMESPEC | 4017 | #ifdef STAT_TIMESPEC |
| 4017 | STAT_TIMESPEC (statb, st_mtim).tv_sec = mtim / 1000; | 4018 | STAT_TIMESPEC (statb, st_mtim).tv_sec = mtim / 1000; |
| 4018 | STAT_TIMESPEC (statb, st_mtim).tv_nsec = (mtim % 1000) * 1000000; | 4019 | STAT_TIMESPEC (statb, st_mtim).tv_nsec = (mtim % 1000) * 1000000; |
| @@ -6169,7 +6170,14 @@ NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) | |||
| 6169 | JNIEXPORT jboolean JNICALL | 6170 | JNIEXPORT jboolean JNICALL |
| 6170 | NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd) | 6171 | NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd) |
| 6171 | { | 6172 | { |
| 6172 | return ftruncate (fd, 0) != -1; | 6173 | if (ftruncate (fd, 0) < 0) |
| 6174 | return false; | ||
| 6175 | |||
| 6176 | /* Reset the file pointer. */ | ||
| 6177 | if (lseek (fd, 0, SEEK_SET) < 0) | ||
| 6178 | return false; | ||
| 6179 | |||
| 6180 | return true; | ||
| 6173 | } | 6181 | } |
| 6174 | 6182 | ||
| 6175 | #ifdef __clang__ | 6183 | #ifdef __clang__ |
| @@ -6722,6 +6730,11 @@ android_fstat (int fd, struct stat *statb) | |||
| 6722 | parcel_fd = open_parcel_fds; | 6730 | parcel_fd = open_parcel_fds; |
| 6723 | for (; parcel_fd; parcel_fd = parcel_fd->next) | 6731 | for (; parcel_fd; parcel_fd = parcel_fd->next) |
| 6724 | { | 6732 | { |
| 6733 | if (parcel_fd->fd == fd) | ||
| 6734 | /* Set STATB->st_dev to a negative device number, signifying | ||
| 6735 | that it's contained within a content provider. */ | ||
| 6736 | statb->st_dev = -4; | ||
| 6737 | |||
| 6725 | if (parcel_fd->fd == fd | 6738 | if (parcel_fd->fd == fd |
| 6726 | && timespec_valid_p (parcel_fd->mtime)) | 6739 | && timespec_valid_p (parcel_fd->mtime)) |
| 6727 | { | 6740 | { |
diff --git a/src/fileio.c b/src/fileio.c index 26b7e193f0a..5d01e10f0ef 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -5582,42 +5582,44 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename, | |||
| 5582 | if (timespec_valid_p (modtime) | 5582 | if (timespec_valid_p (modtime) |
| 5583 | && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system)) | 5583 | && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system)) |
| 5584 | { | 5584 | { |
| 5585 | int desc1 = emacs_open (fn, O_WRONLY, 0); | 5585 | struct stat st1; |
| 5586 | if (desc1 >= 0) | 5586 | |
| 5587 | /* The code below previously tried to open FN O_WRONLY, | ||
| 5588 | subsequently calling fstat on the opened file descriptor. | ||
| 5589 | This proved inefficient and resulted in FN being truncated | ||
| 5590 | under several Android filesystems, and as such has been | ||
| 5591 | changed to a call to `stat'. */ | ||
| 5592 | |||
| 5593 | if (emacs_fstatat (AT_FDCWD, fn, &st1, 0) == 0 | ||
| 5594 | && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) | ||
| 5587 | { | 5595 | { |
| 5588 | struct stat st1; | 5596 | /* Use the heuristic if it appears to be valid. With neither |
| 5589 | if (sys_fstat (desc1, &st1) == 0 | 5597 | O_EXCL nor O_TRUNC, if Emacs happened to write nothing to the |
| 5590 | && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) | 5598 | file, the time stamp won't change. Also, some non-POSIX |
| 5599 | systems don't update an empty file's time stamp when | ||
| 5600 | truncating it. Finally, file systems with 100 ns or worse | ||
| 5601 | resolution sometimes seem to have bugs: on a system with ns | ||
| 5602 | resolution, checking ns % 100 incorrectly avoids the heuristic | ||
| 5603 | 1% of the time, but the problem should be temporary as we will | ||
| 5604 | try again on the next time stamp. */ | ||
| 5605 | bool use_heuristic | ||
| 5606 | = ((open_flags & (O_EXCL | O_TRUNC)) != 0 | ||
| 5607 | && st.st_size != 0 | ||
| 5608 | && modtime.tv_nsec % 100 != 0); | ||
| 5609 | |||
| 5610 | struct timespec modtime1 = get_stat_mtime (&st1); | ||
| 5611 | if (use_heuristic | ||
| 5612 | && timespec_cmp (modtime, modtime1) == 0 | ||
| 5613 | && st.st_size == st1.st_size) | ||
| 5591 | { | 5614 | { |
| 5592 | /* Use the heuristic if it appears to be valid. With neither | 5615 | timestamp_file_system = st.st_dev; |
| 5593 | O_EXCL nor O_TRUNC, if Emacs happened to write nothing to the | 5616 | valid_timestamp_file_system = 1; |
| 5594 | file, the time stamp won't change. Also, some non-POSIX | 5617 | } |
| 5595 | systems don't update an empty file's time stamp when | 5618 | else |
| 5596 | truncating it. Finally, file systems with 100 ns or worse | 5619 | { |
| 5597 | resolution sometimes seem to have bugs: on a system with ns | 5620 | st.st_size = st1.st_size; |
| 5598 | resolution, checking ns % 100 incorrectly avoids the heuristic | 5621 | modtime = modtime1; |
| 5599 | 1% of the time, but the problem should be temporary as we will | ||
| 5600 | try again on the next time stamp. */ | ||
| 5601 | bool use_heuristic | ||
| 5602 | = ((open_flags & (O_EXCL | O_TRUNC)) != 0 | ||
| 5603 | && st.st_size != 0 | ||
| 5604 | && modtime.tv_nsec % 100 != 0); | ||
| 5605 | |||
| 5606 | struct timespec modtime1 = get_stat_mtime (&st1); | ||
| 5607 | if (use_heuristic | ||
| 5608 | && timespec_cmp (modtime, modtime1) == 0 | ||
| 5609 | && st.st_size == st1.st_size) | ||
| 5610 | { | ||
| 5611 | timestamp_file_system = st.st_dev; | ||
| 5612 | valid_timestamp_file_system = 1; | ||
| 5613 | } | ||
| 5614 | else | ||
| 5615 | { | ||
| 5616 | st.st_size = st1.st_size; | ||
| 5617 | modtime = modtime1; | ||
| 5618 | } | ||
| 5619 | } | 5622 | } |
| 5620 | emacs_close (desc1); | ||
| 5621 | } | 5623 | } |
| 5622 | } | 5624 | } |
| 5623 | 5625 | ||