forked from Kistler-Group/sdbus-cpp
Compare commits
15 Commits
v1.4.0
...
refactor/t
Author | SHA1 | Date | |
---|---|---|---|
e75337bd23 | |||
28921ad424 | |||
721f583db1 | |||
47a84ab889 | |||
d80483cdc0 | |||
934d51fa8a | |||
fb9e4ae371 | |||
6e348f3910 | |||
f50e4676fe | |||
1aa30e3a20 | |||
e2b3e98374 | |||
9490b3351f | |||
9da18aec25 | |||
b7b454ba38 | |||
f420b216aa |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -48,19 +48,19 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-release
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-with-embedded-libsystemd
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
|
||||
- name: make
|
||||
run: |
|
||||
cd build
|
||||
@ -86,18 +86,18 @@ jobs:
|
||||
retention-days: 10
|
||||
freebsd-build:
|
||||
name: build (freebsd, clang/libc++, basu)
|
||||
runs-on: macos-12 # until https://github.com/actions/runner/issues/385
|
||||
runs-on: ubuntu-22.04 # until https://github.com/actions/runner/issues/385
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test in FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@v0
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
copyback: false
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y cmake ninja pkgconf basu expat googletest
|
||||
run: |
|
||||
cmake -B _build -G Ninja -DBUILD_CODE_GEN=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON
|
||||
cmake -B _build -G Ninja -DBUILD_CODE_GEN=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON
|
||||
cmake --build _build
|
||||
cmake --install _build
|
||||
pkg install -y dbus
|
||||
|
@ -12,7 +12,8 @@ include(GNUInstallDirs) # Installation directories for `install` command and pkg
|
||||
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
|
||||
#-------------------------------
|
||||
|
||||
set(LIBSYSTEMD "systemd")
|
||||
set(LIBSYSTEMD_IMPL "systemd")
|
||||
set(LIBSYSTEMD_LIB "libsystemd")
|
||||
|
||||
option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF)
|
||||
|
||||
@ -23,13 +24,15 @@ if(NOT BUILD_LIBSYSTEMD)
|
||||
message(WARNING "libsystemd not found, checking for libelogind instead")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=236)
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(LIBSYSTEMD "elogind")
|
||||
set(LIBSYSTEMD_IMPL "elogind")
|
||||
set(LIBSYSTEMD_LIB "libelogind")
|
||||
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
|
||||
list(GET VERSION_LIST 0 Systemd_VERSION)
|
||||
else()
|
||||
else()
|
||||
message(WARNING "libelogind not found, checking for basu instead")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
|
||||
set(LIBSYSTEMD "basu")
|
||||
set(LIBSYSTEMD_IMPL "basu")
|
||||
set(LIBSYSTEMD_LIB "basu")
|
||||
# https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
|
||||
set(Systemd_VERSION "240")
|
||||
endif()
|
||||
@ -125,8 +128,8 @@ add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
|
||||
target_compile_definitions(sdbus-c++-objlib PRIVATE
|
||||
BUILD_LIB=1
|
||||
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
|
||||
SDBUS_${LIBSYSTEMD}
|
||||
SDBUS_HEADER=<${LIBSYSTEMD}/sd-bus.h>)
|
||||
SDBUS_${LIBSYSTEMD_IMPL}
|
||||
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
|
||||
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
@ -169,7 +172,7 @@ install(TARGETS ${EXPORT_SET}
|
||||
# TESTS
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_TESTS "Build and install tests (default OFF)" OFF)
|
||||
option(BUILD_TESTS "Build tests (default OFF)" OFF)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
message(STATUS "Building with tests")
|
||||
@ -218,6 +221,7 @@ endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
|
||||
install(EXPORT sdbus-c++-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
NAMESPACE SDBusCpp::
|
||||
@ -236,6 +240,7 @@ if(BUILD_SHARED_LIBS AND (BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/l
|
||||
else()
|
||||
set(PKGCONFIG_REQS "")
|
||||
endif()
|
||||
set(PKGCONFIG_DEPS ${LIBSYSTEMD_LIB})
|
||||
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
|
||||
|
@ -50,9 +50,13 @@ $ sudo cmake --build . --target install
|
||||
|
||||
Option for building sdbus-c++ stress tests. Default value: `OFF`.
|
||||
|
||||
* `INSTALL_TESTS` [boolean]
|
||||
|
||||
Option for installing tests that were built. Default value: `OFF`.
|
||||
|
||||
* `TESTS_INSTALL_PATH` [string]
|
||||
|
||||
Path where the test binaries shall get installed. Default value: `/opt/test/bin`.
|
||||
Path where the test binaries shall get installed. Default value: `${CMAKE_INSTALL_PREFIX}/tests/sdbus-c++` (previously: `/opt/test/bin`).
|
||||
|
||||
* `BUILD_LIBSYSTEMD` [boolean]
|
||||
|
||||
@ -82,7 +86,7 @@ Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 features.
|
||||
* `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
|
||||
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
|
||||
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
|
||||
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
|
||||
* `expat` - necessary when building xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
|
||||
|
@ -75,13 +75,19 @@ add_custom_command(
|
||||
Solving libsystemd dependency
|
||||
-----------------------------
|
||||
|
||||
sdbus-c++ depends on libsystemd, a C library that is part of [systemd](https://github.com/systemd/systemd) and that contains underlying sd-bus implementation.
|
||||
sdbus-c++ depends on sd-bus API, which is implemented in libsystemd, a C library that is part of [systemd](https://github.com/systemd/systemd).
|
||||
|
||||
Minimum required libsystemd shared library version is 0.20.0 (which corresponds to minimum systemd version 236).
|
||||
|
||||
If your target Linux distribution is already based on systemd ecosystem of version 236 and higher, then there is no additional effort, just make sure you have corresponding systemd header files available (provided by `libsystemd-dev` package on Debian/Ubuntu, for example), and you may go on building sdbus-c++ seamlessly.
|
||||
|
||||
However, sdbus-c++ can perfectly be used in non-systemd environments as well. There are two ways to approach this:
|
||||
However, sdbus-c++ can perfectly be used in non-systemd environments as well. If `libsystemd` is not found in the system when configuring sdbus-c++, then
|
||||
|
||||
1. sdbus-c++ will try to find `libelogind`, which is an extracted logind from original systemd containing sd-bus implementation. If not found, then
|
||||
|
||||
2. sdbus-c++ will try to find `basu`, which is just sd-bus implementation extracted from systemd.
|
||||
|
||||
On systems where neither of these is available, we can build sd-bus as a shared lib manually or we can (conveniently) instruct sdbus-c++ to build and integrate sd-bus into itself for us.
|
||||
|
||||
### Building and distributing libsystemd as a shared library yourself
|
||||
|
||||
@ -101,7 +107,7 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
|
||||
|
||||
### Building and distributing libsystemd as part of sdbus-c++
|
||||
|
||||
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the Also, when distributing, make sure these dependency libraries are installed on the production machine.
|
||||
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also, when distributing, make sure these dependency libraries are installed on the production machine.
|
||||
|
||||
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
|
||||
|
||||
@ -388,6 +394,27 @@ Subsequently, we invoke two RPC calls to object's `concatenate()` method. We cre
|
||||
|
||||
Please note that we can create and destroy D-Bus object proxies dynamically, at any time during runtime, even when they share a common D-Bus connection and there is an active event loop upon the connection. So managing D-Bus object proxies' lifecycle (creating and destroying D-Bus object proxies) is completely thread-safe.
|
||||
|
||||
### Opening bus connections in sdbus-c++
|
||||
|
||||
There are several factory methods to create a bus connection object in sdbus-c++:
|
||||
|
||||
* `createConnection()` - opens a connection to the system bus
|
||||
* `createConnection(const std::string& name)` - opens a connection with the given name to the system bus
|
||||
* `createDefaultBusConnection()` - opens a connection to the session bus when in a user context, and a connection to the system bus, otherwise
|
||||
* `createDefaultBusConnection(const std::string& name)` - opens a connection with the given name to the session bus when in a user context, and a connection with the given name to the system bus, otherwise
|
||||
* `createSystemBusConnection()` - opens a connection to the system bus
|
||||
* `createSystemBusConnection(const std::string& name)` - opens a connection with the given name to the system bus
|
||||
* `createSessionBusConnection()` - opens a connection to the session bus
|
||||
* `createSessionBusConnection(const std::string& name)` - opens a connection with the given name to the session bus
|
||||
* `createSessionBusConnectionWithAddress(const std::string& address)` - opens a connection to the session bus at a custom address
|
||||
* `createRemoteSystemBusConnection(const std::string& host)` - opens a connection to the system bus on a remote host using ssh
|
||||
* `createDirectBusConnection(const std::string& address)` - opens direct D-Bus connection at a custom address (see [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections))
|
||||
* `createDirectBusConnection(int fd)` - opens direct D-Bus connection at the given file descriptor (see [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections))
|
||||
* `createServerBus(int fd)` - opens direct D-Bus connection at the given file descriptor as a server (see [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections))
|
||||
* `createBusConnection(sd_bus *bus)` - creates a connection directly from the underlying sd_bus connection instance (which has been created and set up upfront directly through sd-bus API).
|
||||
|
||||
For more information, peek into [`IConnection.h`](/include/sdbus-c++/IConnection.h) where these functions are declared and documented.
|
||||
|
||||
### Working with D-Bus connections in sdbus-c++
|
||||
|
||||
The design of D-Bus connections in sdbus-c++ allows for certain flexibility and enables users to choose simplicity over scalability or scalability (at a finer granularity of user's choice) at the cost of slightly decreased simplicity.
|
||||
@ -565,6 +592,7 @@ We recommend that sdbus-c++ users prefer the convenience API to the lower level,
|
||||
> assert(e->getMessage() == "Failed to deserialize a int32 value");
|
||||
> }
|
||||
> ```
|
||||
> Signature mismatch in signal handlers is probably the most common reason why signals are not received in the client, while we can see them on the bus with `dbus-monitor`. Use `const sdbus::Error*`-based callback variant and inspect the error to check if that's the cause of such problems.
|
||||
|
||||
> **_Tip_:** When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces:
|
||||
> ```c++
|
||||
@ -1512,10 +1540,10 @@ The above mapping between D-Bus and C++ types is what sdbus-c++ provides by defa
|
||||
|
||||
We need two things to do that:
|
||||
|
||||
* implement `sdbus::Message` insertion and extraction operators, so sdbus-c++ knows how to serialize/deserialize our custom type,
|
||||
* implement `sdbus::Message` insertion (serialization) and extraction (deserialization) operators, so sdbus-c++ knows how to serialize/deserialize our custom type,
|
||||
* specialize `sdbus::signature_of` template for our custom type, so sdbus-c++ knows the mapping to D-Bus type and other necessary information about our type.
|
||||
|
||||
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension:
|
||||
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension. To implement message serialization and deserialization functions for `std::list`, we can simply copy the sdbus-c++ implementation of these functions for `std::vector`, and simply adjust for `std::list`. Then we provide `signature_of` specialization, again written on terms of one specialized for `std::vector`:
|
||||
|
||||
```c++
|
||||
#include <list>
|
||||
@ -1568,12 +1596,11 @@ template <typename _Element, typename _Allocator>
|
||||
struct sdbus::signature_of<std::list<_Element, _Allocator>>
|
||||
: sdbus::signature_of<std::vector<_Element, _Allocator>>
|
||||
{};
|
||||
};
|
||||
```
|
||||
|
||||
Then we can simply use `std::list`s, serialize/deserialize them in a D-Bus message, in D-Bus method calls or return values... and they will be simply transmitted as D-Bus arrays.
|
||||
|
||||
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate:
|
||||
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate. Serialization/deserialization functions can be placed in the same namespace as our custom type, and will be found thanks to the ADR lookup. The `signature_of` specialization must always be in either `sdbus` namespace or in a global namespace:
|
||||
|
||||
```c++
|
||||
namespace my {
|
||||
@ -1596,7 +1623,7 @@ namespace my {
|
||||
sdbus::Struct s{std::forward_as_tuple(items.i, items.s, items.l)};
|
||||
return msg >> s;
|
||||
}
|
||||
}
|
||||
} // namespace my
|
||||
|
||||
template <>
|
||||
struct sdbus::signature_of<my::Struct>
|
||||
@ -1611,7 +1638,7 @@ Live examples of extending sdbus-c++ types can be found in [Message unit tests](
|
||||
Support for match rules
|
||||
-----------------------
|
||||
|
||||
`IConnection` class provides `addMatch` method that you can use to install match rules. An associated callback handler will be called upon an incoming message matching given match rule. There is support for both client-owned and floating (library-owned) match rules. Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
|
||||
`IConnection` class provides `addMatch` and `addMatchAsync` family of methods that you can use to install match rules on that bus connection. An associated callback handler will be called when an incoming D-Bus message matches the given match rule. Clients can decide whether they own and control the match rule lifetime, or whether the match rule lifetime is bound the connection object lifetime (so-called floating match rule). Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
|
||||
|
||||
Using direct (peer-to-peer) D-Bus connections
|
||||
---------------------------------------------
|
||||
|
@ -595,9 +595,8 @@ namespace sdbus {
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
// Catch message unpack exceptions and pass them to the callback
|
||||
// in the expected manner to avoid propagating them up the call
|
||||
// stack to the event loop.
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, &e, args);
|
||||
return;
|
||||
}
|
||||
@ -676,8 +675,10 @@ namespace sdbus {
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
// Invoke callback with error argument and input arguments from the tuple.
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, &e, signalArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke callback with no error and input arguments from the tuple.
|
||||
|
@ -76,6 +76,8 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg);
|
||||
|
||||
inline const char* SDBUSCPP_ERROR_NAME = "org.sdbuscpp.Error";
|
||||
}
|
||||
|
||||
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
|
||||
|
@ -268,10 +268,10 @@ namespace sdbus {
|
||||
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds a match rule for incoming message dispatching
|
||||
* @brief Installs a match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @return RAII-style slot handle representing the ownership of the subscription
|
||||
*
|
||||
* The method installs a match rule for messages received on the specified bus connection.
|
||||
@ -291,10 +291,10 @@ namespace sdbus {
|
||||
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds a floating match rule for incoming message dispatching
|
||||
* @brief Installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
@ -307,6 +307,45 @@ namespace sdbus {
|
||||
*/
|
||||
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @return RAII-style slot handle representing the ownership of the subscription
|
||||
*
|
||||
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
|
||||
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
|
||||
* The `installCallback' callable is called when the response is later received, with the response message
|
||||
* from the broker as parameter. If it's an empty function object, a default implementation is used that
|
||||
* terminates the bus connection should installing the match fail.
|
||||
*
|
||||
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation, and consult
|
||||
* `man sd_bus_add_match`, for more information.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* Refer to the @c addMatch(const std::string& match, message_handler callback, message_handler installCallback)
|
||||
* documentation for more information.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterEventLoop()
|
||||
*
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <algorithm>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -56,7 +56,7 @@ namespace sdbus {
|
||||
Variant();
|
||||
|
||||
template <typename _ValueType>
|
||||
/*explicit*/ Variant(const _ValueType& value) // TODO: Mark explicit in new major version so we don't break client code within v1
|
||||
Variant(const _ValueType& value)
|
||||
: Variant()
|
||||
{
|
||||
msg_.openVariant(signature_of<_ValueType>::str());
|
||||
@ -209,7 +209,7 @@ namespace sdbus {
|
||||
UnixFd() = default;
|
||||
|
||||
explicit UnixFd(int fd)
|
||||
: fd_(::dup(fd))
|
||||
: fd_(checkedDup(fd))
|
||||
{
|
||||
}
|
||||
|
||||
@ -225,8 +225,12 @@ namespace sdbus {
|
||||
|
||||
UnixFd& operator=(const UnixFd& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
close();
|
||||
fd_ = ::dup(other.fd_);
|
||||
fd_ = checkedDup(other.fd_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -237,9 +241,12 @@ namespace sdbus {
|
||||
|
||||
UnixFd& operator=(UnixFd&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
fd_ = std::exchange(other.fd_, -1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -265,9 +272,7 @@ namespace sdbus {
|
||||
|
||||
int release()
|
||||
{
|
||||
auto fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
return std::exchange(fd_, -1);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
@ -276,11 +281,12 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
private:
|
||||
void close()
|
||||
{
|
||||
if (fd_ >= 0)
|
||||
::close(fd_);
|
||||
}
|
||||
/// Closes file descriptor, but does not set it to -1.
|
||||
void close();
|
||||
|
||||
/// Returns negative argument unchanged.
|
||||
/// Otherwise, call ::dup and throw on failure.
|
||||
static int checkedDup(int fd);
|
||||
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: C++ library on top of sd-bus, a systemd D-Bus library
|
||||
Requires@PKGCONFIG_REQS@: @LIBSYSTEMD@
|
||||
Requires@PKGCONFIG_REQS@: @PKGCONFIG_DEPS@
|
||||
Version: @SDBUSCPP_VERSION@
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@
|
||||
Cflags: -I${includedir}
|
||||
|
@ -108,12 +108,20 @@ void Connection::requestName(const std::string& name)
|
||||
|
||||
auto r = iface_->sd_bus_request_name(bus_.get(), name.c_str(), 0);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r);
|
||||
|
||||
// In some cases we need to explicitly notify the event loop
|
||||
// to process messages that may have arrived while executing the call.
|
||||
notifyEventLoop(eventFd_.fd);
|
||||
}
|
||||
|
||||
void Connection::releaseName(const std::string& name)
|
||||
{
|
||||
auto r = iface_->sd_bus_release_name(bus_.get(), name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r);
|
||||
|
||||
// In some cases we need to explicitly notify the event loop
|
||||
// to process messages that may have arrived while executing the call.
|
||||
notifyEventLoop(eventFd_.fd);
|
||||
}
|
||||
|
||||
std::string Connection::getUniqueName() const
|
||||
@ -217,17 +225,11 @@ uint64_t Connection::getMethodCallTimeout() const
|
||||
|
||||
Slot Connection::addMatch(const std::string& match, message_handler callback)
|
||||
{
|
||||
auto matchInfo = std::make_unique<MatchInfo>(MatchInfo{std::move(callback), *this, {}});
|
||||
SDBUS_THROW_ERROR_IF(!callback, "Invalid match callback handler provided", EINVAL);
|
||||
|
||||
auto messageHandler = [](sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) -> int
|
||||
{
|
||||
auto* matchInfo = static_cast<MatchInfo*>(userData);
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||
matchInfo->callback(message);
|
||||
return 0;
|
||||
};
|
||||
auto matchInfo = std::make_unique<MatchInfo>(MatchInfo{std::move(callback), {}, *this, {}});
|
||||
|
||||
auto r = iface_->sd_bus_add_match(bus_.get(), &matchInfo->slot, match.c_str(), std::move(messageHandler), matchInfo.get());
|
||||
auto r = iface_->sd_bus_add_match(bus_.get(), &matchInfo->slot, match.c_str(), &Connection::sdbus_match_callback, matchInfo.get());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add match", -r);
|
||||
|
||||
return {matchInfo.release(), [this](void *ptr)
|
||||
@ -243,6 +245,34 @@ void Connection::addMatch(const std::string& match, message_handler callback, fl
|
||||
floatingMatchRules_.push_back(addMatch(match, std::move(callback)));
|
||||
}
|
||||
|
||||
Slot Connection::addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!callback, "Invalid match callback handler provided", EINVAL);
|
||||
|
||||
sd_bus_message_handler_t sdbusInstallCallback = installCallback ? &Connection::sdbus_match_install_callback : nullptr;
|
||||
auto matchInfo = std::make_unique<MatchInfo>(MatchInfo{std::move(callback), std::move(installCallback), *this, {}});
|
||||
|
||||
auto r = iface_->sd_bus_add_match_async( bus_.get()
|
||||
, &matchInfo->slot
|
||||
, match.c_str()
|
||||
, &Connection::sdbus_match_callback
|
||||
, sdbusInstallCallback
|
||||
, matchInfo.get());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add match", -r);
|
||||
|
||||
return {matchInfo.release(), [this](void *ptr)
|
||||
{
|
||||
auto* matchInfo = static_cast<MatchInfo*>(ptr);
|
||||
iface_->sd_bus_slot_unref(matchInfo->slot);
|
||||
std::default_delete<MatchInfo>{}(matchInfo);
|
||||
}};
|
||||
}
|
||||
|
||||
void Connection::addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t)
|
||||
{
|
||||
floatingMatchRules_.push_back(addMatchAsync(match, std::move(callback), std::move(installCallback)));
|
||||
}
|
||||
|
||||
Slot Connection::addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
@ -565,6 +595,22 @@ std::vector</*const */char*> Connection::to_strv(const std::vector<std::string>&
|
||||
return strv;
|
||||
}
|
||||
|
||||
int Connection::sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* matchInfo = static_cast<MatchInfo*>(userData);
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ matchInfo->callback(message); }, retError);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
int Connection::sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* matchInfo = static_cast<MatchInfo*>(userData);
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ matchInfo->installCallback(message); }, retError);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
Connection::EventFd::EventFd()
|
||||
{
|
||||
fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
|
@ -96,6 +96,8 @@ namespace sdbus::internal {
|
||||
|
||||
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
|
||||
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
|
||||
[[nodiscard]] Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
|
||||
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) override;
|
||||
|
||||
const ISdBus& getSdBusInterface() const override;
|
||||
ISdBus& getSdBusInterface() override;
|
||||
@ -151,10 +153,13 @@ namespace sdbus::internal {
|
||||
void clearEventLoopNotification(int fd) const;
|
||||
void notifyEventLoopNewTimeout() const override;
|
||||
|
||||
private:
|
||||
void joinWithEventLoop();
|
||||
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
|
||||
|
||||
static int sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
struct EventFd
|
||||
{
|
||||
EventFd();
|
||||
@ -165,6 +170,7 @@ namespace sdbus::internal {
|
||||
struct MatchInfo
|
||||
{
|
||||
message_handler callback;
|
||||
message_handler installCallback;
|
||||
Connection& connection;
|
||||
sd_bus_slot *slot;
|
||||
};
|
||||
|
@ -80,6 +80,7 @@ namespace sdbus::internal {
|
||||
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) = 0;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) = 0;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
|
||||
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) = 0;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) = 0;
|
||||
|
@ -347,16 +347,9 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
|
||||
auto& callback = interfaceData->methods[message.getMemberName()].callback;
|
||||
assert(callback);
|
||||
|
||||
try
|
||||
{
|
||||
callback(message);
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(message); }, retError);
|
||||
|
||||
return 1;
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
@ -381,16 +374,9 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
|
||||
|
||||
try
|
||||
{
|
||||
callback(reply);
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(reply); }, retError);
|
||||
|
||||
return 1;
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
@ -416,16 +402,9 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
callback(value);
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(value); }, retError);
|
||||
|
||||
return 1;
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ std::future<MethodReply> Proxy::callMethod(const MethodCall& message, uint64_t t
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply& reply, const Error* error) noexcept
|
||||
{
|
||||
if (error == nullptr)
|
||||
promise->set_value(reply); // TODO: std::move? Can't move now because currently processed message. TODO: Refactor
|
||||
promise->set_value(reply);
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*error));
|
||||
};
|
||||
@ -269,7 +269,7 @@ const Message* Proxy::getCurrentlyProcessedMessage() const
|
||||
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
|
||||
assert(asyncCallData != nullptr);
|
||||
@ -295,7 +295,7 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
||||
proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
try
|
||||
auto ok = invokeHandlerAndCatchErrors([&]
|
||||
{
|
||||
const auto* error = sd_bus_message_get_error(sdbusMessage);
|
||||
if (error == nullptr)
|
||||
@ -307,16 +307,12 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
||||
Error exception(error->name, error->message);
|
||||
asyncCallData->callback(message, &exception);
|
||||
}
|
||||
}
|
||||
catch (const Error&)
|
||||
{
|
||||
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
||||
}
|
||||
}, retError);
|
||||
|
||||
return 0;
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* signalData = static_cast<InterfaceData::SignalData*>(userData);
|
||||
assert(signalData != nullptr);
|
||||
@ -330,16 +326,9 @@ int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd
|
||||
signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
signalData->callback(message);
|
||||
}
|
||||
catch (const Error&)
|
||||
{
|
||||
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
||||
}
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ signalData->callback(message); }, retError);
|
||||
|
||||
return 0;
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
118
src/ScopeGuard.h
118
src/ScopeGuard.h
@ -55,62 +55,104 @@
|
||||
// return; // exiting scope normally
|
||||
// }
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::detail::ScopeGuardOnExit() + [&]() \
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitTag{} + [&]() \
|
||||
/**/
|
||||
|
||||
#define SCOPE_EXIT_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::detail::ScopeGuardOnExit() + [&]() \
|
||||
#define SCOPE_EXIT_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_SUCCESS \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitSuccessTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_SUCCESS_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitSuccessTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_FAILURE \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitFailureTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_FAILURE_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitFailureTag{} + [&]() \
|
||||
/**/
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
template <class _Fun>
|
||||
struct ScopeGuardOnExitTag
|
||||
{
|
||||
static bool holds(int /*originalExceptions*/)
|
||||
{
|
||||
return true; // Always holds
|
||||
}
|
||||
};
|
||||
struct ScopeGuardOnExitSuccessTag
|
||||
{
|
||||
static bool holds(int originalExceptions)
|
||||
{
|
||||
return originalExceptions == std::uncaught_exceptions(); // Only holds when no exception occurred within the scope
|
||||
}
|
||||
};
|
||||
struct ScopeGuardOnExitFailureTag
|
||||
{
|
||||
static bool holds(int originalExceptions)
|
||||
{
|
||||
return originalExceptions != std::uncaught_exceptions(); // Only holds when an exception occurred within the scope
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Fun, typename _Tag>
|
||||
class ScopeGuard
|
||||
{
|
||||
_Fun fnc_;
|
||||
bool active_;
|
||||
|
||||
public:
|
||||
ScopeGuard(_Fun f)
|
||||
: fnc_(std::move(f))
|
||||
, active_(true)
|
||||
ScopeGuard(_Fun f) : fnc_(std::move(f))
|
||||
{
|
||||
}
|
||||
~ScopeGuard()
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs) : fnc_(std::move(rhs.fnc_)), active_(rhs.active_), exceptions_(rhs.exceptions_)
|
||||
{
|
||||
if (active_)
|
||||
fnc_();
|
||||
rhs.dismiss();
|
||||
}
|
||||
|
||||
void dismiss()
|
||||
{
|
||||
active_ = false;
|
||||
}
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs)
|
||||
: fnc_(std::move(rhs.fnc_))
|
||||
, active_(rhs.active_)
|
||||
|
||||
~ScopeGuard()
|
||||
{
|
||||
rhs.dismiss();
|
||||
if (active_ && _Tag::holds(exceptions_))
|
||||
fnc_();
|
||||
}
|
||||
|
||||
private:
|
||||
_Fun fnc_;
|
||||
int exceptions_{std::uncaught_exceptions()};
|
||||
bool active_{true};
|
||||
};
|
||||
|
||||
namespace detail
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, _Fun&& fnc)
|
||||
{
|
||||
enum class ScopeGuardOnExit
|
||||
{
|
||||
};
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
// Helper function to auto-deduce type of the callable entity
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun> operator+(ScopeGuardOnExit, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitFailureTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
}
|
||||
@ -119,13 +161,9 @@ namespace sdbus::internal {
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __COUNTER__) \
|
||||
/**/
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __LINE__) \
|
||||
/**/
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
|
||||
#endif
|
||||
|
||||
#endif /* SDBUS_CPP_INTERNAL_SCOPEGUARD_H_ */
|
||||
|
@ -345,6 +345,13 @@ int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match,
|
||||
return ::sd_bus_add_match(bus, slot, match, callback, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_match_async(bus, slot, match, callback, install_callback, userdata);
|
||||
}
|
||||
|
||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) override;
|
||||
|
@ -29,6 +29,9 @@
|
||||
#include "MessageUtils.h"
|
||||
#include SDBUS_HEADER
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <system_error>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -64,4 +67,27 @@ bool Variant::isEmpty() const
|
||||
return msg_.isEmpty();
|
||||
}
|
||||
|
||||
void UnixFd::close()
|
||||
{
|
||||
if (fd_ >= 0)
|
||||
{
|
||||
::close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
int UnixFd::checkedDup(int fd)
|
||||
{
|
||||
if (fd < 0)
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ret = ::dup(fd);
|
||||
if (ret < 0)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "dup failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
34
src/Utils.h
34
src/Utils.h
@ -50,4 +50,38 @@
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME)
|
||||
#endif
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
template <typename _Callable>
|
||||
bool invokeHandlerAndCatchErrors(_Callable callable, sd_bus_error *retError)
|
||||
{
|
||||
try
|
||||
{
|
||||
callable();
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, e.what());
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, "Unknown error occurred");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implementation of the overload pattern for variant visitation
|
||||
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
|
||||
template <class... Ts> overload(Ts...) -> overload<Ts...>;
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */
|
||||
|
@ -106,7 +106,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-unit-tests PRIVATE
|
||||
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
|
||||
SDBUS_HEADER=<${LIBSYSTEMD}/sd-bus.h>)
|
||||
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
|
||||
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
|
||||
|
||||
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
|
||||
@ -140,22 +140,26 @@ endif()
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(TESTS_INSTALL_PATH "/opt/test/bin" CACHE STRING "Specifies where the test binaries will be installed")
|
||||
option(INSTALL_TESTS "Install tests (default OFF)" OFF)
|
||||
|
||||
install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
if(INSTALL_TESTS)
|
||||
include(GNUInstallDirs)
|
||||
set(TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
|
||||
|
||||
if(ENABLE_PERF_TESTS)
|
||||
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
endif()
|
||||
install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
|
||||
if(ENABLE_STRESS_TESTS)
|
||||
install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
if(ENABLE_PERF_TESTS)
|
||||
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
endif()
|
||||
|
||||
if(ENABLE_STRESS_TESTS)
|
||||
install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
|
@ -88,6 +88,29 @@ TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, CanInstallMatchRuleAsynchronously)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
std::atomic<bool> matchRuleInstalled{false};
|
||||
auto slot = s_proxyConnection->addMatchAsync( matchRule
|
||||
, [&](sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}
|
||||
, [&](sdbus::Message& /*msg*/)
|
||||
{
|
||||
matchRuleInstalled = true;
|
||||
} );
|
||||
|
||||
EXPECT_TRUE(waitUntil(matchRuleInstalled));
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
MOCK_METHOD6(sd_bus_add_object_vtable, int(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata));
|
||||
MOCK_METHOD3(sd_bus_add_object_manager, int(sd_bus *bus, sd_bus_slot **slot, const char *path));
|
||||
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
|
||||
MOCK_METHOD6(sd_bus_add_match_async, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata));
|
||||
MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot));
|
||||
|
||||
MOCK_METHOD1(sd_bus_new, int(sd_bus **ret));
|
||||
|
Reference in New Issue
Block a user