diff --git a/NEWS b/NEWS index a82290c2c..303505258 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ - Dropped legacy tests 31 and 49 to work around clang C++20 bug. - Fixed `stream_to` test failure in non-English locales. (#440) - Clarify `transaction_base::stream` documentation. (#423) + - Avoid `` on MinGW; it's broken there. (#336, #398, #424, #441) 7.4.1 - Missing includes; broke macOS clang build. (#416) 7.4.0 diff --git a/cmake/config.cmake b/cmake/config.cmake index c9e719d0e..7d0c15dfa 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -82,6 +82,10 @@ try_compile( PQXX_HAVE_THREAD_LOCAL ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/thread_local.cxx) +try_compile( + PQXX_HAVE_SLEEP_FOR + ${PROJECT_BINARY_DIR} + SOURCES ${PROJECT_SOURCE_DIR}/config-tests/sleep_for.cxx) # check_cxx_source_compiles requires CMAKE_REQUIRED_DEFINITIONS to specify # compiling arguments. diff --git a/config-tests/sleep_for.cxx b/config-tests/sleep_for.cxx new file mode 100644 index 000000000..6a8130443 --- /dev/null +++ b/config-tests/sleep_for.cxx @@ -0,0 +1,8 @@ +// Test for std::this_thread::sleep_for(). +#include +#include + +int main() +{ + std::this_thread::sleep_for(std::chrono::microseconds{10u}); +} diff --git a/configitems b/configitems index f3b0c6504..5407bf212 100644 --- a/configitems +++ b/configitems @@ -12,6 +12,7 @@ PQXX_HAVE_GCC_PURE public compiler PQXX_HAVE_GCC_VISIBILITY public compiler PQXX_HAVE_POLL internal compiler PQXX_HAVE_PQENCRYPTPASSWORDCONN internal libpq +PQXX_HAVE_SLEEP_FOR internal compiler PQXX_HAVE_SPAN public compiler -PQXX_HAVE_THREAD_LOCAL private compiler +PQXX_HAVE_THREAD_LOCAL internal compiler VERSION internal autotools diff --git a/configure b/configure index 7f2f1cb1a..ed953f3cd 100755 --- a/configure +++ b/configure @@ -17889,6 +17889,41 @@ rm -f core conftest.err conftest.$ac_objext \ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_thread_local" >&5 $as_echo "$have_thread_local" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::this_thread::sleep_for" >&5 +$as_echo_n "checking for std::this_thread::sleep_for... " >&6; } +have_sleep_for=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +// Test for std::this_thread::sleep_for(). + +#include + +#include + + + +int main() + +{ + + std::this_thread::sleep_for(std::chrono::microseconds{10u}); + +} + + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +$as_echo "#define PQXX_HAVE_SLEEP_FOR 1" >>confdefs.h + +else + have_sleep_for=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_sleep_for" >&5 +$as_echo "$have_sleep_for" >&6; } + # Doing my own test for poll() for now. The test built into autoconf-archive # triggers stupid warnings. # TODO: Try newer version of autoconf-archive. diff --git a/configure.ac b/configure.ac index cce567400..d87aecc78 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,17 @@ AC_LINK_IFELSE( have_thread_local=no) AC_MSG_RESULT($have_thread_local) +AC_MSG_CHECKING([for std::this_thread::sleep_for]) +have_sleep_for=yes +AC_LINK_IFELSE( + [read_test(sleep_for.cxx)], + AC_DEFINE( + [PQXX_HAVE_SLEEP_FOR], + 1, + [Define if std::this_thread::sleep_for works.]), + have_sleep_for=no) +AC_MSG_RESULT($have_sleep_for) + # Doing my own test for poll() for now. The test built into autoconf-archive # triggers stupid warnings. # TODO: Try newer version of autoconf-archive. diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index 39cf85cf5..94fbf5b9f 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -84,6 +84,9 @@ /* Define if libpq has PQencryptPasswordConn (since pg 10). */ #undef PQXX_HAVE_PQENCRYPTPASSWORDCONN +/* Define if std::this_thread::sleep_for works. */ +#undef PQXX_HAVE_SLEEP_FOR + /* Define if compiler has std::span */ #undef PQXX_HAVE_SPAN diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 6770bc6cc..f51e70852 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -318,6 +318,13 @@ template auto ssize(T const &c) return static_cast(std::size(c)); #endif // __cpp_lib_ssize } + + +/// Wait. +/** This is normally @c std::this_thread::sleep_for(). But MinGW's @c + * doesn't work, so we must be careful about including it. + */ +void PQXX_LIBEXPORT wait_for(unsigned int microseconds); } // namespace pqxx::internal #include "pqxx/internal/compiler-internal-post.hxx" diff --git a/src/robusttransaction.cxx b/src/robusttransaction.cxx index 6685d3f21..b7e8289b3 100644 --- a/src/robusttransaction.cxx +++ b/src/robusttransaction.cxx @@ -167,13 +167,12 @@ void pqxx::internal::basic_robusttransaction::do_commit() // If we get here, we're in doubt. Figure out what happened. - auto const delay{std::chrono::milliseconds(300)}; int const max_attempts{500}; static_assert(max_attempts > 0); tx_stat stat; for (int attempts{0}; attempts < max_attempts; - ++attempts, std::this_thread::sleep_for(delay)) + ++attempts, pqxx::internal::wait_for(300u)) { stat = tx_unknown; try diff --git a/src/strconv.cxx b/src/strconv.cxx index dc68b1c01..d6f9b10e5 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -270,8 +270,8 @@ template // Skip whitespace. This is not the proper way to do it, but I see no way // that any of the supported encodings could ever produce a valid character // whose byte sequence would confuse this code. - for (here = in.data(); - here < end and (*here == ' ' or *here == '\t'); ++here) + for (here = in.data(); here < end and (*here == ' ' or *here == '\t'); + ++here) ; TYPE out; diff --git a/src/util.cxx b/src/util.cxx index d2fbc1076..f3c6ebe4e 100644 --- a/src/util.cxx +++ b/src/util.cxx @@ -14,10 +14,32 @@ #include #include -extern "C" -{ +#if defined(PQXX_HAVE_SLEEP_FOR) +# include +#endif + +// For select() on recent POSIX systems. +#if __has_include() +# include +#endif + + +// For select() on some older POSIX systems. +#if __has_include () +#include +#endif +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif + + + extern "C" + { #include -} + } #include "pqxx/except" #include "pqxx/util" @@ -185,3 +207,17 @@ std::string pqxx::internal::unesc_bin(std::string_view escaped_data) unesc_bin(escaped_data, reinterpret_cast(buf.data())); return buf; } + + +void pqxx::internal::wait_for(unsigned int microseconds) +{ +#if defined(PQXX_HAVE_SLEEP_FOR) + std::this_thread::sleep_for(std::chrono::microseconds{microseconds}); +#else + // MinGW still does not have a functioning header. Work around this + // using select(). + // Not worth optimising for though -- they'll have to fix it at some point. + timeval tv{microseconds / 1'000'000u, microseconds % 1'000'000u}; + select(0, nullptr, nullptr, nullptr, &tv); +#endif +} diff --git a/test/test04.cxx b/test/test04.cxx index 98c950071..98e5acf76 100644 --- a/test/test04.cxx +++ b/test/test04.cxx @@ -59,7 +59,7 @@ void test_004() PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); // Sleep for one second. I'm not proud of this, but how does one inject // a change to the built-in clock in a static language? - std::this_thread::sleep_for(std::chrono::seconds(1)); + pqxx::internal::wait_for(1'000'000u); notifs = conn.get_notifs(); } diff --git a/test/unit/test_notification.cxx b/test/unit/test_notification.cxx index 1a446e96a..b8ecd8cfd 100644 --- a/test/unit/test_notification.cxx +++ b/test/unit/test_notification.cxx @@ -51,7 +51,7 @@ void test_receive( int notifs{0}; for (int i{0}; (i < 10) and (notifs == 0); - ++i, std::this_thread::sleep_for(std::chrono::seconds(i))) + ++i, pqxx::internal::wait_for(1'000'000u)) notifs = conn.get_notifs(); PQXX_CHECK_EQUAL(notifs, 1, "Got wrong number of notifications.");