Skip to content

Commit

Permalink
Support nonblocking connection.
Browse files Browse the repository at this point in the history
See jtv#487.  We used to have a `connection` class _hierarchy,_ and one of
those did something similar.  But this new class is much more limited in
scope and keeps things simple.

In the future hopefully this can form a basis for C++20 async support.
But for now, any application with a wait loop based on `poll()` or
`select()` should be able to use it.
  • Loading branch information
jtv committed Sep 30, 2021
1 parent 4298a0c commit b4b8935
Show file tree
Hide file tree
Showing 23 changed files with 230 additions and 109 deletions.
9 changes: 3 additions & 6 deletions Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down Expand Up @@ -346,7 +346,6 @@ am__set_TESTS_bases = \
bases='$(TEST_LOGS)'; \
bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
bases=`echo $$bases`
AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
RECHECK_LOGS = $(TEST_LOGS)
TEST_SUITE_LOG = test-suite.log
TEST_EXTENSIONS = @EXEEXT@ .test
Expand Down Expand Up @@ -416,8 +415,6 @@ am__relativize = \
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
DIST_TARGETS = dist-gzip
# Exists only to be overridden by the user if desired.
AM_DISTCHECK_DVI_TARGET = dvi
distuninstallcheck_listfiles = find . -type f -print
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
Expand Down Expand Up @@ -848,7 +845,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
fi; \
echo "$${col}$$br$${std}"; \
echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
echo "$${col}$$br$${std}"; \
create_testsuite_report --maybe-color; \
echo "$$col$$br$$std"; \
Expand Down Expand Up @@ -1053,7 +1050,7 @@ distcheck: dist
$(DISTCHECK_CONFIGURE_FLAGS) \
--srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
Expand Down
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Fix warnings about `[[likely]]` in `if constexpr`. (#475)
- Clearer error for ambiguous string conversion of `char` type. (#481)
- Pseudo-statement in `prepare()` error was for the wrong statement. (#488)
- New class, `connecting` for nonblocking connection to the database. (#487)
7.6.0
- Removed bad string conversion to `std::basic_string_view<std::byte>`. (#463)
- Add C++20 concepts: `binary`, `char_string`, `char_strings`.
Expand Down
13 changes: 9 additions & 4 deletions aclocal.m4
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
# generated automatically by aclocal 1.16.2 -*- Autoconf -*-

# Copyright (C) 1996-2020 Free Software Foundation, Inc.

Expand Down Expand Up @@ -1020,7 +1020,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.16.3], [],
m4_if([$1], [1.16.2], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])

Expand All @@ -1036,7 +1036,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.16.3])dnl
[AM_AUTOMAKE_VERSION([1.16.2])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
Expand Down Expand Up @@ -1724,7 +1724,12 @@ AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
Expand Down
3 changes: 1 addition & 2 deletions config-tests/concepts.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
#include <vector>


template<typename T>
concept Foo = std::ranges::input_range<T>;
template<typename T> concept Foo = std::ranges::input_range<T>;


template<Foo F> auto foo(F const &r)
Expand Down
2 changes: 1 addition & 1 deletion config/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down
4 changes: 2 additions & 2 deletions config/ltmain.sh
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

PROGRAM=libtool
PACKAGE=libtool
VERSION="2.4.6 Debian-2.4.6-15"
VERSION="2.4.6 Debian-2.4.6-14"
package_revision=2.4.6


Expand Down Expand Up @@ -2141,7 +2141,7 @@ include the following information:
compiler: $LTCC
compiler flags: $LTCFLAGS
linker: $LD (gnu? $with_gnu_ld)
version: $progname $scriptversion Debian-2.4.6-15
version: $progname $scriptversion Debian-2.4.6-14
automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
Expand Down
4 changes: 2 additions & 2 deletions config/m4/libtool.m4
Original file line number Diff line number Diff line change
Expand Up @@ -1071,11 +1071,11 @@ _LT_EOF
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[[912]]*)
10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
10.[[012]][[,.]]*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*|11.*)
10.*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
Expand Down
11 changes: 8 additions & 3 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -3978,7 +3978,12 @@ program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
am_aux_dir=`cd "$ac_aux_dir" && pwd`

if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
Expand Down Expand Up @@ -9026,11 +9031,11 @@ $as_echo "$lt_cv_ld_force_load" >&6; }
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[912]*)
10.0,*86*-darwin8*|10.0,*-darwin[91]*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
10.[012][,.]*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*|11.*)
10.*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
Expand Down
2 changes: 1 addition & 1 deletion doc/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down
2 changes: 1 addition & 1 deletion include/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down
2 changes: 1 addition & 1 deletion include/pqxx/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down
101 changes: 90 additions & 11 deletions include/pqxx/connection.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,14 @@ concept ZKey_ZValues = std::ranges::input_range<T> and requires(T t)
{std::cbegin(t)};
{
std::get<0>(*std::cbegin(t))
} -> ZString;
}
->ZString;
{
std::get<1>(*std::cbegin(t))
} -> ZString;
} and std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type>
== 2;
}
->ZString;
}
and std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type> == 2;
#endif // PQXX_HAVE_CONCEPTS
} // namespace pqxx::internal

Expand Down Expand Up @@ -883,15 +885,27 @@ public:
void close();

private:
friend class connecting;
enum connect_mode
{
connect_nonblocking
};
connection(connect_mode, zview connection_string);

/// Poll for ongoing connection, try to progress towards completion.
/** Returns a pair of "now please wait to read data from socket" and "now
* please wait to write data to socket." Both will be false when done.
*
* Throws an exception if polling indicates that the connection has failed.
*/
std::pair<bool, bool> poll_connect();

// Initialise based on connection string.
void init(char const options[]);
// Initialise based on parameter names and values.
void init(char const *params[], char const *values[]);
void complete_init();

void wait_read() const;
void wait_read(std::time_t seconds, long microseconds) const;

result make_result(
internal::pq::PGresult *pgr, std::shared_ptr<std::string> const &query,
std::string_view desc = ""sv);
Expand Down Expand Up @@ -985,6 +999,71 @@ private:
using connection_base = connection;


/// An ongoing, non-blocking stepping stone to a connection.
/** Use this when you want to create a connection to the database, but without
* blocking your whole thread.
*
* Connecting in this way is probably not "faster" (it's more complicated and
* has some extra overhead), but in some situations you can use it to make your
* application as a whole faster. It all depends on having other useful work
* to do in the same thread, and being able to wait on a socket. If you have
* other I/O going on at the same time, your event loop can wait for both the
* libpqxx socket and your own sockets, and wake up whenever any of them is
* ready to do work.
*
* Connecting in this way is not properly "asynchronous;" it's merely
* "nonblocking." This means it's not a super-high-performance mechanism like
* you might get with e.g. @c io_uring. In particular, if we need to look up
* the database hostname in DNS, that will happen synchronously.
*
* To use this, create the @c connecting object, passing a connection string.
* Then loop: If @c wait_to_read() returns true, wait for the socket to have
* incoming data on it. If @c wait_to_write() returns true, wait wait for the
* socket to be ready for writing. Repeat until @c done() returns true (or
* there is an exception). Finally, call @c produce() to get the completed
* connection. This will work only once on the @c connecting object.
*/
class PQXX_LIBEXPORT connecting
{
public:
/// Start connecting.
connecting(zview connection_string) :
m_conn{connection::connect_nonblocking, connection_string}
{}
connecting() : m_conn{connection::connect_nonblocking, ""_zv} {}

/// Get the socket. This may change during the process.
int sock() const noexcept { return m_conn.sock(); }

/// Should we currently wait to @i read from the socket?
bool wait_to_read() const noexcept { return m_reading; }

/// Should we currently wait to @i write to the socket?
bool wait_to_write() const noexcept { return m_writing; }

/// Progress towards completion but don't block.
void process()
{
auto const [reading, writing]{m_conn.poll_connect()};
m_reading = reading;
m_writing = writing;
}

/// Is our connection finished?
bool done() const noexcept { return not m_reading and not m_writing; }

/// Produce the completed connection object.
/** Use this only once, after @c done() returned @c true.
*/
connection produce();

private:
connection m_conn;
bool m_reading{false};
bool m_writing{true};
};


template<typename T> inline std::string connection::quote(T const &t) const
{
if constexpr (nullness<T>::always_null)
Expand Down Expand Up @@ -1050,10 +1129,10 @@ inline connection::connection(MAPPING const &params)

namespace pqxx::internal
{
PQXX_LIBEXPORT void wait_read(internal::pq::PGconn const *);
PQXX_LIBEXPORT void wait_read(
internal::pq::PGconn const *, std::time_t seconds, long microseconds);
PQXX_LIBEXPORT void wait_write(internal::pq::PGconn const *);
/// Wait for a socket to be ready for reading/writing, or timeout.
PQXX_LIBEXPORT void wait_fd(
int fd, bool for_read, bool for_write, unsigned seconds = 1,
unsigned microseconds = 0);
} // namespace pqxx::internal

#include "pqxx/internal/compiler-internal-post.hxx"
Expand Down
4 changes: 2 additions & 2 deletions include/pqxx/strconv.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,8 @@ template<typename TYPE> inline constexpr format param_format(TYPE const &)
* we can reference them by a pointer.
*/
template<class TYPE>
concept binary = std::ranges::contiguous_range<TYPE> and
std::is_same_v<strip_t<value_type<TYPE>>, std::byte>;
concept binary = std::ranges::contiguous_range<TYPE>
and std::is_same_v<strip_t<value_type<TYPE>>, std::byte>;
#endif
//@}
} // namespace pqxx
Expand Down
7 changes: 3 additions & 4 deletions include/pqxx/types.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ using value_type = decltype(*std::begin(std::declval<CONTAINER>()));
#if defined(PQXX_HAVE_CONCEPTS)
/// Concept: Any type that we can read as a string of @c char.
template<typename STRING>
concept char_string = std::ranges::contiguous_range<STRING> and
std::same_as < strip_t<value_type<STRING>>,
char > ;
concept char_string = std::ranges::contiguous_range<STRING>
and std::same_as<strip_t<value_type<STRING>>, char>;

/// Concept: Anything we can iterate to get things we can read as strings.
template<typename RANGE>
Expand All @@ -119,7 +118,7 @@ concept char_strings =
/// Concept: Anything we might want to treat as binary data.
template<typename DATA>
concept potential_binary = std::ranges::contiguous_range<DATA> and
(sizeof(value_type<DATA>) == 1);
(sizeof(value_type<DATA>) == 1);
#endif // PQXX_HAVE_CONCEPTS


Expand Down
3 changes: 1 addition & 2 deletions include/pqxx/util.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,7 @@ std::basic_string_view<std::byte> binary_cast(TYPE const &data)


#if defined(PQXX_HAVE_CONCEPTS)
template<typename CHAR>
concept char_sized = (sizeof(CHAR) == 1);
template<typename CHAR> concept char_sized = (sizeof(CHAR) == 1);
# define PQXX_CHAR_SIZED_ARG char_sized
#else
# define PQXX_CHAR_SIZED_ARG typename
Expand Down
6 changes: 3 additions & 3 deletions include/pqxx/zview.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ namespace pqxx::internal
* support each of these individually.
*/
template<typename T>
concept ZString = std::is_convertible_v < strip_t<T>,
char const * > or std::is_convertible_v<strip_t<T>, zview> or
std::is_convertible_v<T, std::string const &>;
concept ZString = std::is_convertible_v<strip_t<T>, char const *> or
std::is_convertible_v<strip_t<T>, zview> or
std::is_convertible_v<T, std::string const &>;
} // namespace pqxx::internal
#endif // PQXX_HAVE_CONCEPTS

Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.16.3 from Makefile.am.
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@

# Copyright (C) 1994-2020 Free Software Foundation, Inc.
Expand Down
Loading

0 comments on commit b4b8935

Please sign in to comment.