From aa5a8fe28a224fd581b6053e4a5ce38f3e1a9694 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 13 May 2021 10:00:41 +0200 Subject: [PATCH] Codechange: use thread safe time functions Functions like localtime, gmtime and asctime are not thread safe as they (might) reuse the same buffer. So use the safer _s/_r variant for localtime and gmtime, and use strftime in favour of asctime. --- src/CMakeLists.txt | 1 + src/console_cmds.cpp | 9 ++--- src/crashlog.cpp | 6 +-- src/debug.cpp | 5 +-- src/newgrf_profiling.cpp | 6 +-- src/stdafx.h | 3 ++ src/walltime_func.h | 80 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 src/walltime_func.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d4fffc8c794..801a92cb37a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -467,6 +467,7 @@ add_files( viewport_type.h void_cmd.cpp void_map.h + walltime_func.h water.h water_cmd.cpp water_map.h diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index a417831cbfcb..39a52f0b5e10 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -41,7 +41,7 @@ #include "rail.h" #include "game/game.hpp" #include "table/strings.h" -#include +#include "walltime_func.h" #include "safeguards.h" @@ -1369,10 +1369,9 @@ DEF_CONSOLE_CMD(ConGetSysDate) return true; } - time_t t; - time(&t); - auto timeinfo = localtime(&t); - IConsolePrintF(CC_DEFAULT, "System Date: %04d-%02d-%02d %02d:%02d:%02d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); + char buffer[lengthof("2000-01-02 03:04:05")]; + LocalTime::Format(buffer, lastof(buffer), "%Y-%m-%d %H:%M:%S"); + IConsolePrintF(CC_DEFAULT, "System Date: %s", buffer); return true; } diff --git a/src/crashlog.cpp b/src/crashlog.cpp index c447019fef91..c69dddef8400 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -32,8 +32,7 @@ #include "game/game_info.hpp" #include "company_base.h" #include "company_func.h" - -#include +#include "walltime_func.h" #ifdef WITH_ALLEGRO # include @@ -333,9 +332,8 @@ char *CrashLog::LogRecentNews(char *buffer, const char *last) const */ char *CrashLog::FillCrashLog(char *buffer, const char *last) const { - time_t cur_time = time(nullptr); buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n"); - buffer += seprintf(buffer, last, "Crash at: %s", asctime(gmtime(&cur_time))); + buffer += UTCTime::Format(buffer, last, "Crash at: %Y-%m-%d %H:%M:%S (UTC)\n"); YearMonthDay ymd; ConvertDateToYMD(_date, &ymd); diff --git a/src/debug.cpp b/src/debug.cpp index 25a0f1a82f2d..39c695e0360c 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -19,7 +19,7 @@ #include "os/windows/win32.h" #endif -#include +#include "walltime_func.h" #include "network/network_admin.h" SOCKET _debug_socket = INVALID_SOCKET; @@ -248,8 +248,7 @@ const char *GetLogPrefix() { static char _log_prefix[24]; if (_settings_client.gui.show_date_in_logs) { - time_t cur_time = time(nullptr); - strftime(_log_prefix, sizeof(_log_prefix), "[%Y-%m-%d %H:%M:%S] ", localtime(&cur_time)); + LocalTime::Format(_log_prefix, lastof(_log_prefix), "[%Y-%m-%d %H:%M:%S] "); } else { *_log_prefix = '\0'; } diff --git a/src/newgrf_profiling.cpp b/src/newgrf_profiling.cpp index 8ec8cff54694..27a1bc80ef99 100644 --- a/src/newgrf_profiling.cpp +++ b/src/newgrf_profiling.cpp @@ -13,9 +13,9 @@ #include "string_func.h" #include "console_func.h" #include "spritecache.h" +#include "walltime_func.h" #include -#include std::vector _newgrf_profilers; @@ -130,10 +130,8 @@ void NewGRFProfiler::Abort() */ std::string NewGRFProfiler::GetOutputFilename() const { - time_t write_time = time(nullptr); - char timestamp[16] = {}; - strftime(timestamp, lengthof(timestamp), "%Y%m%d-%H%M", localtime(&write_time)); + LocalTime::Format(timestamp, lastof(timestamp), "%Y%m%d-%H%M"); char filepath[MAX_PATH] = {}; seprintf(filepath, lastof(filepath), "%sgrfprofile-%s-%08X.csv", FiosGetScreenshotDir(), timestamp, BSWAP32(this->grffile->grfid)); diff --git a/src/stdafx.h b/src/stdafx.h index 937c053f2f02..319d94edf23d 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -129,6 +129,7 @@ /* Warn about functions using 'printf' format syntax. First argument determines which parameter * is the format string, second argument is start of values passed to printf. */ # define WARN_FORMAT(string, args) __attribute__ ((format (printf, string, args))) +# define WARN_TIME_FORMAT(string) __attribute__ ((format (strftime, string, 0))) # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) # define FINAL final # else @@ -157,6 +158,7 @@ # define NORETURN # define CDECL # define WARN_FORMAT(string, args) +# define WARN_TIME_FORMAT(string) # define FINAL # define FALLTHROUGH # include @@ -205,6 +207,7 @@ # define CDECL _cdecl # define WARN_FORMAT(string, args) +# define WARN_TIME_FORMAT(string) # define FINAL final /* fallthrough attribute, VS 2017 */ diff --git a/src/walltime_func.h b/src/walltime_func.h new file mode 100644 index 000000000000..219a8907de03 --- /dev/null +++ b/src/walltime_func.h @@ -0,0 +1,80 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + + /** @file walltime_func.h Functionality related to the time of the clock on your wall. */ + +#ifndef WALLTIME_FUNC_H +#define WALLTIME_FUNC_H + +#include + +/** Helper for safely converting a std::time_t to a local time std::tm using localtime_s. */ +struct LocalTimeToStruct { + static inline std::tm ToTimeStruct(std::time_t time_since_epoch) + { + std::tm time = {}; +#ifdef WIN32 + /* Windows has swapped the parameters around for localtime_s. */ + localtime_s(&time, &time_since_epoch); +#else + localtime_r(&time_since_epoch, &time); +#endif + return time; + } +}; + +/** Helper for safely converting a std::time_t to a UTC time std::tm using gmtime_s. */ +struct UTCTimeToStruct { + static inline std::tm ToTimeStruct(std::time_t time_since_epoch) + { + std::tm time = {}; +#ifdef WIN32 + /* Windows has swapped the parameters around for gmtime_s. */ + gmtime_s(&time, &time_since_epoch); +#else + gmtime_r(&time_since_epoch, &time); +#endif + return time; + } +}; + +/** + * Container for wall clock time related functionality not directly provided by C++. + * @tparam T The type of the time-to-struct implementation class. + */ +template +struct Time { + /** + * Format the current time with the given strftime format specifiers. + * @param buffer The buffer to write the time string to. + * @param last The last element in the buffer. + * @param format The format according to strftime format specifiers. + * @return The number of characters that were written to the buffer. + */ + static inline size_t Format(char *buffer, const char *last, const char *format) NOACCESS(2) WARN_TIME_FORMAT(3) + { + std::tm time_struct = T::ToTimeStruct(time(nullptr)); +#ifndef _MSC_VER + /* GCC bug #39438; unlike for printf where the appropriate attribute prevent the + * "format non literal" warning, that does not happen for strftime. Even though + * format warnings will be created for invalid strftime formats. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif /* _MSC_VER */ + return strftime(buffer, last - buffer, format, &time_struct); +#ifndef _MSC_VER +#pragma GCC diagnostic pop +#endif /* _MSC_VER */ + } +}; + +/** Wall clock time functionality using the local time zone. */ +using LocalTime = Time; +/** Wall clock time functionality using the UTC time zone. */ +using UTCTime = Time; + +#endif