mercurial/dirstateutils/timestamp.py
author Pierre-Yves David <pierre-yves.david@octobus.net>
Tue, 11 Mar 2025 02:29:42 +0100
branchstable
changeset 53042 cdd7bf612c7b
parent 52643 5cc8deb96b48
permissions -rw-r--r--
bundle-spec: properly format boolean parameter (issue6960) This was breaking automatic clone bundle generation. This changeset fixes it and add a test to catch it in the future.
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)
52643
5cc8deb96b48 pyupgrade: modernize calls to superclass methods
Matt Harbison <matt_harbison@yahoo.com>
parents: 52055
diff changeset
    41
        return super().__new__(cls, value)
48260
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)