Merge pull request #18 from ToruNiina/threadsafe-localtime

add threadsafe localtime_(s|r)
This commit is contained in:
Toru Niina
2019-02-16 23:28:48 +09:00
committed by GitHub

View File

@@ -14,6 +14,36 @@
namespace toml namespace toml
{ {
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
// provided in the absolutely same purpose, but C++11 is actually not compatible
// with C11. We need to dispatch the function depending on the OS.
namespace detail
{
// TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if(!result)
{
throw std::runtime_error("localtime_r failed.");
}
return dst;
}
#else
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
// storage to store the instance of std::tm. On the other platforms, it may not
// be thread-safe.
inline std::tm localtime_s(const std::time_t* src)
{
const auto result = std::localtime(src);
if(!result) {throw std::runtime_error("localtime failed.");}
return *result;
}
#endif
} // detail
enum class month_t : std::int8_t enum class month_t : std::int8_t
{ {
Jan = 0, Jan = 0,
@@ -50,10 +80,8 @@ struct local_date
explicit local_date(const std::chrono::system_clock::time_point& tp) explicit local_date(const std::chrono::system_clock::time_point& tp)
{ {
const auto t = std::chrono::system_clock::to_time_t(tp); const auto t = std::chrono::system_clock::to_time_t(tp);
const auto tmp = std::localtime(&t); //XXX: not threadsafe! const auto time = detail::localtime_s(&t);
assert(tmp); // if std::localtime fails, tmp is nullptr
const std::tm time = *tmp;
*this = local_date(time); *this = local_date(time);
} }
@@ -330,10 +358,8 @@ struct local_datetime
explicit local_datetime(const std::chrono::system_clock::time_point& tp) explicit local_datetime(const std::chrono::system_clock::time_point& tp)
{ {
const auto t = std::chrono::system_clock::to_time_t(tp); const auto t = std::chrono::system_clock::to_time_t(tp);
const auto tmp = std::localtime(&t); //XXX: not threadsafe! std::tm time = detail::localtime_s(&t);
assert(tmp); // if std::localtime fails, tmp is nullptr
std::tm time = *tmp;
this->date = local_date(time); this->date = local_date(time);
this->time = local_time(time); this->time = local_time(time);
@@ -482,10 +508,8 @@ struct offset_datetime
static time_offset get_local_offset() static time_offset get_local_offset()
{ {
// get current timezone // get current timezone
const auto tmp1 = std::time(nullptr); const auto tmp1 = std::time(nullptr);
const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe! const auto t = detail::localtime_s(&tmp1);
assert(tmp2);
std::tm t = *tmp2;
std::array<char, 6> buf; std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0