Skip to content

Commit

Permalink
Work around broken <thread> in MinGW.
Browse files Browse the repository at this point in the history
Fixes #336, #398, #424, #441

For years now, MinGW has had trouble compiling files which
`#include <thread>`.  The exact errors seem to have evolved over that
time, but MinGW users still can't compile libpqxx without editing the
standard header.

Luckily we don't use this header much yet.  It's just `sleep_for()`.  We
used to have our own function for this, which would use one of a bunch
of platform-specific alternatives.  I basically revived it, except that
`sleep_for` is in `std` and most compilers implement it, so it's
basically MinGW versus everyone else.  And therefore, we really only
need the one alternate implementation.
  • Loading branch information
jtv committed Apr 24, 2021
1 parent a4a1c5b commit f5cf977
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 10 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<thread>` on MinGW; it's broken there. (#336, #398, #424, #441)
7.4.1
- Missing includes; broke macOS clang build. (#416)
7.4.0
Expand Down
4 changes: 4 additions & 0 deletions cmake/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions config-tests/sleep_for.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Test for std::this_thread::sleep_for().
#include <chrono>
#include <thread>

int main()
{
std::this_thread::sleep_for(std::chrono::microseconds{10u});
}
3 changes: 2 additions & 1 deletion configitems
Original file line number Diff line number Diff line change
Expand Up @@ -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
35 changes: 35 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -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 <chrono>

#include <thread>



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.
Expand Down
11 changes: 11 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions include/pqxx/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions include/pqxx/util.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ template<typename T> auto ssize(T const &c)
return static_cast<signed_t>(std::size(c));
#endif // __cpp_lib_ssize
}


/// Wait.
/** This is normally @c std::this_thread::sleep_for(). But MinGW's @c <thread>
* 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"
Expand Down
3 changes: 1 addition & 2 deletions src/robusttransaction.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/strconv.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ template<typename TYPE>
// 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;
Expand Down
42 changes: 39 additions & 3 deletions src/util.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,32 @@
#include <cstring>
#include <new>

extern "C"
{
#if defined(PQXX_HAVE_SLEEP_FOR)
# include <thread>
#endif

// For select() on recent POSIX systems.
#if __has_include(<sys/select.h>)
# include <sys/select.h>
#endif


// For select() on some older POSIX systems.
#if __has_include (<sys / types.h>)
#include <sys/types.h>
#endif
#if __has_include(<unistd.h>)
# include <unistd.h>
#endif
#if __has_include(<sys/time.h>)
# include <sys/time.h>
#endif


extern "C"
{
#include <libpq-fe.h>
}
}

#include "pqxx/except"
#include "pqxx/util"
Expand Down Expand Up @@ -185,3 +207,17 @@ std::string pqxx::internal::unesc_bin(std::string_view escaped_data)
unesc_bin(escaped_data, reinterpret_cast<std::byte *>(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 <thread> 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
}
2 changes: 1 addition & 1 deletion test/test04.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion test/unit/test_notification.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down

0 comments on commit f5cf977

Please sign in to comment.