mercurial/dirstateutils/timestamp.py
author Rapha?l Gom?s <rgomes@octobus.net>
Wed, 16 Oct 2024 19:14:30 +0200
changeset 52055 b332ae615714
parent 52053 572d80e51094
child 52643 5cc8deb96b48
permissions -rw-r--r--
merge: improve working-copy mtime race handling Explanations inline. This also makes use of `make_mtime_reliable`, which unifies our mtime raciness logic from the status. On top of this, this fixes the handling of the pure dirstate status to better catch racy status, as we've been doing in Rust for a long time now.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     1
# Copyright Mercurial Contributors
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     2
#
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     3
# This software may be used and distributed according to the terms of the
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     4
# GNU General Public License version 2 or any later version.
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     5
51859
f4733654f144 typing: add `from __future__ import annotations` to most files
Matt Harbison <matt_harbison@yahoo.com>
parents: 48875
diff changeset
     6
from __future__ import annotations
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     7
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
     8
import functools
48379
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
     9
import os
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    10
import stat
52052
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
    11
import time
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
    12
from typing import Optional, Tuple
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    13
48397
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    14
from .. import error
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    15
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    16
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    17
rangemask = 0x7FFFFFFF
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    18
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    19
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    20
@functools.total_ordering
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
class timestamp(tuple):
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    22
    """
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    23
    A Unix timestamp with optional nanoseconds precision,
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    24
    modulo 2**31 seconds.
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    25
48442
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
    26
    A 3-tuple containing:
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    27
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    28
    `truncated_seconds`: seconds since the Unix epoch,
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    29
    truncated to its lower 31 bits
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    30
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    31
    `subsecond_nanoseconds`: number of nanoseconds since `truncated_seconds`.
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    32
    When this is zero, the sub-second precision is considered unknown.
48442
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
    33
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
    34
    `second_ambiguous`: whether this timestamp is still "reliable"
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
    35
    (see `reliable_mtime_of`) if we drop its sub-second component.
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    36
    """
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    37
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    38
    def __new__(cls, value):
48398
111098af6356 dirstate-item: add a "second_ambiguous` flag in the mtime tuple
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48397
diff changeset
    39
        truncated_seconds, subsec_nanos, second_ambiguous = value
111098af6356 dirstate-item: add a "second_ambiguous` flag in the mtime tuple
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48397
diff changeset
    40
        value = (truncated_seconds & rangemask, subsec_nanos, second_ambiguous)
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    41
        return super(timestamp, cls).__new__(cls, value)
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    42
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    43
    def __eq__(self, other):
48397
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    44
        raise error.ProgrammingError(
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    45
            'timestamp should never be compared directly'
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    46
        )
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    47
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    48
    def __gt__(self, other):
48397
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    49
        raise error.ProgrammingError(
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    50
            'timestamp should never be compared directly'
8d585aa9becf dirstate: drop comparison primitive on the timestamp class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48395
diff changeset
    51
        )
48262
68bb472aee9c dirstate: ignore sub-second component when either is zero in mtime
Simon Sapin <simon.sapin@octobus.net>
parents: 48260
diff changeset
    52
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    53
52052
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
    54
def get_fs_now(vfs) -> Optional[timestamp]:
48379
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    55
    """return a timestamp for "now" in the current vfs
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    56
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    57
    This will raise an exception if no temporary files could be created.
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    58
    """
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    59
    tmpfd, tmpname = vfs.mkstemp()
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    60
    try:
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    61
        return mtime_of(os.fstat(tmpfd))
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    62
    finally:
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    63
        os.close(tmpfd)
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    64
        vfs.unlink(tmpname)
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    65
08b060abd658 dirstate: move "get fs now" in the timestamp utility module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48263
diff changeset
    66
52052
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
    67
def zero() -> timestamp:
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    68
    """
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    69
    Returns the `timestamp` at the Unix epoch.
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    70
    """
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    71
    return tuple.__new__(timestamp, (0, 0))
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    72
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    73
52052
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
    74
def mtime_of(stat_result: os.stat_result) -> timestamp:
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
    """
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    76
    Takes an `os.stat_result`-like object and returns a `timestamp` object
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    77
    for its modification time.
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    78
    """
48263
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    79
    try:
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    80
        # TODO: add this attribute to `osutil.stat` objects,
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    81
        # see `mercurial/cext/osutil.c`.
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    82
        #
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    83
        # This attribute is also not available on Python 2.
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    84
        nanos = stat_result.st_mtime_ns
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    85
    except AttributeError:
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    86
        # https://docs.python.org/2/library/os.html#os.stat_float_times
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    87
        # "For compatibility with older Python versions,
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    88
        #  accessing stat_result as a tuple always returns integers."
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    89
        secs = stat_result[stat.ST_MTIME]
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    90
48263
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    91
        subsec_nanos = 0
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    92
    else:
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    93
        billion = int(1e9)
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    94
        secs = nanos // billion
83d0bd45b662 dirstate-v2: actually use sub-second mtime precision
Simon Sapin <simon.sapin@octobus.net>
parents: 48262
diff changeset
    95
        subsec_nanos = nanos % billion
48260
269ff8978086 dirstate: store mtimes with nanosecond precision in memory
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
48398
111098af6356 dirstate-item: add a "second_ambiguous` flag in the mtime tuple
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48397
diff changeset
    97
    return timestamp((secs, subsec_nanos, False))
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
    98
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
    99
52052
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
   100
def reliable_mtime_of(
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
   101
    stat_result: os.stat_result, present_mtime: timestamp
7ea1cb46b590 timestamp: add type information to the module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51859
diff changeset
   102
) -> Optional[timestamp]:
52053
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   103
    """Wrapper for `make_mtime_reliable` for stat objects"""
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   104
    file_mtime = mtime_of(stat_result)
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   105
    return make_mtime_reliable(file_mtime, present_mtime)
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   106
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   107
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   108
def make_mtime_reliable(
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   109
    file_timestamp: timestamp, present_mtime: timestamp
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   110
) -> Optional[timestamp]:
48442
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
   111
    """Same as `mtime_of`, but return `None` or a `Timestamp` with
c8ca21962ff4 dirstate: Document Timestamp.second_ambiguous
Simon Sapin <simon.sapin@octobus.net>
parents: 48422
diff changeset
   112
    `second_ambiguous` set if the date might be ambiguous.
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   113
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   114
    A modification time is reliable if it is older than "present_time" (or
48422
000130cfafb6 rhg: Update the dirstate on disk after status
Simon Sapin <simon.sapin@octobus.net>
parents: 48403
diff changeset
   115
    sufficiently in the future).
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   116
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   117
    Otherwise a concurrent modification might happens with the same mtime.
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   118
    """
52053
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   119
    file_second = file_timestamp[0]
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   120
    file_ns = file_timestamp[1]
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   121
    boundary_second = present_mtime[0]
48403
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   122
    boundary_ns = present_mtime[1]
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   123
    # If the mtime of the ambiguous file is younger (or equal) to the starting
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   124
    # point of the `status` walk, we cannot garantee that another, racy, write
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   125
    # will not happen right after with the same mtime and we cannot cache the
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   126
    # information.
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   127
    #
48403
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   128
    # However if the mtime is far away in the future, this is likely some
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   129
    # mismatch between the current clock and previous file system operation. So
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   130
    # mtime more than one days in the future are considered fine.
48403
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   131
    if boundary_second == file_second:
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   132
        if file_ns and boundary_ns:
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   133
            if file_ns < boundary_ns:
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   134
                return timestamp((file_second, file_ns, True))
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   135
        return None
ca42667c8d26 status: keep second-ambiguous mtimes during fixup
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48398
diff changeset
   136
    elif boundary_second < file_second < (3600 * 24 + boundary_second):
48395
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   137
        return None
9ae0353c9f5d status: move the boundary comparison logic within the timestamp module
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 48379
diff changeset
   138
    else:
52053
572d80e51094 timestamp: make the reliable comparison more usable from outside
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52052
diff changeset
   139
        return file_timestamp
52055
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   140
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   141
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   142
FS_TICK_WAIT_TIMEOUT = 0.1  # 100 milliseconds
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   143
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   144
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   145
def wait_until_fs_tick(vfs) -> Optional[Tuple[timestamp, bool]]:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   146
    """Wait until the next update from the filesystem time by writing in a loop
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   147
    a new temporary file inside the working directory and checking if its time
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   148
    differs from the first one observed.
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   149
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   150
    Returns `None` if we are unable to get the filesystem time,
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   151
    `(timestamp, True)` if we've timed out waiting for the filesystem clock
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   152
    to tick, and `(timestamp, False)` if we've waited successfully.
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   153
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   154
    On Linux, your average tick is going to be a "jiffy", or 1/HZ.
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   155
    HZ is your kernel's tick rate (if it has one configured) and the value
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   156
    is the one returned by `grep 'CONFIG_HZ=' /boot/config-$(uname -r)`,
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   157
    again assuming a normal setup.
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   158
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   159
    In my case (Alphare) at the time of writing, I get `CONFIG_HZ=250`,
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   160
    which equates to 4ms.
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   161
    This might change with a series that could make it to Linux 6.12:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   162
    https://lore.kernel.org/all/20241002-mgtime-v10-8-d1c4717f5284@kernel.org
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   163
    """
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   164
    start = time.monotonic()
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   165
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   166
    try:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   167
        old_fs_time = get_fs_now(vfs)
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   168
        new_fs_time = get_fs_now(vfs)
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   169
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   170
        while (
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   171
            new_fs_time[0] == old_fs_time[0]
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   172
            and new_fs_time[1] == old_fs_time[1]
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   173
        ):
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   174
            if time.monotonic() - start > FS_TICK_WAIT_TIMEOUT:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   175
                return (old_fs_time, True)
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   176
            new_fs_time = get_fs_now(vfs)
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   177
    except OSError:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   178
        return None
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   179
    else:
b332ae615714 merge: improve working-copy mtime race handling
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52053
diff changeset
   180
        return (new_fs_time, False)