aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libnvxx/nv_list.cc30
-rw-r--r--libnvxx/nvxx.321
-rw-r--r--libnvxx/nvxx_base.h20
-rw-r--r--libnvxx/tests/Makefile2
-rw-r--r--libnvxx/tests/nvxx_basic.cc82
5 files changed, 127 insertions, 28 deletions
diff --git a/libnvxx/nv_list.cc b/libnvxx/nv_list.cc
index 6eadd36..eace039 100644
--- a/libnvxx/nv_list.cc
+++ b/libnvxx/nv_list.cc
@@ -130,20 +130,6 @@ nv_list::recv(int fd, int flags)
std::error_code(errno, std::system_category()));
}
-nv_list
-nv_list::xfer(int fd, nv_list &&nvl, int flags)
-{
- if (auto *nv = ::nvlist_xfer(fd, nvl.__m_nv, flags);
- nv != nullptr) {
- // nvlist_xfer destroys the original list
- nvl.__m_nv = nullptr;
- return (nv_list(nv));
- }
-
- throw std::system_error(
- std::error_code(errno, std::system_category()));
-}
-
namespace __detail {
/*
@@ -164,6 +150,22 @@ __nv_list::operator const_nv_list() const
return (const_nv_list(__m_nv));
}
+nv_list
+__nv_list::xfer(int fd, int flags) &&
+{
+ __throw_if_error();
+
+ auto *nv = ::nvlist_xfer(fd, __m_nv, flags);
+ // nvlist_xfer always destroys the original list
+ __m_nv = nullptr;
+
+ if (nv != nullptr)
+ return (nv_list(nv));
+
+ throw std::system_error(
+ std::error_code(errno, std::system_category()));
+}
+
void
__nv_list::free(std::string_view key)
{
diff --git a/libnvxx/nvxx.3 b/libnvxx/nvxx.3
index 37db659..c5ef02f 100644
--- a/libnvxx/nvxx.3
+++ b/libnvxx/nvxx.3
@@ -134,6 +134,8 @@ struct nv_list : <unspecified> {
operator const_nv_list() const;
+ nv_list xfer(int fd, int flags = 0) &&;
+
void add_null(std::string_view key);
void add_bool(std::string_view key, bool value);
void add_number(std::string_view key, std::uint64_t);
@@ -603,6 +605,25 @@ If the nvlist is already in the error state, then an exception of type
is thrown and the error state is not changed.
.Pp
The
+.Fn xfer
+function sends this nvlist over a socket as if by
+.Fn send ,
+and then receives a new nvlist from the same socket as if by
+.Fn recv ,
+which is returned.
+If
+.Fa flags
+is non-zero, the specified flags will be used to create the returned nvlist.
+The nvlist being sent is left in the empty state.
+If the nvlist is in the error state, an exception of type
+.Vt nv_error_state
+is thrown.
+If an operating system error occurs while writing to or reading from the file
+descriptor, an exception of type
+.Vt std::system_error
+is thrown and the nvlist being sent is left in the empty state.
+.Pp
+The
.Fn add_null ,
.Fn add_bool ,
.Fn add_number ,
diff --git a/libnvxx/nvxx_base.h b/libnvxx/nvxx_base.h
index 43e53cf..06d213e 100644
--- a/libnvxx/nvxx_base.h
+++ b/libnvxx/nvxx_base.h
@@ -214,6 +214,16 @@ struct __nv_list : virtual __nv_list_base {
*/
operator const_nv_list() const;
+ /*
+ * Send this nv_list over a file descriptor and receive another nv_list
+ * in response, which is returned, by calling nvlist_xfer(). On
+ * failure, throws std::system_error.
+ *
+ * The source nv_list is moved-from and is left in an undefined state.
+ * The returned nv_list is owning.
+ */
+ [[nodiscard]] nv_list xfer(int __fd, int __flags = 0) &&;
+
/* add */
void add_null(std::string_view);
@@ -434,16 +444,6 @@ struct nv_list final
*/
[[nodiscard]] static auto recv(int __fd, int __flags = 0) -> nv_list;
- /*
- * Send an nv_list over a file descriptor and receive another nv_list
- * in response, which is returned, by calling nvlist_xfer(). On
- * failure, throws std::system_error.
- *
- * The source nv_list is moved-from and is left in an undefined state.
- * The returned nv_list is owning.
- */
- [[nodiscard]] static nv_list xfer(int, nv_list &&, int);
-
void add_bool_range(std::string_view __key,
std::ranges::range auto &&__value)
{
diff --git a/libnvxx/tests/Makefile b/libnvxx/tests/Makefile
index 3ea8a5a..384f8a8 100644
--- a/libnvxx/tests/Makefile
+++ b/libnvxx/tests/Makefile
@@ -9,6 +9,6 @@ CXXSTD= c++23
CXXFLAGS+= -W -Wall -Wextra
CFLAGS+= -I${.CURDIR:H}
LDFLAGS+= -lprivateatf-c++ -L${.OBJDIR:H} -lnvxx
-LDFLAGS.nvxx_basic+= -lnv
+LDFLAGS.nvxx_basic+= -lnv -lpthread
.include <bsd.test.mk>
diff --git a/libnvxx/tests/nvxx_basic.cc b/libnvxx/tests/nvxx_basic.cc
index dbca98c..ca70fec 100644
--- a/libnvxx/tests/nvxx_basic.cc
+++ b/libnvxx/tests/nvxx_basic.cc
@@ -10,6 +10,7 @@
#include <span>
#include <string>
#include <string_view>
+#include <thread>
#include <sys/types.h>
#include <sys/socket.h>
@@ -526,8 +527,6 @@ TEST_CASE(nvxx_send_recv)
TEST_CASE(nvxx_send_error)
{
- using namespace std::literals;
-
auto nvl = bsd::nv_list();
nvl.set_error(std::errc::invalid_argument);
@@ -556,6 +555,79 @@ TEST_CASE(nvxx_send_empty)
}
/*
+ * xfer
+ */
+
+TEST_CASE(nvxx_xfer)
+{
+ using namespace std::literals;
+ auto constexpr send_key = "test"sv;
+ auto constexpr send_value = 42u;
+ auto constexpr resp_key = "response"sv;
+
+ auto fds = std::array<int, 2>{};
+ auto ret = ::socketpair(AF_UNIX, SOCK_STREAM, 0, &fds[0]);
+ ATF_REQUIRE_EQ(0, ret);
+
+ bsd::nv_fd fd0(fds[0]);
+ bsd::nv_fd fd1(fds[1]);
+
+ /*
+ * testing xfer() is a bit tricky because it blocks waiting for a
+ * response. create a thread that receives a list and sends its own in
+ * response.
+ */
+
+ auto respond_thread = std::thread([&] {
+ auto nvl2 = bsd::nv_list::recv(fd1.get());
+
+ auto resp_nvl = bsd::nv_list();
+ resp_nvl.add_number(resp_key, nvl2.get_number(send_key));
+ resp_nvl.send(fd1.get());
+ });
+
+ auto send_nvl = bsd::nv_list();
+ send_nvl.add_number(send_key, send_value);
+
+ auto nvl2 = std::move(send_nvl).xfer(fd0.get());
+ ATF_REQUIRE_EQ(send_value, nvl2.get_number(resp_key));
+
+ ATF_REQUIRE_THROW(std::logic_error, send_nvl.ptr());
+
+ respond_thread.join();
+}
+
+TEST_CASE(nvxx_xfer_error)
+{
+ auto nvl = bsd::nv_list();
+ nvl.set_error(std::errc::invalid_argument);
+
+ auto fds = std::array<int, 2>{};
+ auto ret = ::socketpair(AF_UNIX, SOCK_STREAM, 0, &fds[0]);
+ ATF_REQUIRE_EQ(0, ret);
+
+ bsd::nv_fd fd0(fds[0]);
+ bsd::nv_fd fd1(fds[1]);
+
+ ATF_REQUIRE_THROW(bsd::nv_error_state, (void)std::move(nvl).xfer(fd0.get()));
+}
+
+TEST_CASE(nvxx_xfer_empty)
+{
+ auto nvl = bsd::nv_list();
+ auto nvl2 = std::move(nvl);
+
+ auto fds = std::array<int, 2>{};
+ auto ret = ::socketpair(AF_UNIX, SOCK_STREAM, 0, &fds[0]);
+ ATF_REQUIRE_EQ(0, ret);
+
+ bsd::nv_fd fd0(fds[0]);
+ bsd::nv_fd fd1(fds[1]);
+
+ ATF_REQUIRE_THROW(std::logic_error, (void)std::move(nvl).xfer(fd0.get()));
+}
+
+/*
* exists(_type)
*/
@@ -2107,11 +2179,15 @@ ATF_INIT_TEST_CASES(tcs)
ATF_ADD_TEST_CASE(tcs, nvxx_unpack);
ATF_ADD_TEST_CASE(tcs, nvxx_unpack_range);
- ATF_ADD_TEST_CASE(tcs, nvxx_send_non_socket);
ATF_ADD_TEST_CASE(tcs, nvxx_send_recv);
+ ATF_ADD_TEST_CASE(tcs, nvxx_send_non_socket);
ATF_ADD_TEST_CASE(tcs, nvxx_send_empty);
ATF_ADD_TEST_CASE(tcs, nvxx_send_error);
+ ATF_ADD_TEST_CASE(tcs, nvxx_xfer);
+ ATF_ADD_TEST_CASE(tcs, nvxx_xfer_empty);
+ ATF_ADD_TEST_CASE(tcs, nvxx_xfer_error);
+
ATF_ADD_TEST_CASE(tcs, nvxx_exists);
ATF_ADD_TEST_CASE(tcs, nvxx_exists_nul_key);
ATF_ADD_TEST_CASE(tcs, nvxx_exists_type);