geteuid
getopt_long
getpwuid
- gettimeofday
posix_fallocate
realpath
setenv
// 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
#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
inode_cache(config)
#endif
{
- gettimeofday(&time_of_invocation, nullptr);
+ time_of_invocation = util::TimePoint::now();
}
void
#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
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;
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;
#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
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()));
}
-// 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>
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)
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);
#pragma once
#include <core/wincompat.hpp>
+#include <util/TimePoint.hpp>
#include <sys/stat.h>
#include <sys/types.h>
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;
uint32_t reparse_tag() const;
#endif
- timespec atim() const;
- timespec ctim() const;
- timespec mtim() const;
-
protected:
using StatFunction = int (*)(const char*, stat_t*);
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
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
-}
#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>
# 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>
}
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(×tamp, &result)) {
return result;
#pragma once
#include <Stat.hpp>
+#include <util/TimePoint.hpp>
#include <util/Tokenizer.hpp>
#include <cstdint>
// 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.
//
} // 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)
{
-// 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.
//
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
#include <optional>
#include <string_view>
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
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)
{
}
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);
}
// 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;
}
&& 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) {
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");
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;
} 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]);
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);
#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>
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)
}
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));
}
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
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)
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>();
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());
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());
#include <Digest.hpp>
#include <core/Serializer.hpp>
+#include <util/TimePoint.hpp>
#include <third_party/nonstd/span.hpp>
struct FileStats
{
uint64_t size;
- int64_t mtime;
- int64_t ctime;
+ util::TimePoint mtime;
+ util::TimePoint ctime;
};
using FileStater = std::function<FileStats(std::string)>;
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;
};
== 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);
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
{
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)});
}
}
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)) {
#pragma once
#include <core/StatisticsCounters.hpp>
+#include <util/TimePoint.hpp>
#include <cstdint>
#include <ctime>
// 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;
});
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;
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));
}
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,
#include <core/wincompat.hpp>
#include <fmtmacros.hpp>
#include <storage/primary/StatsFile.hpp>
+#include <util/Duration.hpp>
#include <util/file.hpp>
#ifdef HAVE_UNISTD_H
// 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
{
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
#include <storage/primary/util.hpp>
#include <storage/types.hpp>
#include <util/Bytes.hpp>
+#include <util/TimePoint.hpp>
#include <third_party/nonstd/span.hpp>
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 ---
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;
}
// 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;
// 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",
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;
}
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;
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) {
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(
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(
Bytes.cpp
LockFile.cpp
TextTable.cpp
+ TimePoint.cpp
Tokenizer.cpp
file.cpp
path.cpp
#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 {
#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)
{
<< 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;
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.
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;
}
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;
}
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;
}
#pragma once
#include <NonCopyable.hpp>
+#include <util/TimePoint.hpp>
#include <condition_variable>
#include <cstdint>
#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
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
};
return true;
}
-inline std::optional<double>
+inline std::optional<util::TimePoint>
LockFile::get_last_lock_update()
{
return std::nullopt;
# endif
#endif
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
#include <cerrno>
#include <codecvt>
#include <cstring>
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);
#pragma once
+#include <util/TimePoint.hpp>
#include <util/types.hpp>
#include <third_party/nonstd/expected.hpp>
// 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>
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);
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);
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);
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
}
#include <algorithm>
-using doctest::Approx;
using TestUtil::TestContext;
TEST_SUITE_BEGIN("Util");
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);
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);