Skip to content

Commit

Permalink
Codechange: use thread safe time functions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rubidium42 committed May 14, 2021
1 parent 297d6e2 commit aa5a8fe
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Expand Up @@ -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
Expand Down
9 changes: 4 additions & 5 deletions src/console_cmds.cpp
Expand Up @@ -41,7 +41,7 @@
#include "rail.h"
#include "game/game.hpp"
#include "table/strings.h"
#include <time.h>
#include "walltime_func.h"

#include "safeguards.h"

Expand Down Expand Up @@ -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;
}

Expand Down
6 changes: 2 additions & 4 deletions src/crashlog.cpp
Expand Up @@ -32,8 +32,7 @@
#include "game/game_info.hpp"
#include "company_base.h"
#include "company_func.h"

#include <time.h>
#include "walltime_func.h"

#ifdef WITH_ALLEGRO
# include <allegro.h>
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 2 additions & 3 deletions src/debug.cpp
Expand Up @@ -19,7 +19,7 @@
#include "os/windows/win32.h"
#endif

#include <time.h>
#include "walltime_func.h"

#include "network/network_admin.h"
SOCKET _debug_socket = INVALID_SOCKET;
Expand Down Expand Up @@ -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';
}
Expand Down
6 changes: 2 additions & 4 deletions src/newgrf_profiling.cpp
Expand Up @@ -13,9 +13,9 @@
#include "string_func.h"
#include "console_func.h"
#include "spritecache.h"
#include "walltime_func.h"

#include <chrono>
#include <time.h>


std::vector<NewGRFProfiler> _newgrf_profilers;
Expand Down Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions src/stdafx.h
Expand Up @@ -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
Expand Down Expand Up @@ -157,6 +158,7 @@
# define NORETURN
# define CDECL
# define WARN_FORMAT(string, args)
# define WARN_TIME_FORMAT(string)
# define FINAL
# define FALLTHROUGH
# include <malloc.h>
Expand Down Expand Up @@ -205,6 +207,7 @@

# define CDECL _cdecl
# define WARN_FORMAT(string, args)
# define WARN_TIME_FORMAT(string)
# define FINAL final

/* fallthrough attribute, VS 2017 */
Expand Down
80 changes: 80 additions & 0 deletions 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 <http://www.gnu.org/licenses/>.
*/

/** @file walltime_func.h Functionality related to the time of the clock on your wall. */

#ifndef WALLTIME_FUNC_H
#define WALLTIME_FUNC_H

#include <ctime>

/** 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 <typename T>
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<LocalTimeToStruct>;
/** Wall clock time functionality using the UTC time zone. */
using UTCTime = Time<UTCTimeToStruct>;

#endif

0 comments on commit aa5a8fe

Please sign in to comment.