]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactor: Use util::TimePoint for timestamps
authorJoel Rosdahl <joel@rosdahl.net>
Tue, 20 Sep 2022 17:01:38 +0000 (19:01 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 21 Sep 2022 15:06:32 +0000 (17:06 +0200)
33 files changed:
cmake/GenerateConfigurationFile.cmake
cmake/config.h.in
src/Context.cpp
src/Context.hpp
src/InodeCache.cpp
src/Logging.cpp
src/MiniTrace.cpp
src/Stat.hpp
src/Util.cpp
src/Util.hpp
src/Win32Util.cpp
src/Win32Util.hpp
src/ccache.cpp
src/core/CacheEntry.cpp
src/core/CacheEntry.hpp
src/core/Manifest.cpp
src/core/Manifest.hpp
src/core/Statistics.cpp
src/core/Statistics.hpp
src/core/mainoptions.cpp
src/storage/primary/PrimaryStorage.cpp
src/storage/primary/PrimaryStorage.hpp
src/storage/primary/PrimaryStorage_cleanup.cpp
src/storage/primary/PrimaryStorage_compress.cpp
src/storage/primary/PrimaryStorage_statistics.cpp
src/util/CMakeLists.txt
src/util/LockFile.cpp
src/util/LockFile.hpp
src/util/file.cpp
src/util/file.hpp
unittest/test_Stat.cpp
unittest/test_Util.cpp
unittest/test_util_LockFile.cpp

index 7effeb86e9e7afb38100d225636fe0fe6ccd023c..20be4eac11f9efeb8114845d488e799aa65583e8 100644 (file)
@@ -29,7 +29,6 @@ set(functions
     geteuid
     getopt_long
     getpwuid
-    gettimeofday
     posix_fallocate
     realpath
     setenv
index 9210147ed9e2cb6eee47697952ae56047f22d61f..28ea3c2a91e14aca9e19342af6a33a0f90285f0a 100644 (file)
@@ -85,9 +85,6 @@
 // Define if you have the "getpwuid" function.
 #cmakedefine HAVE_GETPWUID
 
-// Define if you have the "gettimeofday" function.
-#cmakedefine HAVE_GETTIMEOFDAY
-
 // Define if you have the <linux/fs.h> header file.
 #cmakedefine HAVE_LINUX_FS_H
 
index 27a2a2c98d0e4d45c37b9ac9f5221523d220f05a..c46d3684707631b90c2cfe44128826e9fbe35a44 100644 (file)
 
 #include <Win32Util.hpp>
 #include <core/wincompat.hpp>
+#include <util/TimePoint.hpp>
 #include <util/path.hpp>
 
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
@@ -47,7 +45,7 @@ Context::Context()
     inode_cache(config)
 #endif
 {
-  gettimeofday(&time_of_invocation, nullptr);
+  time_of_invocation = util::TimePoint::now();
 }
 
 void
index a2e1ec0a7bb66fa085dbe585639b07e362dcd1f4..63844489a13c7112d721d4e784684c60e90c4e4e 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <core/Manifest.hpp>
 #include <storage/Storage.hpp>
+#include <util/TimePoint.hpp>
 
 #include <ctime>
 #include <optional>
 #include <unordered_map>
 #include <vector>
 
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
-
 class SignalHandler;
 
 class Context : NonCopyable
@@ -69,11 +66,11 @@ public:
   Args orig_args;
 
   // Time of ccache invocation.
-  timeval time_of_invocation;
+  util::TimePoint time_of_invocation;
 
   // Time of compilation. Used to see if include files have changed after
   // compilation.
-  time_t time_of_compilation = 0;
+  util::TimePoint time_of_compilation;
 
   // Files included by the preprocessor and their hashes.
   std::unordered_map<std::string, Digest> included_files;
index 006ffab8cc7476229d559db8dc6333e1de61827d..8f09b98151916c9cd3a44b117dae8f88add9fe6d 100644 (file)
@@ -231,8 +231,8 @@ InodeCache::hash_inode(const std::string& path,
   key.st_dev = stat.device();
   key.st_ino = stat.inode();
   key.st_mode = stat.mode();
-  key.st_mtim = stat.mtim();
-  key.st_ctim = stat.ctim();
+  key.st_mtim = stat.mtime().to_timespec();
+  key.st_ctim = stat.ctime().to_timespec();
   key.st_size = stat.size();
 
   Hash hash;
index 82e4b1c237308fff44f22b64ed58fad01ecf00c1..beac470728cd6f1c32f098105fb92869bedc0e6b 100644 (file)
@@ -35,9 +35,6 @@
 #ifdef HAVE_SYSLOG_H
 #  include <syslog.h>
 #endif
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
 
 #ifdef __linux__
 #  ifdef HAVE_SYS_IOCTL_H
@@ -84,22 +81,21 @@ do_log(std::string_view message, bool bulk)
 
   if (!bulk) {
     char timestamp[100];
-    struct timeval tv;
-    gettimeofday(&tv, nullptr);
-    auto tm = Util::localtime(tv.tv_sec);
+    auto now = util::TimePoint::now();
+    auto tm = Util::localtime(now);
     if (tm) {
       strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &*tm);
     } else {
       snprintf(timestamp,
                sizeof(timestamp),
                "%llu",
-               static_cast<long long unsigned int>(tv.tv_sec));
+               static_cast<long long unsigned int>(now.sec()));
     }
     snprintf(prefix,
              sizeof(prefix),
              "[%s.%06d %-5d] ",
              timestamp,
-             static_cast<int>(tv.tv_usec),
+             static_cast<unsigned int>(now.nsec_decimal_part() / 1000),
              static_cast<int>(getpid()));
   }
 
index 07f495ad7b31a527d213b805fe3c07a3e7419ad6..90ffcc4bdf84db5fcab83d2a6a845be58b33de96 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2020-2021 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2022 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
 #include "fmtmacros.hpp"
 
 #include <core/wincompat.hpp>
-
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
+#include <util/TimePoint.hpp>
 
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
@@ -53,18 +50,6 @@ get_system_tmp_dir()
   return "/tmp";
 }
 
-double
-time_seconds()
-{
-#ifdef HAVE_GETTIMEOFDAY
-  struct timeval tv;
-  gettimeofday(&tv, nullptr);
-  return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-#else
-  return (double)time(nullptr);
-#endif
-}
-
 } // namespace
 
 MiniTrace::MiniTrace(const ArgsInfo& args_info)
@@ -75,7 +60,8 @@ MiniTrace::MiniTrace(const ArgsInfo& args_info)
   m_tmp_trace_file = tmp_file.path;
 
   mtr_init(m_tmp_trace_file.c_str());
-  m_start_time = FMT("{:f}", time_seconds());
+  auto now = util::TimePoint::now();
+  m_start_time = FMT("{}.{:06}", now.sec(), now.nsec_decimal_part() / 1000);
   MTR_INSTANT_C("", "", "time", m_start_time.c_str());
   MTR_META_PROCESS_NAME("ccache");
   MTR_START("program", "ccache", m_trace_id);
index dcc4b2feb359fad7aae72cbdc985a6164bcf5014..5c81a5bad6cb3926c128d799ab83e65a8e2fc158 100644 (file)
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <core/wincompat.hpp>
+#include <util/TimePoint.hpp>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -125,9 +126,9 @@ public:
   dev_t device() const;
   ino_t inode() const;
   mode_t mode() const;
-  time_t atime() const;
-  time_t ctime() const;
-  time_t mtime() const;
+  util::TimePoint atime() const;
+  util::TimePoint ctime() const;
+  util::TimePoint mtime() const;
   uint64_t size() const;
 
   uint64_t size_on_disk() const;
@@ -141,10 +142,6 @@ public:
   uint32_t reparse_tag() const;
 #endif
 
-  timespec atim() const;
-  timespec ctim() const;
-  timespec mtim() const;
-
 protected:
   using StatFunction = int (*)(const char*, stat_t*);
 
@@ -197,22 +194,40 @@ Stat::mode() const
   return m_stat.st_mode;
 }
 
-inline time_t
+inline util::TimePoint
 Stat::atime() const
 {
-  return atim().tv_sec;
+#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_ATIM)
+  return util::TimePoint(m_stat.st_atim);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
+  return util::TimePoint(m_stat.st_atimespec);
+#else
+  return util::TimePoint(m_stat.st_atime, 0);
+#endif
 }
 
-inline time_t
+inline util::TimePoint
 Stat::ctime() const
 {
-  return ctim().tv_sec;
+#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_CTIM)
+  return util::TimePoint(m_stat.st_ctim);
+#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
+  return util::TimePoint(m_stat.st_ctimespec);
+#else
+  return util::TimePoint(m_stat.st_ctime, 0);
+#endif
 }
 
-inline time_t
+inline util::TimePoint
 Stat::mtime() const
 {
-  return mtim().tv_sec;
+#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_MTIM)
+  return util::TimePoint(m_stat.st_mtim);
+#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
+  return util::TimePoint(m_stat.st_mtimespec);
+#else
+  return util::TimePoint(m_stat.st_mtime, 0);
+#endif
 }
 
 inline uint64_t
@@ -262,39 +277,3 @@ Stat::reparse_tag() const
   return m_stat.st_reparse_tag;
 }
 #endif
-
-inline timespec
-Stat::atim() const
-{
-#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_ATIM)
-  return m_stat.st_atim;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
-  return m_stat.st_atimespec;
-#else
-  return {m_stat.st_atime, 0};
-#endif
-}
-
-inline timespec
-Stat::ctim() const
-{
-#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_CTIM)
-  return m_stat.st_ctim;
-#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
-  return m_stat.st_ctimespec;
-#else
-  return {m_stat.st_ctime, 0};
-#endif
-}
-
-inline timespec
-Stat::mtim() const
-{
-#if defined(_WIN32) || defined(HAVE_STRUCT_STAT_ST_MTIM)
-  return m_stat.st_mtim;
-#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
-  return m_stat.st_mtimespec;
-#else
-  return {m_stat.st_mtime, 0};
-#endif
-}
index 8b0b6e0ec58591f9ba2951f4dcd1cd664e8b4985..2d9bb3b733afd21fcb85e11d33771c29ffb178e9 100644 (file)
@@ -30,6 +30,7 @@
 #include <core/exceptions.hpp>
 #include <core/wincompat.hpp>
 #include <fmtmacros.hpp>
+#include <util/TimePoint.hpp>
 #include <util/file.hpp>
 #include <util/path.hpp>
 #include <util/string.hpp>
@@ -52,10 +53,6 @@ extern "C" {
 #  include <pwd.h>
 #endif
 
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
-
 #ifdef __linux__
 #  ifdef HAVE_SYS_IOCTL_H
 #    include <sys/ioctl.h>
@@ -795,9 +792,9 @@ is_precompiled_header(std::string_view path)
 }
 
 std::optional<tm>
-localtime(std::optional<time_t> time)
+localtime(std::optional<util::TimePoint> time)
 {
-  time_t timestamp = time ? *time : ::time(nullptr);
+  time_t timestamp = time ? time->sec() : util::TimePoint::now().sec();
   tm result;
   if (localtime_r(&timestamp, &result)) {
     return result;
index 2b2f0c1fffb3753caca05a24d819c525c5e2bd15..e29aff3d4308c9f37fd190e5a8f55600892d009e 100644 (file)
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <Stat.hpp>
+#include <util/TimePoint.hpp>
 #include <util/Tokenizer.hpp>
 
 #include <cstdint>
@@ -235,7 +236,7 @@ bool is_precompiled_header(std::string_view path);
 
 // Thread-safe version of `localtime(3)`. If `time` is not specified the current
 // time of day is used.
-std::optional<tm> localtime(std::optional<time_t> time = {});
+std::optional<tm> localtime(std::optional<util::TimePoint> time = {});
 
 // Construct a normalized native path.
 //
index 92d1ffb58a505360a7524efbc28002d898457042..2769f48a3374f884a214a70631196e4912e81c9c 100644 (file)
@@ -133,22 +133,6 @@ get_last_ntstatus()
 
 } // namespace Win32Util
 
-// From: https://stackoverflow.com/a/58162122/262458
-#ifdef _MSC_VER
-int
-gettimeofday(struct timeval* tp, struct timezone* /*tzp*/)
-{
-  namespace sc = std::chrono;
-  sc::system_clock::duration d = sc::system_clock::now().time_since_epoch();
-  sc::seconds s = sc::duration_cast<sc::seconds>(d);
-  tp->tv_sec = static_cast<long>(s.count());
-  tp->tv_usec =
-    static_cast<long>(sc::duration_cast<sc::microseconds>(d - s).count());
-
-  return 0;
-}
-#endif
-
 struct tm*
 localtime_r(time_t* _clock, struct tm* _result)
 {
index c805dc7d8f41d0d3fdf01dbafc8999e02dac854a..2611759f85d4331eea4ebd86943444ff3b5c13b3 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2020-2021 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2022 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -27,7 +27,6 @@
 struct tm* localtime_r(time_t* _clock, struct tm* _result);
 
 #  ifdef _MSC_VER
-int gettimeofday(struct timeval* tp, struct timezone* tzp);
 int asprintf(char** strp, const char* fmt, ...);
 #  endif
 
index 7bd3e4f3d72094df6680371d50a89e81c8ca40e3..94d4a561aafe9cf3b583447eef6b18327fffa1ed 100644 (file)
@@ -65,9 +65,6 @@
 #include <optional>
 #include <string_view>
 
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
@@ -161,7 +158,7 @@ add_prefix(const Context& ctx, Args& args, const std::string& prefix_command)
 
 static std::string
 prepare_debug_path(const std::string& debug_dir,
-                   const timeval& time_of_invocation,
+                   const util::TimePoint& time_of_invocation,
                    const std::string& output_obj,
                    std::string_view suffix)
 {
@@ -174,19 +171,19 @@ prepare_debug_path(const std::string& debug_dir,
   }
 
   char timestamp[100];
-  const auto tm = Util::localtime(time_of_invocation.tv_sec);
+  const auto tm = Util::localtime(time_of_invocation);
   if (tm) {
     strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &*tm);
   } else {
     snprintf(timestamp,
              sizeof(timestamp),
              "%llu",
-             static_cast<long long unsigned int>(time_of_invocation.tv_sec));
+             static_cast<long long unsigned int>(time_of_invocation.sec()));
   }
   return FMT("{}.{}_{:06}.ccache-{}",
              prefix,
              timestamp,
-             time_of_invocation.tv_usec,
+             time_of_invocation.nsec_decimal_part() / 1000,
              suffix);
 }
 
@@ -265,14 +262,14 @@ include_file_too_new(const Context& ctx,
   // starting compilation and writing the include file. See also the notes under
   // "Performance" in doc/MANUAL.adoc.
   if (!(ctx.config.sloppiness().is_enabled(core::Sloppy::include_file_mtime))
-      && path_stat.mtime() >= ctx.time_of_compilation) {
+      && path_stat.mtime().sec() >= ctx.time_of_compilation.sec()) {
     LOG("Include file {} too new", path);
     return true;
   }
 
   // The same >= logic as above applies to the change time of the file.
   if (!(ctx.config.sloppiness().is_enabled(core::Sloppy::include_file_ctime))
-      && path_stat.ctime() >= ctx.time_of_compilation) {
+      && path_stat.ctime().sec() >= ctx.time_of_compilation.sec()) {
     LOG("Include file {} ctime too new", path);
     return true;
   }
@@ -799,8 +796,8 @@ update_manifest(Context& ctx,
         && ctx.time_of_compilation > std::max(stat.mtime(), stat.ctime());
       return core::Manifest::FileStats{
         stat.size(),
-        stat && cache_time ? stat.mtime() : -1,
-        stat && cache_time ? stat.ctime() : -1,
+        stat && cache_time ? stat.mtime() : util::TimePoint(),
+        stat && cache_time ? stat.ctime() : util::TimePoint(),
       };
     });
   if (added) {
@@ -1020,7 +1017,7 @@ to_cache(Context& ctx,
     depend_mode_args.push_back(depend_extra_args);
     add_prefix(ctx, depend_mode_args, ctx.config.prefix_command());
 
-    ctx.time_of_compilation = time(nullptr);
+    ctx.time_of_compilation = util::TimePoint::now();
     result = do_execute(ctx, depend_mode_args);
   }
   MTR_END("execute", "compiler");
@@ -1101,7 +1098,7 @@ to_cache(Context& ctx,
 static nonstd::expected<Digest, Failure>
 get_result_key_from_cpp(Context& ctx, Args& args, Hash& hash)
 {
-  ctx.time_of_compilation = time(nullptr);
+  ctx.time_of_compilation = util::TimePoint::now();
 
   std::string preprocessed_path;
   std::string cpp_stderr_data;
@@ -1192,7 +1189,7 @@ hash_compiler(const Context& ctx,
   } else if (ctx.config.compiler_check() == "mtime") {
     hash.hash_delimiter("cc_mtime");
     hash.hash(st.size());
-    hash.hash(st.mtime());
+    hash.hash(st.mtime().nsec());
   } else if (util::starts_with(ctx.config.compiler_check(), "string:")) {
     hash.hash_delimiter("cc_hash");
     hash.hash(&ctx.config.compiler_check()[7]);
@@ -1851,7 +1848,7 @@ calculate_result_and_manifest_key(Context& ctx,
   bool found_ccbin = false;
 
   hash.hash_delimiter("cache entry version");
-  hash.hash(core::k_cache_entry_format_version);
+  hash.hash(core::CacheEntry::k_format_version);
 
   hash.hash_delimiter("result version");
   hash.hash(core::Result::k_format_version);
index fda2e11115269216a844d61b9bc6a8d04af4d3ec..17e3253abfa9831638ac3edad55d6d0293400f7f 100644 (file)
@@ -26,6 +26,7 @@
 #include <core/exceptions.hpp>
 #include <core/types.hpp>
 #include <fmtmacros.hpp>
+#include <util/TimePoint.hpp>
 #include <util/expected.hpp>
 #include <util/file.hpp>
 #include <util/zstd.hpp>
@@ -69,16 +70,25 @@ cache_entry_type_from_int(const uint8_t entry_type)
 
 namespace core {
 
+// Version 0:
+//   - First version.
+// Version 1:
+//   - Added self_contained field.
+//   - The checksum is now for the (potentially) compressed payload instead of
+//     the uncompressed payload, and the checksum is now always stored
+//     uncompressed.
+const uint8_t CacheEntry::k_format_version = 1;
+
 CacheEntry::Header::Header(const Config& config,
                            core::CacheEntryType entry_type)
   : magic(k_ccache_magic),
-    entry_format_version(k_cache_entry_format_version),
+    entry_format_version(k_format_version),
     entry_type(entry_type),
     compression_type(compression_type_from_config(config)),
     compression_level(compression_level_from_config(config)),
     self_contained(entry_type != CacheEntryType::result
                    || !core::Result::Serializer::use_raw_files(config)),
-    creation_time(time(nullptr)),
+    creation_time(util::TimePoint::now().sec()),
     ccache_version(CCACHE_VERSION),
     namespace_(config.namespace_()),
     entry_size(0)
@@ -132,7 +142,7 @@ CacheEntry::Header::parse(nonstd::span<const uint8_t> data)
   }
 
   reader.read_int(entry_format_version);
-  if (entry_format_version != core::k_cache_entry_format_version) {
+  if (entry_format_version != k_format_version) {
     throw core::Error(
       FMT("Unknown entry format version: {}", entry_format_version));
   }
index c4adecfc434b05f40d664dcbc06758ed36e03254..0402f3a9f769867c180ae7f474a9fef98720283a 100644 (file)
@@ -63,18 +63,10 @@ namespace core {
 
 const uint16_t k_ccache_magic = 0xccac;
 
-// Version 0:
-//   - First version.
-// Version 1:
-//   - Added self_contained field.
-//   - The checksum is now for the (potentially) compressed payload instead of
-//     the uncompressed payload, and the checksum is now always stored
-//     uncompressed.
-const uint16_t k_cache_entry_format_version = 1;
-
 class CacheEntry
 {
 public:
+  static const uint8_t k_format_version;
   constexpr static uint8_t default_compression_level = 1;
 
   class Header
index 7922be7c94598ffda823b669a1f89bf924d6ce7e..cf4f4fccd4775ce44c0545912ec66b7104cd21bc 100644 (file)
@@ -76,7 +76,7 @@ template<> struct hash<core::Manifest::FileInfo>
 
 namespace core {
 
-const uint8_t Manifest::k_format_version = 0;
+const uint8_t Manifest::k_format_version = 1;
 
 void
 Manifest::read(nonstd::span<const uint8_t> data)
@@ -107,8 +107,8 @@ Manifest::read(nonstd::span<const uint8_t> data)
     reader.read_int(entry.index);
     reader.read_and_copy_bytes({entry.digest.bytes(), Digest::size()});
     reader.read_int(entry.fsize);
-    reader.read_int(entry.mtime);
-    reader.read_int(entry.ctime);
+    entry.mtime.set_sec(reader.read_int<int64_t>());
+    entry.ctime.set_sec(reader.read_int<int64_t>());
   }
 
   const auto result_count = reader.read_int<uint32_t>();
@@ -264,8 +264,8 @@ Manifest::serialize(util::Bytes& output)
     writer.write_int<uint32_t>(file_info.index);
     writer.write_bytes({file_info.digest.bytes(), Digest::size()});
     writer.write_int(file_info.fsize);
-    writer.write_int(file_info.mtime);
-    writer.write_int(file_info.ctime);
+    writer.write_int(file_info.mtime.sec());
+    writer.write_int(file_info.ctime.sec());
   }
 
   writer.write_int<uint32_t>(m_results.size());
@@ -430,8 +430,16 @@ Manifest::inspect(FILE* const stream) const
     PRINT(stream, "    Path index: {}\n", m_file_infos[i].index);
     PRINT(stream, "    Hash: {}\n", m_file_infos[i].digest.to_string());
     PRINT(stream, "    File size: {}\n", m_file_infos[i].fsize);
-    PRINT(stream, "    Mtime: {}\n", m_file_infos[i].mtime);
-    PRINT(stream, "    Ctime: {}\n", m_file_infos[i].ctime);
+    if (m_file_infos[i].mtime == util::TimePoint(-1)) {
+      PRINT_RAW(stream, "    Mtime: -\n");
+    } else {
+      PRINT(stream, "    Mtime: {}\n", m_file_infos[i].mtime.sec());
+    }
+    if (m_file_infos[i].ctime == util::TimePoint(-1)) {
+      PRINT_RAW(stream, "    Ctime: -\n");
+    } else {
+      PRINT(stream, "    Ctime: {}\n", m_file_infos[i].ctime.sec());
+    }
   }
 
   PRINT(stream, "Results ({}):\n", m_results.size());
index 399194bc4aa1baf04a8d8afcee6982d6dfd9640f..a8f3b67e7642decda4aa51e529368c6b68d37ea1 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <Digest.hpp>
 #include <core/Serializer.hpp>
+#include <util/TimePoint.hpp>
 
 #include <third_party/nonstd/span.hpp>
 
@@ -42,8 +43,8 @@ public:
   struct FileStats
   {
     uint64_t size;
-    int64_t mtime;
-    int64_t ctime;
+    util::TimePoint mtime;
+    util::TimePoint ctime;
   };
 
   using FileStater = std::function<FileStats(std::string)>;
@@ -67,11 +68,11 @@ public:
 private:
   struct FileInfo
   {
-    uint32_t index; // Index to m_files.
-    Digest digest;  // Digest of referenced file.
-    uint64_t fsize; // Size of referenced file.
-    int64_t mtime;  // mtime of referenced file.
-    int64_t ctime;  // ctime of referenced file.
+    uint32_t index;        // Index to m_files.
+    Digest digest;         // Digest of referenced file.
+    uint64_t fsize;        // Size of referenced file.
+    util::TimePoint mtime; // mtime of referenced file.
+    util::TimePoint ctime; // ctime of referenced file.
 
     bool operator==(const FileInfo& other) const;
   };
index ab7d5e4bd69622452a613efd7c1d8a689cc3ee7b..289b77222f85eabee973f3fef6934dfc5bfaa80b 100644 (file)
@@ -127,9 +127,9 @@ static_assert(sizeof(k_statistics_fields) / sizeof(k_statistics_fields[0])
               == static_cast<size_t>(Statistic::END) - 1);
 
 static std::string
-format_timestamp(const uint64_t value)
+format_timestamp(const util::TimePoint& value)
 {
-  if (value == 0) {
+  if (value.sec() == 0) {
     return "never";
   } else {
     const auto tm = Util::localtime(value);
@@ -219,7 +219,7 @@ add_ratio_row(util::TextTable& table,
 
 std::string
 Statistics::format_human_readable(const Config& config,
-                                  const time_t last_updated,
+                                  const util::TimePoint& last_updated,
                                   const uint8_t verbosity,
                                   const bool from_log) const
 {
@@ -251,7 +251,7 @@ Statistics::format_human_readable(const Config& config,
     table.add_row(
       {"Stats updated:", C(format_timestamp(last_updated)).colspan(4)});
     if (verbosity > 1) {
-      const uint64_t last_zeroed = S(stats_zeroed_timestamp);
+      const util::TimePoint last_zeroed(S(stats_zeroed_timestamp));
       table.add_row(
         {"Stats zeroed:", C(format_timestamp(last_zeroed)).colspan(4)});
     }
@@ -351,11 +351,11 @@ Statistics::format_human_readable(const Config& config,
 }
 
 std::string
-Statistics::format_machine_readable(const time_t last_updated) const
+Statistics::format_machine_readable(const util::TimePoint& last_updated) const
 {
   std::vector<std::string> lines;
 
-  lines.push_back(FMT("stats_updated_timestamp\t{}\n", last_updated));
+  lines.push_back(FMT("stats_updated_timestamp\t{}\n", last_updated.sec()));
 
   for (const auto& field : k_statistics_fields) {
     if (!(field.flags & FLAG_NEVER)) {
index eb80e1caed5bf799515880c4ca5f160d367e59b0..f74d81e522741c9f0b0f22eebd9b05cb910e58aa 100644 (file)
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <core/StatisticsCounters.hpp>
+#include <util/TimePoint.hpp>
 
 #include <cstdint>
 #include <ctime>
@@ -40,12 +41,13 @@ public:
 
   // Format cache statistics in human-readable format.
   std::string format_human_readable(const Config& config,
-                                    time_t last_updated,
+                                    const util::TimePoint& last_updated,
                                     uint8_t verbosity,
                                     bool from_log) const;
 
   // Format cache statistics in machine-readable format.
-  std::string format_machine_readable(time_t last_updated) const;
+  std::string
+  format_machine_readable(const util::TimePoint& last_updated) const;
 
   const StatisticsCounters& counters() const;
 
index 717ca8e5c3e55fdb6a0589af0e7159d951af00de..0fbe2e9a8568650c58258f28c80ed61b47dab43e 100644 (file)
@@ -283,11 +283,8 @@ trim_dir(const std::string& dir,
   });
 
   std::sort(files.begin(), files.end(), [&](const auto& f1, const auto& f2) {
-    const auto ts_1 = trim_lru_mtime ? f1.stat.mtim() : f1.stat.atim();
-    const auto ts_2 = trim_lru_mtime ? f2.stat.mtim() : f2.stat.atim();
-    const auto ns_1 = 1'000'000'000ULL * ts_1.tv_sec + ts_1.tv_nsec;
-    const auto ns_2 = 1'000'000'000ULL * ts_2.tv_sec + ts_2.tv_nsec;
-    return ns_1 < ns_2;
+    return trim_lru_mtime ? f1.stat.mtime() < f2.stat.mtime()
+                          : f1.stat.atime() < f2.stat.atime();
   });
 
   uint64_t size_after = size_before;
@@ -506,9 +503,7 @@ process_main_options(int argc, const char* const* argv)
       return inspect_path(arg);
 
     case PRINT_STATS: {
-      StatisticsCounters counters;
-      time_t last_updated;
-      std::tie(counters, last_updated) =
+      const auto [counters, last_updated] =
         storage::primary::PrimaryStorage(config).get_all_statistics();
       Statistics statistics(counters);
       PRINT_RAW(stdout, statistics.format_machine_readable(last_updated));
@@ -600,9 +595,7 @@ process_main_options(int argc, const char* const* argv)
     }
 
     case 's': { // --show-stats
-      StatisticsCounters counters;
-      time_t last_updated;
-      std::tie(counters, last_updated) =
+      const auto [counters, last_updated] =
         storage::primary::PrimaryStorage(config).get_all_statistics();
       Statistics statistics(counters);
       PRINT_RAW(stdout,
index 32c7f581e81483d776bfa6b04ee3f70fd053b412..88154bfffec0a9c7c68a39b3fdb43efedaf41e2f 100644 (file)
@@ -28,6 +28,7 @@
 #include <core/wincompat.hpp>
 #include <fmtmacros.hpp>
 #include <storage/primary/StatsFile.hpp>
+#include <util/Duration.hpp>
 #include <util/file.hpp>
 
 #ifdef HAVE_UNISTD_H
@@ -40,7 +41,7 @@ namespace storage::primary {
 
 // How often (in seconds) to scan $CCACHE_DIR/tmp for left-over temporary
 // files.
-const int k_tempdir_cleanup_interval = 2 * 24 * 60 * 60; // 2 days
+const util::Duration k_tempdir_cleanup_interval(2 * 24 * 60 * 60); // 2 days
 
 // Maximum files per cache directory. This constant is somewhat arbitrarily
 // chosen to be large enough to avoid unnecessary cache levels but small enough
@@ -371,7 +372,7 @@ PrimaryStorage::clean_internal_tempdir()
 {
   MTR_SCOPE("primary_storage", "clean_internal_tempdir");
 
-  const time_t now = time(nullptr);
+  const auto now = util::TimePoint::now();
   const auto cleaned_stamp = FMT("{}/.cleaned", m_config.temporary_dir());
   const auto cleaned_stat = Stat::stat(cleaned_stamp);
   if (cleaned_stat
index 2789a141d28c69e21d3bb92ccd12cbb2505c2a43..966f04e1e1621c281c5a26d9219052064cddb425 100644 (file)
@@ -25,6 +25,7 @@
 #include <storage/primary/util.hpp>
 #include <storage/types.hpp>
 #include <util/Bytes.hpp>
+#include <util/TimePoint.hpp>
 
 #include <third_party/nonstd/span.hpp>
 
@@ -85,7 +86,8 @@ public:
   void zero_all_statistics();
 
   // Get statistics and last time of update for the whole primary storage cache.
-  std::pair<core::StatisticsCounters, time_t> get_all_statistics() const;
+  std::pair<core::StatisticsCounters, util::TimePoint>
+  get_all_statistics() const;
 
   // --- Cleanup ---
 
index 6165c724d6f488bab4cf14437037a1d250911bec..060720c384ee893dc4ed69af43e3da73acb294a5 100644 (file)
@@ -109,7 +109,7 @@ PrimaryStorage::clean_dir(const std::string& subdir,
 
   uint64_t cache_size = 0;
   uint64_t files_in_cache = 0;
-  time_t current_time = time(nullptr);
+  auto current_time = util::TimePoint::now();
   std::unordered_map<std::string /*result_file*/,
                      std::vector<std::string> /*associated_raw_files*/>
     raw_files_map;
@@ -124,7 +124,7 @@ PrimaryStorage::clean_dir(const std::string& subdir,
     }
 
     // Delete any tmp files older than 1 hour right away.
-    if (file.lstat().mtime() + 3600 < current_time
+    if (file.lstat().mtime() + util::Duration(3600) < current_time
         && TemporaryFile::is_tmp_file(file.path())) {
       Util::unlink_tmp(file.path());
       continue;
@@ -142,11 +142,7 @@ PrimaryStorage::clean_dir(const std::string& subdir,
 
   // Sort according to modification time, oldest first.
   std::sort(files.begin(), files.end(), [](const auto& f1, const auto& f2) {
-    const auto ts_1 = f1.lstat().mtim();
-    const auto ts_2 = f2.lstat().mtim();
-    const auto ns_1 = 1'000'000'000ULL * ts_1.tv_sec + ts_1.tv_nsec;
-    const auto ns_2 = 1'000'000'000ULL * ts_2.tv_sec + ts_2.tv_nsec;
-    return ns_1 < ns_2;
+    return f1.lstat().mtime() < f2.lstat().mtime();
   });
 
   LOG("Before cleanup: {:.0f} KiB, {:.0f} files",
@@ -165,8 +161,7 @@ PrimaryStorage::clean_dir(const std::string& subdir,
     if ((max_size == 0 || cache_size <= max_size)
         && (max_files == 0 || files_in_cache <= max_files)
         && (!max_age
-            || file.lstat().mtime()
-                 > (current_time - static_cast<int64_t>(*max_age)))
+            || file.lstat().mtime() > (current_time - util::Duration(*max_age)))
         && (!namespace_ || max_age)) {
       break;
     }
index bceed45551dc77682af4aba0bb7ffe0065c1798d..b9ee286f1c62bc744e0cb866ffaee15f436d693a 100644 (file)
@@ -136,7 +136,7 @@ recompress_file(RecompressionStatistics& statistics,
   core::CacheEntry cache_entry(cache_file_data);
   cache_entry.verify_checksum();
 
-  header.entry_format_version = core::k_cache_entry_format_version;
+  header.entry_format_version = core::CacheEntry::k_format_version;
   header.compression_type =
     level ? core::CompressionType::zstd : core::CompressionType::none;
   header.compression_level = wanted_level;
@@ -147,7 +147,7 @@ recompress_file(RecompressionStatistics& statistics,
   new_cache_file.commit();
 
   // Restore mtime/atime to keep cache LRU cleanup working as expected:
-  util::set_timestamps(cache_file.path(), old_stat.mtim(), old_stat.atim());
+  util::set_timestamps(cache_file.path(), old_stat.mtime(), old_stat.atime());
 
   const auto new_stat = Stat::stat(cache_file.path(), Stat::OnError::log);
   StatsFile(stats_file).update([=](auto& cs) {
index dc9ff146c40caf95540a1c2850f04f8d6247deb2..833e13ddb35e02f595bac7f481dbda5cd587bcd9 100644 (file)
@@ -45,7 +45,7 @@ for_each_level_1_and_2_stats_file(
 void
 PrimaryStorage::zero_all_statistics()
 {
-  const time_t timestamp = time(nullptr);
+  const auto now = util::TimePoint::now();
   const auto zeroable_fields = core::Statistics::get_zeroable_fields();
 
   for_each_level_1_and_2_stats_file(
@@ -54,18 +54,18 @@ PrimaryStorage::zero_all_statistics()
         for (const auto statistic : zeroable_fields) {
           cs.set(statistic, 0);
         }
-        cs.set(core::Statistic::stats_zeroed_timestamp, timestamp);
+        cs.set(core::Statistic::stats_zeroed_timestamp, now.sec());
       });
     });
 }
 
 // Get statistics and last time of update for the whole primary storage cache.
-std::pair<core::StatisticsCounters, time_t>
+std::pair<core::StatisticsCounters, util::TimePoint>
 PrimaryStorage::get_all_statistics() const
 {
   core::StatisticsCounters counters;
   uint64_t zero_timestamp = 0;
-  time_t last_updated = 0;
+  util::TimePoint last_updated;
 
   // Add up the stats in each directory.
   for_each_level_1_and_2_stats_file(
index 59d72f6ac39d9e2a668ab71d636836803226e732..f3133e715156c1f8306c8218349b2f77d7e40859 100644 (file)
@@ -3,6 +3,7 @@ set(
   Bytes.cpp
   LockFile.cpp
   TextTable.cpp
+  TimePoint.cpp
   Tokenizer.cpp
   file.cpp
   path.cpp
index 3a0bd7978037d40223824cdaf1a17fcb2ad8043d..0d7e96e8e9ad2feebba4bbaa794ea9799a17edb3 100644 (file)
@@ -29,9 +29,6 @@
 
 #include "third_party/fmt/core.h"
 
-#ifdef HAVE_SYS_TIME_H
-#  include <sys/time.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
 const double k_min_sleep_time = 0.010;
 const double k_max_sleep_time = 0.050;
 #ifndef _WIN32
-const double k_staleness_limit = 2;
-const double k_keep_alive_interval = k_staleness_limit / 4;
-const auto k_keep_alive_interval_ms = std::chrono::milliseconds{
-  static_cast<uint64_t>(k_keep_alive_interval * 1000)};
+const util::Duration k_staleness_limit(2);
+const util::Duration k_keep_alive_interval(k_staleness_limit / 4);
 #endif
 
 namespace {
@@ -155,21 +150,6 @@ LockFile::acquire(const bool blocking)
 
 #ifndef _WIN32
 
-static double
-time_from_clock()
-{
-  timeval tv;
-  gettimeofday(&tv, nullptr);
-  return tv.tv_sec + static_cast<double>(tv.tv_usec) / 1'000'000;
-}
-
-static double
-time_from_stat(const Stat& stat)
-{
-  const auto mtime = stat.mtim();
-  return mtime.tv_sec + static_cast<double>(mtime.tv_nsec) / 1'000'000'000;
-}
-
 bool
 LockFile::do_acquire(const bool blocking)
 {
@@ -178,9 +158,9 @@ LockFile::do_acquire(const bool blocking)
      << std::this_thread::get_id();
   const auto content_prefix = ss.str();
 
-  double last_seen_activity = [this] {
+  util::TimePoint last_seen_activity = [this] {
     const auto last_lock_update = get_last_lock_update();
-    return last_lock_update ? *last_lock_update : time_from_clock();
+    return last_lock_update ? *last_lock_update : util::TimePoint::now();
   }();
 
   std::string initial_content;
@@ -188,7 +168,9 @@ LockFile::do_acquire(const bool blocking)
                                            k_max_sleep_time * 1000);
 
   while (true) {
-    const auto my_content = FMT("{}-{}", content_prefix, time_from_clock());
+    const auto now = util::TimePoint::now();
+    const auto my_content =
+      FMT("{}-{}.{}", content_prefix, now.sec(), now.nsec());
 
     if (symlink(my_content.c_str(), m_lock_file.c_str()) == 0) {
       // We got the lock.
@@ -245,20 +227,23 @@ LockFile::do_acquire(const bool blocking)
       last_seen_activity = std::max(last_seen_activity, *last_lock_update);
     }
 
-    const double inactive_duration = time_from_clock() - last_seen_activity;
+    const util::Duration inactive_duration =
+      util::TimePoint::now() - last_seen_activity;
 
     if (inactive_duration < k_staleness_limit) {
-      LOG("Lock {} held by another process active {:.3f} seconds ago",
+      LOG("Lock {} held by another process active {}.{:03} seconds ago",
           m_lock_file,
-          inactive_duration);
+          inactive_duration.sec(),
+          inactive_duration.nsec() / 1'000'000);
       if (!blocking) {
         return false;
       }
     } else if (content == initial_content) {
       // The lock seems to be stale -- break it and try again.
-      LOG("Breaking {} since it has been inactive for {:.3f} seconds",
+      LOG("Breaking {} since it has been inactive for {}.{:03} seconds",
           m_lock_file,
-          inactive_duration);
+          inactive_duration.sec(),
+          inactive_duration.nsec() / 1'000'000);
       if (!on_before_break() || !Util::unlink_tmp(m_lock_file)) {
         return false;
       }
@@ -376,7 +361,10 @@ LongLivedLockFile::on_after_acquire()
     while (true) {
       std::unique_lock<std::mutex> lock(m_stop_keep_alive_mutex);
       m_stop_keep_alive_condition.wait_for(
-        lock, k_keep_alive_interval_ms, [this] { return m_stop_keep_alive; });
+        lock,
+        std::chrono::seconds(k_keep_alive_interval.sec())
+          + std::chrono::nanoseconds(k_keep_alive_interval.nsec()),
+        [this] { return m_stop_keep_alive; });
       if (m_stop_keep_alive) {
         return;
       }
@@ -407,11 +395,11 @@ LongLivedLockFile::on_before_break()
   return Util::unlink_tmp(m_alive_file);
 }
 
-std::optional<double>
+std::optional<util::TimePoint>
 LongLivedLockFile::get_last_lock_update()
 {
   if (const auto stat = Stat::stat(m_alive_file); stat) {
-    return time_from_stat(stat);
+    return stat.mtime();
   } else {
     return std::nullopt;
   }
index 1bf6808cbf18187f81e3e6008af22d93ae3b0a32..705dd7b87a6d2f2026a019cc8db62fff25015f19 100644 (file)
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <NonCopyable.hpp>
+#include <util/TimePoint.hpp>
 
 #include <condition_variable>
 #include <cstdint>
@@ -62,7 +63,7 @@ private:
 #ifndef _WIN32
   bool do_acquire(bool blocking);
   virtual bool on_before_break();
-  virtual std::optional<double> get_last_lock_update();
+  virtual std::optional<util::TimePoint> get_last_lock_update();
 #else
   void* do_acquire(bool blocking);
 #endif
@@ -98,7 +99,7 @@ private:
   void on_after_acquire() override;
   void on_before_release() override;
   bool on_before_break() override;
-  std::optional<double> get_last_lock_update() override;
+  std::optional<util::TimePoint> get_last_lock_update() override;
 #endif
 };
 
@@ -134,7 +135,7 @@ LockFile::on_before_break()
   return true;
 }
 
-inline std::optional<double>
+inline std::optional<util::TimePoint>
 LockFile::get_last_lock_update()
 {
   return std::nullopt;
index 728f521e91f2e05d476f4fd22e79d7086dcf0436..b3d3771fcf332e29030fb8b68724aa41aea85f08 100644 (file)
 #  endif
 #endif
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
 #include <cerrno>
 #include <codecvt>
 #include <cstring>
@@ -238,30 +242,31 @@ read_file_part(const std::string& path, size_t pos, size_t count);
 
 void
 set_timestamps(const std::string& path,
-               std::optional<timespec> mtime,
-               std::optional<timespec> atime)
+               std::optional<util::TimePoint> mtime,
+               std::optional<util::TimePoint> atime)
 {
 #ifdef HAVE_UTIMENSAT
   timespec atime_mtime[2];
   if (mtime) {
-    atime_mtime[0] = atime ? *atime : *mtime;
-    atime_mtime[1] = *mtime;
+    atime_mtime[0] = (atime ? *atime : *mtime).to_timespec();
+    atime_mtime[1] = mtime->to_timespec();
   }
   utimensat(AT_FDCWD, path.c_str(), mtime ? atime_mtime : nullptr, 0);
 #elif defined(HAVE_UTIMES)
   timeval atime_mtime[2];
   if (mtime) {
-    atime_mtime[0].tv_sec = atime ? atime->tv_sec : mtime->tv_sec;
-    atime_mtime[0].tv_usec = (atime ? atime->tv_nsec : mtime->tv_nsec) / 1000;
-    atime_mtime[1].tv_sec = mtime->tv_sec;
-    atime_mtime[1].tv_usec = mtime->tv_nsec / 1000;
+    atime_mtime[0].tv_sec = atime ? atime->sec() : mtime->sec();
+    atime_mtime[0].tv_usec =
+      (atime ? atime->nsec_decimal_part() : mtime->nsec_decimal_part()) / 1000;
+    atime_mtime[1].tv_sec = mtime->sec();
+    atime_mtime[1].tv_usec = mtime->nsec_decimal_part() / 1000;
   }
   utimes(path.c_str(), mtime ? atime_mtime : nullptr);
 #else
   utimbuf atime_mtime;
   if (mtime) {
-    atime_mtime.actime = atime ? atime->tv_sec : mtime->tv_sec;
-    atime_mtime.modtime = mtime->tv_sec;
+    atime_mtime.actime = atime ? atime->sec() : mtime->sec();
+    atime_mtime.modtime = mtime->sec();
     utime(path.c_str(), &atime_mtime);
   } else {
     utime(path.c_str(), nullptr);
index 7ebdbdbec85b327314e9e393aedabba1f5ae829c..bb0c8b226aec7fb00fde7317b22b50581ef4fcd6 100644 (file)
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <util/TimePoint.hpp>
 #include <util/types.hpp>
 
 #include <third_party/nonstd/expected.hpp>
@@ -67,8 +68,8 @@ read_file_part(const std::string& path, size_t pos, size_t count);
 // Set atime/mtime of `path`. If `mtime` is std::nullopt, set to the current
 // time. If `atime` is std::nullopt, set to what `mtime` specifies.
 void set_timestamps(const std::string& path,
-                    std::optional<timespec> mtime = std::nullopt,
-                    std::optional<timespec> atime = std::nullopt);
+                    std::optional<util::TimePoint> mtime = std::nullopt,
+                    std::optional<util::TimePoint> atime = std::nullopt);
 
 // Write `size` bytes from binary `data` to `fd`.
 nonstd::expected<void, std::string>
index b7e6178aae7fcf3ec319fda19e89678f073cc40a..fbd0afe5c5ea49ed6bf9b31b9e11a2436281a82d 100644 (file)
@@ -154,20 +154,16 @@ TEST_CASE("Default constructor")
   CHECK(stat.device() == 0);
   CHECK(stat.inode() == 0);
   CHECK(stat.mode() == 0);
-  CHECK(stat.ctime() == 0);
-  CHECK(stat.mtime() == 0);
+  CHECK(stat.ctime().sec() == 0);
+  CHECK(stat.ctime().nsec() == 0);
+  CHECK(stat.mtime().sec() == 0);
+  CHECK(stat.mtime().nsec() == 0);
   CHECK(stat.size() == 0);
   CHECK(stat.size_on_disk() == 0);
   CHECK(!stat.is_directory());
   CHECK(!stat.is_regular());
   CHECK(!stat.is_symlink());
 
-  CHECK(stat.ctim().tv_sec == 0);
-  CHECK(stat.ctim().tv_nsec == 0);
-
-  CHECK(stat.mtim().tv_sec == 0);
-  CHECK(stat.mtim().tv_nsec == 0);
-
 #ifdef _WIN32
   CHECK(stat.file_attributes() == 0);
   CHECK(stat.reparse_tag() == 0);
@@ -210,20 +206,16 @@ TEST_CASE("Return values when file is missing")
   CHECK(stat.device() == 0);
   CHECK(stat.inode() == 0);
   CHECK(stat.mode() == 0);
-  CHECK(stat.ctime() == 0);
-  CHECK(stat.mtime() == 0);
+  CHECK(stat.ctime().sec() == 0);
+  CHECK(stat.ctime().nsec() == 0);
+  CHECK(stat.mtime().sec() == 0);
+  CHECK(stat.mtime().nsec() == 0);
   CHECK(stat.size() == 0);
   CHECK(stat.size_on_disk() == 0);
   CHECK(!stat.is_directory());
   CHECK(!stat.is_regular());
   CHECK(!stat.is_symlink());
 
-  CHECK(stat.ctim().tv_sec == 0);
-  CHECK(stat.ctim().tv_nsec == 0);
-
-  CHECK(stat.mtim().tv_sec == 0);
-  CHECK(stat.mtim().tv_nsec == 0);
-
 #ifdef _WIN32
   CHECK(stat.file_attributes() == 0);
   CHECK(stat.reparse_tag() == 0);
@@ -259,13 +251,10 @@ TEST_CASE("Return values when file exists")
   struct timespec last_write_time =
     win32_filetime_to_timespec(info.ftLastWriteTime);
 
-  CHECK(stat.ctime() == creation_time.tv_sec);
-  CHECK(stat.mtime() == last_write_time.tv_sec);
-
-  CHECK(stat.ctim().tv_sec == creation_time.tv_sec);
-  CHECK(stat.ctim().tv_nsec == creation_time.tv_nsec);
-  CHECK(stat.mtim().tv_sec == last_write_time.tv_sec);
-  CHECK(stat.mtim().tv_nsec == last_write_time.tv_nsec);
+  CHECK(stat.ctime().sec() == creation_time.tv_sec);
+  CHECK(stat.ctime().nsec_decimal_part() == creation_time.tv_nsec);
+  CHECK(stat.mtime().sec() == last_write_time.tv_sec);
+  CHECK(stat.mtime().nsec_decimal_part() == last_write_time.tv_nsec);
 
   CHECK(stat.size_on_disk() == ((stat.size() + 1023) & ~1023));
   CHECK(stat.file_attributes() == info.dwFileAttributes);
@@ -278,30 +267,28 @@ TEST_CASE("Return values when file exists")
   CHECK(stat.device() == st.st_dev);
   CHECK(stat.inode() == st.st_ino);
   CHECK(stat.mode() == st.st_mode);
-  CHECK(stat.ctime() == st.st_ctime);
-  CHECK(stat.mtime() == st.st_mtime);
   CHECK(stat.size_on_disk() == st.st_blocks * 512);
 
 #  ifdef HAVE_STRUCT_STAT_ST_CTIM
-  CHECK(stat.ctim().tv_sec == st.st_ctim.tv_sec);
-  CHECK(stat.ctim().tv_nsec == st.st_ctim.tv_nsec);
+  CHECK(stat.ctime().sec() == st.st_ctim.tv_sec);
+  CHECK(stat.ctime().nsec_decimal_part() == st.st_ctim.tv_nsec);
 #  elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
-  CHECK(stat.ctim().tv_sec == st.st_ctimespec.tv_sec);
-  CHECK(stat.ctim().tv_nsec == st.st_ctimespec.tv_nsec);
+  CHECK(stat.ctime().sec() == st.st_ctimespec.tv_sec);
+  CHECK(stat.ctime().nsec_decimal_part() == st.st_ctimespec.tv_nsec);
 #  else
-  CHECK(stat.ctim().tv_sec == st.st_ctime);
-  CHECK(stat.ctim().tv_nsec == 0);
+  CHECK(stat.ctime().sec() == st.st_ctime);
+  CHECK(stat.ctime().nsec_decimal_part() == 0);
 #  endif
 
 #  ifdef HAVE_STRUCT_STAT_ST_MTIM
-  CHECK(stat.mtim().tv_sec == st.st_mtim.tv_sec);
-  CHECK(stat.mtim().tv_nsec == st.st_mtim.tv_nsec);
+  CHECK(stat.mtime().sec() == st.st_mtim.tv_sec);
+  CHECK(stat.mtime().nsec_decimal_part() == st.st_mtim.tv_nsec);
 #  elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
-  CHECK(stat.mtim().tv_sec == st.st_mtimespec.tv_sec);
-  CHECK(stat.mtim().tv_nsec == st.st_mtimespec.tv_nsec);
+  CHECK(stat.mtime().sec() == st.st_mtimespec.tv_sec);
+  CHECK(stat.mtime().nsec_decimal_part() == st.st_mtimespec.tv_nsec);
 #  else
-  CHECK(stat.mtim().tv_sec == st.st_mtime);
-  CHECK(stat.mtim().tv_nsec == 0);
+  CHECK(stat.mtime().sec() == st.st_mtime);
+  CHECK(stat.mtime().nsec_decimal_part() == 0);
 #  endif
 #endif
 }
index d16d713511263dc4b3eb1850c3221ca9dbc4df00..b71dfdb33cc481cbb9c83a18e65d284e2104cd01 100644 (file)
@@ -38,7 +38,6 @@
 
 #include <algorithm>
 
-using doctest::Approx;
 using TestUtil::TestContext;
 
 TEST_SUITE_BEGIN("Util");
index f8fe05db1bcd463d741437f3d43f6d8b4d4b90e8..ff3153c66512f3268c6898288eb872300f19208f 100644 (file)
@@ -136,7 +136,7 @@ TEST_CASE("Break stale lock, blocking")
   TestContext test_context;
 
   util::write_file("test.alive", "");
-  const timespec long_time_ago{0, 0};
+  const util::TimePoint long_time_ago(0, 0);
   util::set_timestamps("test.alive", long_time_ago);
   CHECK(symlink("foo", "test.lock") == 0);
 
@@ -150,7 +150,7 @@ TEST_CASE("Break stale lock, non-blocking")
   TestContext test_context;
 
   util::write_file("test.alive", "");
-  const timespec long_time_ago{0, 0};
+  const util::TimePoint long_time_ago(0, 0);
   util::set_timestamps("test.alive", long_time_ago);
   CHECK(symlink("foo", "test.lock") == 0);