Skip to content

Commit 2462d28

Browse files
committed
localtime_thread_safe, PYBIND11_COMPAT_STRDUP
1 parent e052ff0 commit 2462d28

File tree

3 files changed

+36
-29
lines changed

3 files changed

+36
-29
lines changed

include/pybind11/chrono.h

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
#pragma once
1212

1313
#include "pybind11.h"
14-
#include <cmath>
15-
#include <ctime>
14+
1615
#include <chrono>
16+
#include <cmath>
17+
#include <mutex>
18+
19+
#include <time.h>
20+
1721
#include <datetime.h>
1822

1923
// Backport the PyDateTime_DELTA functions from Python3.3 if required
@@ -95,6 +99,22 @@ template <typename type> class duration_caster {
9599
PYBIND11_TYPE_CASTER(type, _("datetime.timedelta"));
96100
};
97101

102+
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
103+
#if defined(__STDC_WANT_LIB_EXT1__) || defined(_MSC_VER)
104+
if (localtime_s(buf, time))
105+
return nullptr;
106+
return buf;
107+
#else
108+
static std::mutex mtx;
109+
std::lock_guard<std::mutex> lock(mtx);
110+
std::tm *tm_ptr = localtime(time);
111+
if (tm_ptr != nullptr) {
112+
*buf = *tm_ptr;
113+
}
114+
return tm_ptr;
115+
#endif
116+
}
117+
98118
// This is for casting times on the system clock into datetime.datetime instances
99119
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
100120
public:
@@ -162,23 +182,10 @@ template <typename Duration> class type_caster<std::chrono::time_point<std::chro
162182
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
163183
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
164184

165-
// std::localtime returns a pointer to a static internal std::tm object on success,
166-
// or null pointer otherwise
167-
#if defined(_MSC_VER)
168-
# pragma warning(push)
169-
# pragma warning(disable : 4996) // warning C4996: The POSIX name for this item is deprecated.
170-
#endif
171-
std::tm *localtime_ptr = std::localtime(&tt);
172-
#if defined(_MSC_VER)
173-
# pragma warning(pop)
174-
#endif
185+
std::tm localtime;
186+
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
175187
if (!localtime_ptr)
176188
throw cast_error("Unable to represent system_clock in local time");
177-
178-
// this function uses static memory so it's best to copy it out asap just in case
179-
// otherwise other code that is using localtime may break this (not just python code)
180-
std::tm localtime = *localtime_ptr;
181-
182189
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
183190
localtime.tm_mon + 1,
184191
localtime.tm_mday,

include/pybind11/detail/common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@
125125
# endif
126126
#endif
127127

128+
// https://en.cppreference.com/w/c/chrono/localtime
129+
#if defined(__STDC_LIB_EXT1__) && !defined(__STDC_WANT_LIB_EXT1__)
130+
# define __STDC_WANT_LIB_EXT1__
131+
#endif
132+
128133
#include <Python.h>
129134
#include <frameobject.h>
130135
#include <pythread.h>

include/pybind11/pybind11.h

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,9 @@ inline bool apply_exception_translators(std::forward_list<ExceptionTranslator>&
7878
}
7979

8080
#if defined(_MSC_VER)
81-
# pragma warning(push)
82-
# pragma warning(disable : 4996) // warning C4996: The POSIX name for this item is deprecated.
83-
#endif
84-
85-
inline char *strdup_helper(const char *s) { return strdup(s); }
86-
87-
#if defined(_MSC_VER)
88-
# pragma warning(pop)
81+
# define PYBIND11_COMPAT_STRDUP _strdup
82+
#else
83+
# define PYBIND11_COMPAT_STRDUP strdup
8984
#endif
9085

9186
PYBIND11_NAMESPACE_END(detail)
@@ -287,7 +282,7 @@ class cpp_function : public function {
287282
std::free(s);
288283
}
289284
char *operator()(const char *s) {
290-
auto t = detail::strdup_helper(s);
285+
auto t = PYBIND11_COMPAT_STRDUP(s);
291286
strings.push_back(t);
292287
return t;
293288
}
@@ -532,7 +527,7 @@ class cpp_function : public function {
532527
std::free(const_cast<char *>(func->m_ml->ml_doc));
533528
// Install docstring if it's non-empty (when at least one option is enabled)
534529
func->m_ml->ml_doc
535-
= signatures.empty() ? nullptr : detail::strdup_helper(signatures.c_str());
530+
= signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str());
536531

537532
if (rec->is_method) {
538533
m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr());
@@ -1537,15 +1532,15 @@ class class_ : public detail::generic_type {
15371532
detail::process_attributes<Extra...>::init(extra..., rec_fget);
15381533
if (rec_fget->doc && rec_fget->doc != doc_prev) {
15391534
free(doc_prev);
1540-
rec_fget->doc = detail::strdup_helper(rec_fget->doc);
1535+
rec_fget->doc = PYBIND11_COMPAT_STRDUP(rec_fget->doc);
15411536
}
15421537
}
15431538
if (rec_fset) {
15441539
char *doc_prev = rec_fset->doc;
15451540
detail::process_attributes<Extra...>::init(extra..., rec_fset);
15461541
if (rec_fset->doc && rec_fset->doc != doc_prev) {
15471542
free(doc_prev);
1548-
rec_fset->doc = detail::strdup_helper(rec_fset->doc);
1543+
rec_fset->doc = PYBIND11_COMPAT_STRDUP(rec_fset->doc);
15491544
}
15501545
if (! rec_active) rec_active = rec_fset;
15511546
}

0 commit comments

Comments
 (0)