mirror of
https://github.com/Kistler-Group/sdbus-cpp.git
synced 2026-08-27 23:46:53 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
175c43ec53 | ||
|
|
c137dfa213 | ||
|
|
a0dadcc6fe | ||
|
|
0d010440c5 | ||
|
|
ae8849e545 | ||
|
|
fb35a9a196 | ||
|
|
6cbd49ddaa | ||
|
|
fb0a70a831 | ||
|
|
9af20af001 | ||
|
|
63bbb07ef0 | ||
|
|
00d0837d98 | ||
|
|
e91bedd4cb | ||
|
|
dc66efbbcb | ||
|
|
a23aadbe5e | ||
|
|
ae57c6760b | ||
|
|
21820f7529 | ||
|
|
3a4f343fb9 | ||
|
|
dee6adce02 | ||
|
|
3e68fee4cd | ||
|
|
8dfd29b0f0 | ||
|
|
db71707be4 | ||
|
|
c9583f2887 | ||
|
|
975f1bf07f |
+23
-18
@@ -2,9 +2,9 @@
|
||||
# PROJECT INFORMATION
|
||||
#-------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project(sdbus-c++ VERSION 0.8.0 LANGUAGES C CXX)
|
||||
project(sdbus-c++ VERSION 0.8.2 LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
@@ -16,12 +16,14 @@ option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into
|
||||
|
||||
if(NOT BUILD_LIBSYSTEMD)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SYSTEMD libsystemd>=236)
|
||||
if(NOT SYSTEMD_FOUND)
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
|
||||
if(NOT TARGET PkgConfig::Systemd)
|
||||
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
|
||||
"(you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to try downloading "
|
||||
"and building libsystemd in as part of sdbus-c++ during configuration)")
|
||||
endif()
|
||||
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
|
||||
set(LIBSYSTEMD_VERSION ${Systemd_VERSION})
|
||||
else()
|
||||
# Build static libsystemd library as an external project
|
||||
include(cmake/LibsystemdExternalProject.cmake)
|
||||
@@ -80,11 +82,7 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
|
||||
# GENERAL COMPILER CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 17) # Supported in CMake>=3.8
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
#----------------------------------
|
||||
# LIBRARY BUILD INFORMATION
|
||||
@@ -93,22 +91,25 @@ endif()
|
||||
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
# We promote BUILD_SHARED_LIBS flags to (global) option only if we are the main project
|
||||
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
endif()
|
||||
|
||||
# Having an object target allows unit tests to reuse already built sources without re-building
|
||||
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
|
||||
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
|
||||
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<BUILD_INTERFACE:${SYSTEMD_INCLUDE_DIRS}>)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
|
||||
if(DEFINED BUILD_SHARED_LIBS)
|
||||
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS})
|
||||
endif()
|
||||
if(BUILD_LIBSYSTEMD)
|
||||
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
|
||||
endif()
|
||||
target_link_libraries(sdbus-c++-objlib PUBLIC Systemd::Libsystemd)
|
||||
|
||||
add_library(sdbus-c++ $<TARGET_OBJECTS:sdbus-c++-objlib>)
|
||||
add_library(sdbus-c++)
|
||||
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
set_target_properties(sdbus-c++
|
||||
@@ -116,13 +117,17 @@ set_target_properties(sdbus-c++
|
||||
VERSION "${SDBUSCPP_VERSION}"
|
||||
SOVERSION "${SDBUSCPP_VERSION_MAJOR}"
|
||||
OUTPUT_NAME "sdbus-c++")
|
||||
target_link_libraries(sdbus-c++ PRIVATE ${SYSTEMD_LIBRARIES})
|
||||
target_link_libraries(sdbus-c++ PRIVATE sdbus-c++-objlib)
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
set(EXPORT_SET sdbus-c++)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
list(APPEND EXPORT_SET "sdbus-c++-objlib")
|
||||
endif()
|
||||
|
||||
install(TARGETS sdbus-c++
|
||||
install(TARGETS ${EXPORT_SET}
|
||||
EXPORT sdbus-c++-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
|
||||
|
||||
@@ -142,3 +142,21 @@ v0.7.8
|
||||
v0.8.0
|
||||
- [[Breaking ABI change]] Implement support for input & output parameter names for D-Bus methods and signals, which are used in introspection
|
||||
- Explain better in tutorial the design and how to use connections in relation to objects and proxies
|
||||
|
||||
v0.8.1
|
||||
- Switch to full C++17 support
|
||||
- Switch to more modern CMake (>=3.12)
|
||||
- Provide better names to event loop-related IConnection methods, keep old ones marked as deprecated for backwards compatibility
|
||||
|
||||
v0.8.2
|
||||
- Introduce support for cancellable async calls
|
||||
- Add getObjectPath() for proxy and object classes
|
||||
- Sanitize names of namespaces/methods/signals/properties/arguments in sdbus-c++-xml2cpp
|
||||
- Fix delivery of signals to multiple proxies subscribed to them
|
||||
- Fix file existence condition in sdbus-c++-xml2cpp
|
||||
- Fix CallData race condition in Proxy::callMethod
|
||||
- Fix integration tests for libsystemd older than 242
|
||||
- Fix installation of public sd-bus headers in internal libsystemd build
|
||||
- Fix integration test cases failing in specific situations
|
||||
- Fix build with clang 9.0.1 and libcxx
|
||||
- Fix potential data race in Proxy's condition variable
|
||||
|
||||
@@ -65,7 +65,7 @@ $ sudo make install
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature (gcc >= 6, clang >= 3.7)
|
||||
* `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.)
|
||||
* `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.
|
||||
|
||||
@@ -38,20 +38,19 @@ ExternalProject_Add(LibsystemdBuildProject
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
|
||||
COMMAND ${MESON} --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR>
|
||||
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR>
|
||||
BUILD_COMMAND ${BUILD_VERSION_H}
|
||||
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
|
||||
BUILD_ALWAYS 1
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS 0
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/systemd <INSTALL_DIR>/include/systemd
|
||||
LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1)
|
||||
|
||||
ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR)
|
||||
set(SYSTEMD_INCLUDE_DIRS ${SOURCE_DIR}/src)
|
||||
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)
|
||||
set(SYSTEMD_LIBRARY_DIRS ${BINARY_DIR})
|
||||
|
||||
set(SYSTEMD_VERSION ${LIBSYSTEMD_VERSION})
|
||||
ExternalProject_Get_property(LibsystemdBuildProject INSTALL_DIR)
|
||||
|
||||
add_library(Systemd::Libsystemd STATIC IMPORTED)
|
||||
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${SYSTEMD_LIBRARY_DIRS}/libsystemd.a)
|
||||
set(SYSTEMD_LIBRARIES Systemd::Libsystemd ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
|
||||
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${BINARY_DIR}/libsystemd.a)
|
||||
file(MAKE_DIRECTORY ${INSTALL_DIR}/include/systemd) # Trick for CMake to stop complaining about non-existent ${INSTALL_DIR}/include directory
|
||||
target_include_directories(Systemd::Libsystemd INTERFACE ${INSTALL_DIR}/include)
|
||||
target_link_libraries(Systemd::Libsystemd INTERFACE ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
|
||||
|
||||
@@ -2,10 +2,3 @@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
|
||||
# This is here for backwards-compatibility. Please use more modern target-based approach.
|
||||
set(SDBUSCPP_VERSION "@SDBUSCPP_VERSION@")
|
||||
set(SDBUSCPP_FOUND "TRUE")
|
||||
set_and_check(SDBUSCPP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
set_and_check(SDBUSCPP_LIBRARY_DIR "@CMAKE_INSTALL_FULL_LIBDIR@")
|
||||
set(SDBUSCPP_LIBRARIES sdbus-c++)
|
||||
|
||||
+18
-10
@@ -26,7 +26,7 @@ Introduction
|
||||
|
||||
sdbus-c++ is a C++ D-Bus library built on top of [sd-bus](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html), a lightweight D-Bus client library implemented within [systemd](https://github.com/systemd/systemd) project. It provides D-Bus functionality on a higher level of abstraction, trying to employ C++ type system to shift as much work as possible from the developer to the compiler.
|
||||
|
||||
sdbus-c++ does not cover the entire sd-bus API, but provides tools for implementing the most common functionality - RPC method calls, signals and properties. There is room for additions and improvements, as needed and when needed.
|
||||
Although sdbus-c++ covers most of sd-bus API, it does not (yet) fully cover every sd-bus API detail. The focus is put on the most widely used functionality: D-Bus connections, object, proxies, synchronous and asynchronous method calls, signals, and properties. If you are missing a desired functionality, you are welcome to submit an issue, or, best, to contribute to sdbus-c++ by submitting a pull request.
|
||||
|
||||
Integrating sdbus-c++ into your project
|
||||
---------------------------------------
|
||||
@@ -51,7 +51,7 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
|
||||
)
|
||||
```
|
||||
|
||||
Note: sdbus-c++ library depends on C++17, since it uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature. To use the library, make sure you have a C++ standard library that supports the feature. The feature is supported by e.g. gcc >= 6, and clang >= 3.7.
|
||||
Note: sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
|
||||
|
||||
Solving libsystemd dependency
|
||||
-----------------------------
|
||||
@@ -135,7 +135,7 @@ The following diagram illustrates the major entities in sdbus-c++.
|
||||
|
||||

|
||||
|
||||
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. A processing loop should be run on the connection.
|
||||
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. An I/O event loop should be run on the bus connection.
|
||||
|
||||
`IObject` represents the concept of an object that exposes its methods, signals and properties. Its responsibilities are:
|
||||
|
||||
@@ -262,8 +262,8 @@ int main(int argc, char *argv[])
|
||||
concatenator->registerSignal(interfaceName, "concatenated", "s");
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
// Run the I/O event loop on the bus connection.
|
||||
connection->enterEventLoop();
|
||||
}
|
||||
```
|
||||
|
||||
@@ -363,14 +363,18 @@ We should bear that in mind when designing more complex, multi-threaded services
|
||||
|
||||
So, more technically, how can we use connections from the server and the client perspective?
|
||||
|
||||
#### Using D-Bus connections on the server side
|
||||
|
||||
On the **server** side, we generally need to create D-Bus objects and publish their APIs. For that we first need a connection with a unique bus name. We need to create the D-Bus connection manually ourselves, request bus name on it, and manually launch its event loop:
|
||||
|
||||
* either in a blocking way, through `enterProcessingLoop()`,
|
||||
* or in a non-blocking async way, through `enterProcessingLoopAsync()`),
|
||||
* or, when we have our own implementation of an event loop (e.g. we are using sd-event event loop), we can ask the connection for its underlying fd and use that fd in our loop.
|
||||
* either in a blocking way, through `enterEventLoop()`,
|
||||
* or in a non-blocking async way, through `enterEventLoopAsync()`,
|
||||
* or, when we have our own implementation of an event loop (e.g. we are using sd-event event loop), we can ask the connection for its underlying fd, I/O events and timeouts through `getEventLoopPollData()` and use that data in our event loop mechanism.
|
||||
|
||||
Of course, at any time before or after running the event loop on the connection, we can create and "hook", as well as remove, objects and proxies upon that connection.
|
||||
|
||||
#### Using D-Bus connections on the client side
|
||||
|
||||
On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory:
|
||||
|
||||
* In case we (the application) already maintain a D-Bus connection, e.g. because we a D-Bus service anyway, the simple and typical approach is to create proxy upon that connection. The proxy will share the connection with others. So we pass connection reference to proxy factory. With this approach we must of course ensure that the connection exists as long as the proxy exists.
|
||||
@@ -381,6 +385,10 @@ On the **client** side we have more options when creating D-Bus proxies. That co
|
||||
|
||||
* Or we don't bother about any connection at all when creating a proxy (the factory overload with no connection parameter). Under the hood, the proxy creates its own *system bus* connection, creates a separate thread and runs an event loop in it. This is **the simplest approach** for non-complex D-Bus clients. For more complex ones, with big number of proxies, this hurts scalability but may improve concurrency (see discussion higher above), so we should make a conscious choice.
|
||||
|
||||
#### Stopping I/O event loops graciously
|
||||
|
||||
A connection with an asynchronous event loop (i.e. one initiated through `enterEventLoopAsync()`) will stop and join its event loop thread automatically in its destructor. An event loop that blocks in the synchronous `enterEventLoop()` call can be unblocked through `leaveEventLoop()` call on the respective bus connection issued from a different thread or from an OS signal handler.
|
||||
|
||||
Implementing the Concatenator example using convenience sdbus-c++ API layer
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
@@ -447,7 +455,7 @@ int main(int argc, char *argv[])
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
connection->enterEventLoop();
|
||||
}
|
||||
```
|
||||
|
||||
@@ -729,7 +737,7 @@ int main(int argc, char *argv[])
|
||||
Concatenator concatenator(*connection, objectPath);
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
connection->enterEventLoop();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -131,6 +131,14 @@ namespace sdbus {
|
||||
getObject().unregister();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getObject().getObjectPath();
|
||||
}
|
||||
|
||||
protected:
|
||||
using base_type = AdaptorInterfaces;
|
||||
};
|
||||
|
||||
@@ -42,13 +42,11 @@ namespace sdbus {
|
||||
class IProxy;
|
||||
class Variant;
|
||||
class Error;
|
||||
class PendingAsyncCall;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
template <typename... _Args>
|
||||
inline constexpr bool are_strings_v = std::conjunction<std::is_convertible<_Args, std::string>...>::value;
|
||||
|
||||
class MethodRegistrator
|
||||
{
|
||||
public:
|
||||
@@ -57,16 +55,11 @@ namespace sdbus {
|
||||
~MethodRegistrator() noexcept(false);
|
||||
|
||||
MethodRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function>
|
||||
std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
|
||||
template <typename _Function>
|
||||
std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
|
||||
template <typename _Function> MethodRegistrator& implementedAs(_Function&& callback);
|
||||
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String>
|
||||
std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> withInputParamNames(_String... paramNames);
|
||||
template <typename... _String> MethodRegistrator& withInputParamNames(_String... paramNames);
|
||||
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String>
|
||||
std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> withOutputParamNames(_String... paramNames);
|
||||
template <typename... _String> MethodRegistrator& withOutputParamNames(_String... paramNames);
|
||||
MethodRegistrator& markAsDeprecated();
|
||||
MethodRegistrator& markAsPrivileged();
|
||||
MethodRegistrator& withNoReply();
|
||||
@@ -94,8 +87,7 @@ namespace sdbus {
|
||||
SignalRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename... _Args> SignalRegistrator& withParameters();
|
||||
template <typename... _Args> SignalRegistrator& withParameters(std::vector<std::string> paramNames);
|
||||
template <typename... _Args, typename... _String>
|
||||
std::enable_if_t<are_strings_v<_String...>, SignalRegistrator&> withParameters(_String... paramNames);
|
||||
template <typename... _Args, typename... _String> SignalRegistrator& withParameters(_String... paramNames);
|
||||
SignalRegistrator& markAsDeprecated();
|
||||
|
||||
private:
|
||||
@@ -202,7 +194,7 @@ namespace sdbus {
|
||||
template <typename _Rep, typename _Period>
|
||||
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename _Function> void uponReplyInvoke(_Function&& callback);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
|
||||
@@ -88,38 +88,7 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&>
|
||||
MethodRegistrator::implementedAs(_Function&& callback)
|
||||
{
|
||||
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple
|
||||
call >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
// For callbacks returning a non-void value, `apply' also returns that value.
|
||||
// For callbacks returning void, `apply' returns an empty tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs); // We don't yet have C++17's std::apply :-(
|
||||
|
||||
// The return value is stored to the reply message.
|
||||
// In case of void functions, ret is an empty tuple and thus nothing is stored.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&>
|
||||
MethodRegistrator::implementedAs(_Function&& callback)
|
||||
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
|
||||
{
|
||||
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
@@ -132,8 +101,22 @@ namespace sdbus {
|
||||
// Deserialize input arguments from the message into the tuple.
|
||||
call >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, typename function_traits<_Function>::async_result_t{std::move(call)}, std::move(inputArgs));
|
||||
if constexpr (!is_async_method_v<_Function>)
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs);
|
||||
|
||||
// Store output arguments to the reply message and send it back.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple and with result object to be set later
|
||||
using AsyncResult = typename function_traits<_Function>::async_result_t;
|
||||
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
|
||||
}
|
||||
};
|
||||
|
||||
return *this;
|
||||
@@ -147,8 +130,10 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> MethodRegistrator::withInputParamNames(_String... paramNames)
|
||||
inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withInputParamNames({paramNames...});
|
||||
}
|
||||
|
||||
@@ -160,8 +145,10 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> MethodRegistrator::withOutputParamNames(_String... paramNames)
|
||||
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withOutputParamNames({paramNames...});
|
||||
}
|
||||
|
||||
@@ -245,9 +232,9 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename... _Args, typename... _String>
|
||||
inline std::enable_if_t<are_strings_v<_String...>, SignalRegistrator&>
|
||||
SignalRegistrator::withParameters(_String... paramNames)
|
||||
inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
|
||||
|
||||
return withParameters<_Args...>({paramNames...});
|
||||
@@ -589,7 +576,7 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
void AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
|
||||
PendingAsyncCall AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
@@ -604,10 +591,10 @@ namespace sdbus {
|
||||
reply >> args;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, error, args); // TODO: Use std::apply when switching to full C++17 support
|
||||
sdbus::apply(callback, error, args);
|
||||
};
|
||||
|
||||
proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
|
||||
return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
|
||||
}
|
||||
|
||||
/*** ---------------- ***/
|
||||
@@ -644,7 +631,7 @@ namespace sdbus {
|
||||
signal >> signalArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
|
||||
sdbus::apply(callback, signalArgs);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace sdbus {
|
||||
* @class IConnection
|
||||
*
|
||||
* An interface to D-Bus bus connection. Incorporates implementation
|
||||
* of both synchronous and asynchronous processing loop.
|
||||
* of both synchronous and asynchronous D-Bus I/O event loop.
|
||||
*
|
||||
* All methods throw sdbus::Error in case of failure. All methods in
|
||||
* this class are thread-aware, but not thread-safe.
|
||||
@@ -82,31 +82,31 @@ namespace sdbus {
|
||||
virtual std::string getUniqueName() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters the D-Bus processing loop
|
||||
* @brief Enters I/O event loop on this bus connection
|
||||
*
|
||||
* The incoming D-Bus messages are processed in the loop. The method
|
||||
* blocks indefinitely, until unblocked via leaveProcessingLoop.
|
||||
* blocks indefinitely, until unblocked through leaveEventLoop().
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void enterProcessingLoop() = 0;
|
||||
virtual void enterEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters the D-Bus processing loop in a separate thread
|
||||
* @brief Enters I/O event loop on this bus connection in a separate thread
|
||||
*
|
||||
* The same as enterProcessingLoop, except that it doesn't block
|
||||
* because it runs the loop in a separate thread managed internally.
|
||||
* The same as enterEventLoop, except that it doesn't block
|
||||
* because it runs the loop in a separate, internally managed thread.
|
||||
*/
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void enterEventLoopAsync() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Leaves the D-Bus processing loop
|
||||
* @brief Leaves the I/O event loop running on this bus connection
|
||||
*
|
||||
* Ends the previously started processing loop.
|
||||
* This causes the loop to exit and frees the thread serving the loop
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
virtual void leaveEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
@@ -120,7 +120,7 @@ namespace sdbus {
|
||||
virtual void addObjectManager(const std::string& objectPath) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns parameters you can pass to poll
|
||||
* @brief Returns fd, I/O events and timeout data you can pass to poll
|
||||
*
|
||||
* To integrate sdbus with your app's own custom event handling system
|
||||
* (without the requirement of an extra thread), you can use this
|
||||
@@ -130,26 +130,31 @@ namespace sdbus {
|
||||
* to process the event. This means that all of sdbus's callbacks will
|
||||
* arrive on your app's main event thread (opposed to on a thread created
|
||||
* by sdbus-c++). If you are unsure what this all means then use
|
||||
* enterProcessingLoop() or enterProcessingLoopAsync() instead.
|
||||
* enterEventLoop() or enterEventLoopAsync() instead.
|
||||
*
|
||||
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
|
||||
* by this method to g_main_context_add_poll.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PollData getProcessLoopPollData() const = 0;
|
||||
virtual PollData getEventLoopPollData() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Process a pending request
|
||||
*
|
||||
* @returns true if an event was processed, false if poll should be called
|
||||
*
|
||||
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
|
||||
* from within this method. This method should ONLY be used in conjuction
|
||||
* with getProcessLoopPollData(). enterProcessingLoop() and
|
||||
* enterProcessingLoopAsync() will call this method for you, so there is no
|
||||
* need to call it when using these. If you are unsure what this all means then
|
||||
* don't use this method.
|
||||
* with getEventLoopPollData().
|
||||
* This method returns true if an I/O message was processed. This you can try
|
||||
* to call this method again before going to poll on I/O events. The method
|
||||
* returns false if no operations were pending, and the caller should then
|
||||
* poll for I/O events before calling this method again.
|
||||
* enterEventLoop() and enterEventLoopAsync() will call this method for you,
|
||||
* so there is no need to call it when using these. If you are unsure what
|
||||
* this all means then don't use this method.
|
||||
*
|
||||
* @returns true if an event was processed
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual bool processPendingRequest() = 0;
|
||||
@@ -184,6 +189,34 @@ namespace sdbus {
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual uint64_t getMethodCallTimeout() const = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterEventLoop()
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoop()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterProcessingLoopAsync()
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoopAsync()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::leaveProcessingLoop()
|
||||
*
|
||||
* @deprecated This function has been replaced by leaveEventLoop()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::getProcessLoopPollData()
|
||||
*
|
||||
* @deprecated This function has been replaced by getEventLoopPollData()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
|
||||
};
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
@@ -193,6 +226,26 @@ namespace sdbus {
|
||||
return setMethodCallTimeout(microsecs.count());
|
||||
}
|
||||
|
||||
inline void IConnection::enterProcessingLoop()
|
||||
{
|
||||
enterEventLoop();
|
||||
}
|
||||
|
||||
inline void IConnection::enterProcessingLoopAsync()
|
||||
{
|
||||
enterEventLoopAsync();
|
||||
}
|
||||
|
||||
inline void IConnection::leaveProcessingLoop()
|
||||
{
|
||||
leaveEventLoop();
|
||||
}
|
||||
|
||||
inline IConnection::PollData IConnection::getProcessLoopPollData() const
|
||||
{
|
||||
return getEventLoopPollData();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
*
|
||||
@@ -200,7 +253,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection with a name
|
||||
@@ -210,7 +263,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
@@ -219,7 +272,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection with a name
|
||||
@@ -229,7 +282,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection
|
||||
@@ -238,7 +291,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection with a name
|
||||
@@ -248,7 +301,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection on a remote host using ssh
|
||||
@@ -258,7 +311,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
MethodRegistrator registerMethod(const std::string& methodName);
|
||||
[[nodiscard]] MethodRegistrator registerMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will provide on D-Bus
|
||||
@@ -377,7 +377,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalRegistrator registerSignal(const std::string& signalName);
|
||||
[[nodiscard]] SignalRegistrator registerSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Registers property that the object will provide on D-Bus
|
||||
@@ -396,7 +396,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertyRegistrator registerProperty(const std::string& propertyName);
|
||||
[[nodiscard]] PropertyRegistrator registerProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets flags (annotations) for a given interface
|
||||
@@ -413,7 +413,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
|
||||
[[nodiscard]] InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
|
||||
|
||||
/*!
|
||||
* @brief Emits signal on D-Bus
|
||||
@@ -434,7 +434,12 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalEmitter emitSignal(const std::string& signalName);
|
||||
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
virtual const std::string& getObjectPath() const = 0;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
@@ -475,14 +480,14 @@ namespace sdbus {
|
||||
* issue signals and provide properties.
|
||||
*
|
||||
* Creating a D-Bus object instance is (thread-)safe even upon the connection
|
||||
* which is already running its processing loop.
|
||||
* which is already running its I/O event loop.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+69
-19
@@ -38,6 +38,10 @@ namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class IConnection;
|
||||
class PendingAsyncCall;
|
||||
namespace internal {
|
||||
class Proxy;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@@ -108,22 +112,23 @@ namespace sdbus {
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @param[in] timeout Timeout for dbus call in microseconds
|
||||
* @return Cookie for the the pending asynchronous call
|
||||
*
|
||||
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
|
||||
* the provided async reply handler will get invoked from the context of the connection
|
||||
* event loop processing thread.
|
||||
* I/O event loop thread.
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
|
||||
virtual PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
||||
@@ -178,7 +183,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
MethodInvoker callMethod(const std::string& methodName);
|
||||
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
||||
@@ -202,7 +207,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
||||
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal handler for a given signal of the proxied D-Bus object
|
||||
@@ -222,7 +227,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalSubscriber uponSignal(const std::string& signalName);
|
||||
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the proxied D-Bus object
|
||||
@@ -241,7 +246,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertyGetter getProperty(const std::string& propertyName);
|
||||
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the proxied D-Bus object
|
||||
@@ -260,7 +265,52 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertySetter setProperty(const std::string& propertyName);
|
||||
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
virtual const std::string& getObjectPath() const = 0;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class PendingAsyncCall
|
||||
*
|
||||
* PendingAsyncCall represents a simple handle type to cancel the delivery
|
||||
* of the asynchronous D-Bus call result to the application.
|
||||
*
|
||||
* The handle is lifetime-independent from the originating Proxy object.
|
||||
* It's safe to call its methods even after the Proxy has gone.
|
||||
*
|
||||
***********************************************/
|
||||
class PendingAsyncCall
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Cancels the delivery of the pending asynchronous call result
|
||||
*
|
||||
* This function effectively removes the callback handler registered to the
|
||||
* async D-Bus method call result delivery. Does nothing if the call was
|
||||
* completed already, or if the originating Proxy object has gone meanwhile.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/*!
|
||||
* @brief Answers whether the asynchronous call is still pending
|
||||
*
|
||||
* @return True if the call is pending, false if the call has been fully completed
|
||||
*
|
||||
* Pending call in this context means a call whose results have not arrived, or
|
||||
* have arrived and are currently being processed by the callback handler.
|
||||
*/
|
||||
bool isPending() const;
|
||||
|
||||
private:
|
||||
friend internal::Proxy;
|
||||
PendingAsyncCall(std::weak_ptr<void> callData);
|
||||
|
||||
private:
|
||||
std::weak_ptr<void> callData_;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
@@ -273,10 +323,10 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline void IProxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
inline PendingAsyncCall IProxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
callMethod(message, std::move(asyncReplyCallback), microsecs.count());
|
||||
return callMethod(message, std::move(asyncReplyCallback), microsecs.count());
|
||||
}
|
||||
|
||||
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
|
||||
@@ -315,7 +365,7 @@ namespace sdbus {
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection. The caller still
|
||||
* remains the owner of the connection (the proxy just keeps a reference to it), and
|
||||
* should make sure that a processing loop is running on that connection, so the proxy
|
||||
* should make sure that an I/O event loop is running on that connection, so the proxy
|
||||
* may receive incoming signals and asynchronous method replies.
|
||||
*
|
||||
* Code example:
|
||||
@@ -323,9 +373,9 @@ namespace sdbus {
|
||||
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
@@ -346,9 +396,9 @@ namespace sdbus {
|
||||
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
@@ -367,8 +417,8 @@ namespace sdbus {
|
||||
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath );
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -55,11 +55,7 @@ namespace sdbus {
|
||||
|
||||
// Assume the caller has already obtained message ownership
|
||||
struct adopt_message_t { explicit adopt_message_t() = default; };
|
||||
#ifdef __cpp_inline_variables
|
||||
inline constexpr adopt_message_t adopt_message{};
|
||||
#else
|
||||
constexpr adopt_message_t adopt_message{};
|
||||
#endif
|
||||
|
||||
/********************************************//**
|
||||
* @class Message
|
||||
@@ -75,7 +71,7 @@ namespace sdbus {
|
||||
* of @c IObject and @c IProxy.
|
||||
*
|
||||
***********************************************/
|
||||
class Message
|
||||
class [[nodiscard]] Message
|
||||
{
|
||||
public:
|
||||
Message& operator<<(bool item);
|
||||
@@ -166,11 +162,7 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
|
||||
#ifdef __cpp_inline_variables
|
||||
inline constexpr dont_request_slot_t dont_request_slot{};
|
||||
#else
|
||||
constexpr dont_request_slot_t dont_request_slot{};
|
||||
#endif
|
||||
|
||||
class MethodCall : public Message
|
||||
{
|
||||
@@ -283,10 +275,7 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
void serialize_pack(Message& msg, _Args&&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg << std::forward<_Args>(args)), 0)...};
|
||||
(void)(msg << ... << args);
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
@@ -378,10 +367,7 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
void deserialize_pack(Message& msg, _Args&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg >> args), 0)...};
|
||||
(void)(msg >> ... >> args);
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
|
||||
@@ -76,12 +76,7 @@ namespace sdbus {
|
||||
{
|
||||
assert(call_.isValid());
|
||||
auto reply = call_.createReply();
|
||||
#ifdef __cpp_fold_expressions
|
||||
(reply << ... << results);
|
||||
#else
|
||||
using _ = std::initializer_list<int>;
|
||||
(void)_{(void(reply << results), 0)...};
|
||||
#endif
|
||||
reply.send();
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +165,14 @@ namespace sdbus {
|
||||
getProxy().unregister();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getProxy().getObjectPath();
|
||||
}
|
||||
|
||||
protected:
|
||||
using base_type = ProxyInterfaces;
|
||||
};
|
||||
|
||||
@@ -245,11 +245,9 @@ namespace sdbus {
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<_ValueTypes>::str()...};
|
||||
std::string signature;
|
||||
signature += "(";
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
(signature += ... += signature_of<_ValueTypes>::str());
|
||||
signature += ")";
|
||||
return signature;
|
||||
}
|
||||
@@ -489,11 +487,8 @@ namespace sdbus {
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
// TODO: This could be a fold expression in C++17...
|
||||
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
||||
std::string signature;
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
(void)(signature += ... += signature_of<std::decay_t<_Types>>::str());
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
@@ -536,27 +531,17 @@ namespace sdbus {
|
||||
return std::forward<_Function>(f)(e, std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning non-void values.
|
||||
// In this case just forward function return value.
|
||||
// For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
|
||||
// For void-returning functions, apply_impl returns an empty tuple.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<!std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning void.
|
||||
// In this case, to have uniform code on the caller side, return empty tuple, our synonym for `void'.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
{
|
||||
std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
return std::tuple<>{};
|
||||
if constexpr (!std::is_void_v<function_result_t<_Function>>)
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
else
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...), std::tuple<>{};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,11 +178,7 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
struct adopt_fd_t { explicit adopt_fd_t() = default; };
|
||||
#ifdef __cpp_inline_variables
|
||||
inline constexpr adopt_fd_t adopt_fd{};
|
||||
#else
|
||||
constexpr adopt_fd_t adopt_fd{};
|
||||
#endif
|
||||
|
||||
/********************************************//**
|
||||
* @struct UnixFd
|
||||
|
||||
+19
-17
@@ -35,7 +35,7 @@
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory)
|
||||
: iface_(std::move(interface))
|
||||
@@ -61,7 +61,7 @@ Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t,
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
leaveProcessingLoop();
|
||||
Connection::leaveEventLoop();
|
||||
}
|
||||
|
||||
void Connection::requestName(const std::string& name)
|
||||
@@ -84,7 +84,7 @@ std::string Connection::getUniqueName() const
|
||||
return unique;
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoop()
|
||||
void Connection::enterEventLoop()
|
||||
{
|
||||
loopThreadId_ = std::this_thread::get_id();
|
||||
|
||||
@@ -98,25 +98,25 @@ void Connection::enterProcessingLoop()
|
||||
|
||||
auto success = waitForNextRequest();
|
||||
if (!success)
|
||||
break; // Exit processing loop
|
||||
break; // Exit I/O event loop
|
||||
}
|
||||
|
||||
loopThreadId_ = std::thread::id{};
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoopAsync()
|
||||
void Connection::enterEventLoopAsync()
|
||||
{
|
||||
if (!asyncLoopThread_.joinable())
|
||||
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); });
|
||||
asyncLoopThread_ = std::thread([this](){ enterEventLoop(); });
|
||||
}
|
||||
|
||||
void Connection::leaveProcessingLoop()
|
||||
void Connection::leaveEventLoop()
|
||||
{
|
||||
notifyProcessingLoopToExit();
|
||||
joinWithProcessingLoop();
|
||||
notifyEventLoopToExit();
|
||||
joinWithEventLoop();
|
||||
}
|
||||
|
||||
sdbus::IConnection::PollData Connection::getProcessLoopPollData() const
|
||||
Connection::PollData Connection::getEventLoopPollData() const
|
||||
{
|
||||
ISdBus::PollData pollData;
|
||||
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &pollData);
|
||||
@@ -358,13 +358,13 @@ void Connection::finishHandshake(sd_bus* bus)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoopToExit()
|
||||
void Connection::notifyEventLoopToExit()
|
||||
{
|
||||
assert(loopExitFd_.fd >= 0);
|
||||
|
||||
uint64_t value = 1;
|
||||
auto r = write(loopExitFd_.fd, &value, sizeof(value));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify event loop", -errno);
|
||||
}
|
||||
|
||||
void Connection::clearExitNotification()
|
||||
@@ -374,7 +374,7 @@ void Connection::clearExitNotification()
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
|
||||
}
|
||||
|
||||
void Connection::joinWithProcessingLoop()
|
||||
void Connection::joinWithEventLoop()
|
||||
{
|
||||
if (asyncLoopThread_.joinable())
|
||||
asyncLoopThread_.join();
|
||||
@@ -398,7 +398,7 @@ bool Connection::waitForNextRequest()
|
||||
assert(bus != nullptr);
|
||||
assert(loopExitFd_.fd != 0);
|
||||
|
||||
auto sdbusPollData = getProcessLoopPollData();
|
||||
auto sdbusPollData = getEventLoopPollData();
|
||||
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_.fd, POLLIN, 0}};
|
||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
|
||||
@@ -454,11 +454,10 @@ Connection::LoopExitEventFd::~LoopExitEventFd()
|
||||
close(fd);
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
namespace sdbus::internal {
|
||||
|
||||
namespace internal {
|
||||
std::unique_ptr<sdbus::internal::IConnection> createConnection()
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
@@ -466,8 +465,11 @@ std::unique_ptr<sdbus::internal::IConnection> createConnection()
|
||||
auto connectionInternal = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createConnection()
|
||||
{
|
||||
return createSystemBusConnection();
|
||||
|
||||
+9
-9
@@ -40,9 +40,9 @@
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Connection
|
||||
class Connection final
|
||||
: public sdbus::IConnection // External, public interface
|
||||
, public sdbus::internal::IConnection // Internal, private interface
|
||||
{
|
||||
@@ -60,10 +60,10 @@ namespace sdbus { namespace internal {
|
||||
void requestName(const std::string& name) override;
|
||||
void releaseName(const std::string& name) override;
|
||||
std::string getUniqueName() const override;
|
||||
void enterProcessingLoop() override;
|
||||
void enterProcessingLoopAsync() override;
|
||||
void leaveProcessingLoop() override;
|
||||
sdbus::IConnection::PollData getProcessLoopPollData() const override;
|
||||
void enterEventLoop() override;
|
||||
void enterEventLoopAsync() override;
|
||||
void leaveEventLoop() override;
|
||||
PollData getEventLoopPollData() const override;
|
||||
bool processPendingRequest() override;
|
||||
|
||||
void addObjectManager(const std::string& objectPath) override;
|
||||
@@ -118,9 +118,9 @@ namespace sdbus { namespace internal {
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
void notifyProcessingLoopToExit();
|
||||
void notifyEventLoopToExit();
|
||||
void clearExitNotification();
|
||||
void joinWithProcessingLoop();
|
||||
void joinWithEventLoop();
|
||||
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
|
||||
|
||||
struct LoopExitEventFd
|
||||
@@ -139,6 +139,6 @@ namespace sdbus { namespace internal {
|
||||
LoopExitEventFd loopExitFd_;
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
|
||||
|
||||
+14
-16
@@ -44,8 +44,7 @@ namespace sdbus {
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
using SlotPtr = std::unique_ptr<void, std::function<void(void*)>>;
|
||||
|
||||
@@ -57,10 +56,10 @@ namespace internal {
|
||||
virtual const ISdBus& getSdBusInterface() const = 0;
|
||||
virtual ISdBus& getSdBusInterface() = 0;
|
||||
|
||||
virtual SlotPtr addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) = 0;
|
||||
[[nodiscard]] virtual SlotPtr addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) = 0;
|
||||
|
||||
virtual PlainMessage createPlainMessage() const = 0;
|
||||
virtual MethodCall createMethodCall( const std::string& destination
|
||||
@@ -81,23 +80,22 @@ namespace internal {
|
||||
virtual void emitInterfacesRemovedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces ) = 0;
|
||||
|
||||
virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
|
||||
[[nodiscard]] virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
|
||||
|
||||
virtual SlotPtr registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
[[nodiscard]] virtual SlotPtr registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
virtual void enterEventLoopAsync() = 0;
|
||||
virtual void leaveEventLoop() = 0;
|
||||
|
||||
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> createConnection();
|
||||
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class ISdBus
|
||||
{
|
||||
@@ -84,6 +84,6 @@ namespace sdbus { namespace internal {
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#endif //SDBUS_CXX_ISDBUS_H
|
||||
|
||||
+7
-2
@@ -37,7 +37,7 @@
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
||||
: connection_(connection), objectPath_(std::move(objectPath))
|
||||
@@ -225,6 +225,11 @@ sdbus::IConnection& Object::getConnection() const
|
||||
return dynamic_cast<sdbus::IConnection&>(connection_);
|
||||
}
|
||||
|
||||
const std::string& Object::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable;
|
||||
@@ -391,7 +396,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
|
||||
+2
-3
@@ -37,8 +37,7 @@
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Object
|
||||
: public IObject
|
||||
@@ -102,6 +101,7 @@ namespace internal {
|
||||
bool hasObjectManager() const override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
const std::string& getObjectPath() const override;
|
||||
|
||||
private:
|
||||
using InterfaceName = std::string;
|
||||
@@ -173,6 +173,5 @@ namespace internal {
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
|
||||
|
||||
+54
-14
@@ -36,7 +36,7 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
|
||||
@@ -56,7 +56,7 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
{
|
||||
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
|
||||
// in order that we get and process signals, async call replies, and other messages from D-Bus.
|
||||
connection_->enterProcessingLoopAsync();
|
||||
connection_->enterEventLoopAsync();
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||
@@ -93,16 +93,20 @@ MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
return sendMethodCallMessageAndWaitForReply(message, timeout);
|
||||
}
|
||||
|
||||
void Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
|
||||
PendingAsyncCall Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
|
||||
auto callData = std::make_unique<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}});
|
||||
auto callData = std::make_shared<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}});
|
||||
auto weakData = std::weak_ptr<AsyncCalls::CallData>{callData};
|
||||
|
||||
callData->slot = message.send(callback, callData.get(), timeout);
|
||||
|
||||
pendingAsyncCalls_.addCall(callData->slot.get(), std::move(callData));
|
||||
auto slotPtr = callData->slot.get();
|
||||
pendingAsyncCalls_.addCall(slotPtr, std::move(callData));
|
||||
|
||||
return {weakData};
|
||||
}
|
||||
|
||||
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
|
||||
@@ -123,8 +127,8 @@ MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& messag
|
||||
|
||||
void Proxy::SyncCallReplyData::sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error)
|
||||
{
|
||||
SCOPE_EXIT{ cond_.notify_one(); };
|
||||
std::unique_lock<std::mutex> lock{mutex_};
|
||||
std::unique_lock lock{mutex_};
|
||||
SCOPE_EXIT{ cond_.notify_one(); }; // This must happen before unlocking the mutex to avoid potential data race on spurious wakeup in the waiting thread
|
||||
SCOPE_EXIT{ arrived_ = true; };
|
||||
|
||||
//error_ = nullptr; // Necessary if SyncCallReplyData instance is thread_local
|
||||
@@ -137,7 +141,7 @@ void Proxy::SyncCallReplyData::sendMethodReplyToWaitingThread(MethodReply& reply
|
||||
|
||||
MethodReply Proxy::SyncCallReplyData::waitForMethodReply()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{mutex_};
|
||||
std::unique_lock lock{mutex_};
|
||||
cond_.wait(lock, [this](){ return arrived_; });
|
||||
|
||||
//arrived_ = false; // Necessary if SyncCallReplyData instance is thread_local
|
||||
@@ -194,19 +198,27 @@ void Proxy::unregister()
|
||||
interfaces_.clear();
|
||||
}
|
||||
|
||||
const std::string& Proxy::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
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);
|
||||
assert(asyncCallData->callback);
|
||||
auto& proxy = asyncCallData->proxy;
|
||||
auto slot = asyncCallData->slot.get();
|
||||
|
||||
// We are removing the CallData item at the complete scope exit, after the callback has been invoked.
|
||||
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
|
||||
// is the synchronization point between callback invocation and Proxy::unregister.
|
||||
SCOPE_EXIT
|
||||
{
|
||||
// Slot may be null if we're doing blocking synchronous call implemented by means of asynchronous call,
|
||||
// because in that case the call data is still alive on the stack, we don't need to manage it separately.
|
||||
if (asyncCallData->slot)
|
||||
proxy.pendingAsyncCalls_.removeCall(asyncCallData->slot.get());
|
||||
// Remove call meta-data if it's a real async call (a sync call done in terms of async has slot == nullptr)
|
||||
if (slot)
|
||||
proxy.pendingAsyncCalls_.removeCall(slot);
|
||||
};
|
||||
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
|
||||
@@ -238,10 +250,38 @@ int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd
|
||||
|
||||
callback(message);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callData)
|
||||
: callData_(std::move(callData))
|
||||
{
|
||||
}
|
||||
|
||||
void PendingAsyncCall::cancel()
|
||||
{
|
||||
if (auto ptr = callData_.lock(); ptr != nullptr)
|
||||
{
|
||||
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
|
||||
callData->proxy.pendingAsyncCalls_.removeCall(callData->slot.get());
|
||||
|
||||
// At this point, the callData item is being deleted, leading to the release of the
|
||||
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
|
||||
// callback is currently being processed, the sd-bus mutex is locked by the event
|
||||
// loop thread, thus access to the callData item is synchronized and thread-safe.
|
||||
}
|
||||
}
|
||||
|
||||
bool PendingAsyncCall::isPending() const
|
||||
{
|
||||
return !callData_.expired();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
|
||||
+29
-13
@@ -37,8 +37,7 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Proxy
|
||||
: public IProxy
|
||||
@@ -53,7 +52,7 @@ namespace internal {
|
||||
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
|
||||
void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
|
||||
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
@@ -61,6 +60,8 @@ namespace internal {
|
||||
void finishRegistration() override;
|
||||
void unregister() override;
|
||||
|
||||
const std::string& getObjectPath() const override;
|
||||
|
||||
private:
|
||||
class SyncCallReplyData
|
||||
{
|
||||
@@ -82,6 +83,8 @@ namespace internal {
|
||||
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
friend PendingAsyncCall;
|
||||
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
@@ -120,33 +123,46 @@ namespace internal {
|
||||
clear();
|
||||
}
|
||||
|
||||
bool addCall(void* slot, std::unique_ptr<CallData>&& asyncCallData)
|
||||
bool addCall(void* slot, std::shared_ptr<CallData> asyncCallData)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::lock_guard lock(mutex_);
|
||||
return calls_.emplace(slot, std::move(asyncCallData)).second;
|
||||
}
|
||||
|
||||
bool removeCall(void* slot)
|
||||
void removeCall(void* slot)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return calls_.erase(slot) > 0;
|
||||
std::unique_lock lock(mutex_);
|
||||
if (auto it = calls_.find(slot); it != calls_.end())
|
||||
{
|
||||
auto callData = std::move(it->second);
|
||||
calls_.erase(it);
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
|
||||
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
std::unique_lock lock(mutex_);
|
||||
auto asyncCallSlots = std::move(calls_);
|
||||
// Perform releasing of sd-bus slots outside of the calls_ critical section which avoids
|
||||
// double mutex dead lock when the async reply handler is invoked at the same time.
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
|
||||
// and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<void*, std::unique_ptr<CallData>> calls_;
|
||||
std::unordered_map<void*, std::shared_ptr<CallData>> calls_;
|
||||
std::mutex mutex_;
|
||||
} pendingAsyncCalls_;
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
|
||||
|
||||
+28
-28
@@ -28,74 +28,74 @@
|
||||
#include "SdBus.h"
|
||||
#include <sdbus-c++/Error.h>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_ref(m);
|
||||
}
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_unref(m);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_send(bus, m, cookie);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_call(bus, m, usec, ret_error, reply);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new(bus, m, type);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_signal(bus, m, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_return(call, m);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_error(call, m, e);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message
|
||||
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_set_method_call_timeout(bus, usec);
|
||||
#else
|
||||
@@ -116,7 +116,7 @@ int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
|
||||
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_get_method_call_timeout(bus, ret);
|
||||
#else
|
||||
@@ -128,35 +128,35 @@ int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
|
||||
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_properties_changed_strv(bus, path, interface, names);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_object_added(sd_bus *bus, const char *path)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_object_added(bus, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_object_removed(sd_bus *bus, const char *path)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_object_removed(bus, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
|
||||
}
|
||||
@@ -178,62 +178,62 @@ int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
|
||||
|
||||
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_request_name(bus, name, flags);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_release_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
return ::sd_bus_get_unique_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::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)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_object_manager(bus, slot, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return :: sd_bus_add_match(bus, slot, match, callback, userdata);
|
||||
}
|
||||
|
||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_slot_unref(slot);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_process(bus, r);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r = ::sd_bus_get_fd(bus);
|
||||
if (r < 0)
|
||||
@@ -260,4 +260,4 @@ sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)
|
||||
return ::sd_bus_flush_close_unref(bus);
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@
|
||||
#include "ISdBus.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class SdBus final : public ISdBus
|
||||
{
|
||||
@@ -79,6 +79,6 @@ private:
|
||||
std::recursive_mutex sdbusMutex_;
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#endif //SDBUS_C_SDBUS_H
|
||||
|
||||
+22
-33
@@ -1,37 +1,29 @@
|
||||
#-------------------------------
|
||||
# DOWNLOAD AND BUILD OF GOOGLETEST
|
||||
# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
|
||||
#-------------------------------
|
||||
|
||||
configure_file(googletest-download/CMakeLists.txt.in googletest-download/CMakeLists.txt)
|
||||
include(FetchContent)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
|
||||
message("Fetching googletest...")
|
||||
FetchContent_Declare(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND "")
|
||||
|
||||
if(result)
|
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||
#FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually:
|
||||
FetchContent_GetProperties(googletest)
|
||||
if(NOT googletest_POPULATED)
|
||||
FetchContent_Populate(googletest)
|
||||
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
|
||||
|
||||
if(result)
|
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
|
||||
|
||||
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
|
||||
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
|
||||
EXCLUDE_FROM_ALL)
|
||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
|
||||
|
||||
#-------------------------------
|
||||
# SOURCE FILES CONFIGURATION
|
||||
#-------------------------------
|
||||
@@ -84,15 +76,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# BUILD INFORMATION
|
||||
#----------------------------------
|
||||
|
||||
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS} $<TARGET_OBJECTS:sdbus-c++-objlib>)
|
||||
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
|
||||
target_include_directories(sdbus-c++-unit-tests PRIVATE ${SYSTEMD_INCLUDE_DIRS}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(sdbus-c++-unit-tests ${SYSTEMD_LIBRARIES} gmock gmock_main)
|
||||
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib gmock gmock_main)
|
||||
|
||||
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
|
||||
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ gmock gmock_main)
|
||||
|
||||
# Manual performance and stress tests
|
||||
|
||||
@@ -62,16 +62,17 @@ public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
s_connection->requestName(INTERFACE_NAME);
|
||||
s_connection->enterProcessingLoopAsync();
|
||||
s_connection->enterEventLoopAsync();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
s_connection->leaveProcessingLoop();
|
||||
s_connection->leaveEventLoop();
|
||||
s_connection->releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 5s)
|
||||
template <typename _Fnc>
|
||||
static bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = 5s)
|
||||
{
|
||||
std::chrono::milliseconds elapsed{};
|
||||
std::chrono::milliseconds step{5ms};
|
||||
@@ -80,11 +81,16 @@ public:
|
||||
elapsed += step;
|
||||
if (elapsed > timeout)
|
||||
return false;
|
||||
} while (!flag);
|
||||
} while (!fnc());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 5s)
|
||||
{
|
||||
return waitUntil([&flag]() -> bool { return flag; }, timeout);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetUp() override
|
||||
{
|
||||
@@ -110,6 +116,8 @@ std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus
|
||||
|
||||
}
|
||||
|
||||
using SdbusTestObject = AdaptorAndProxyFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
@@ -127,8 +135,6 @@ TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
|
||||
// Methods
|
||||
|
||||
using SdbusTestObject = AdaptorAndProxyFixture;
|
||||
|
||||
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
|
||||
@@ -210,7 +216,7 @@ TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
{
|
||||
auto resObjectPath = m_proxy->getObjectPath();
|
||||
auto resObjectPath = m_proxy->getObjPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
@@ -385,6 +391,51 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
{
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
|
||||
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
|
||||
auto call = m_proxy->doOperationClientSideAsync(0);
|
||||
(void) future.get(); // Wait for the call to finish
|
||||
|
||||
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
@@ -455,6 +506,18 @@ TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestingProxy>(*s_connection, INTERFACE_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestingProxy>(*s_connection, INTERFACE_NAME, OBJECT_PATH);
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
{
|
||||
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
|
||||
@@ -622,7 +685,20 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
|
||||
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
|
||||
#if LIBSYSTEMD_VERSION<=244
|
||||
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
@@ -639,7 +715,20 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3)); // 3 properties under INTERFACE_NAME
|
||||
#if LIBSYSTEMD_VERSION<=244
|
||||
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ using ::testing::Eq;
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::createConnection());
|
||||
ASSERT_NO_THROW(auto con = sdbus::createConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestRegisteredDbusName)
|
||||
@@ -78,13 +78,13 @@ TEST(Connection, CannotReleaseNonrequestedName)
|
||||
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveProcessingLoop)
|
||||
TEST(Connection, CanEnterAndLeaveEventLoop)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
|
||||
std::thread t([&](){ connection->enterProcessingLoop(); });
|
||||
connection->leaveProcessingLoop();
|
||||
std::thread t([&](){ connection->enterEventLoop(); });
|
||||
connection->leaveEventLoop();
|
||||
|
||||
t.join();
|
||||
|
||||
|
||||
@@ -50,32 +50,32 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
void noArgNoReturn() const
|
||||
void noArgNoReturn() const override
|
||||
{
|
||||
}
|
||||
|
||||
int32_t getInt() const
|
||||
int32_t getInt() const override
|
||||
{
|
||||
return INT32_VALUE;
|
||||
}
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple() const
|
||||
std::tuple<uint32_t, std::string> getTuple() const override
|
||||
{
|
||||
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
|
||||
}
|
||||
|
||||
double multiply(const int64_t& a, const double& b) const
|
||||
double multiply(const int64_t& a, const double& b) const override
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b) const
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b) const override
|
||||
{
|
||||
m_multiplyResult = a * b;
|
||||
m_wasMultiplyCalled = true;
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const override
|
||||
{
|
||||
std::vector<int16_t> res{x.get<1>()};
|
||||
auto y = std::get<std::vector<int16_t>>(x);
|
||||
@@ -83,13 +83,13 @@ protected:
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(sdbus::Variant& v)
|
||||
sdbus::Variant processVariant(sdbus::Variant& v) override
|
||||
{
|
||||
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const override
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> res;
|
||||
for (auto item : x)
|
||||
@@ -99,12 +99,12 @@ protected:
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const override
|
||||
{
|
||||
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) override
|
||||
{
|
||||
int32_t res{0};
|
||||
res += std::get<0>(a) + std::get<1>(a);
|
||||
@@ -112,7 +112,7 @@ protected:
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) override
|
||||
{
|
||||
uint32_t res{0};
|
||||
for (auto x : a)
|
||||
@@ -126,13 +126,13 @@ protected:
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t doOperation(uint32_t param)
|
||||
uint32_t doOperation(uint32_t param) override
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
return param;
|
||||
}
|
||||
|
||||
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result)
|
||||
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) override
|
||||
{
|
||||
if (param == 0)
|
||||
{
|
||||
@@ -150,20 +150,20 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature() const
|
||||
sdbus::Signature getSignature() const override
|
||||
{
|
||||
return SIGNATURE_VALUE;
|
||||
}
|
||||
sdbus::ObjectPath getObjectPath() const
|
||||
sdbus::ObjectPath getObjPath() const override
|
||||
{
|
||||
return OBJECT_PATH_VALUE;
|
||||
}
|
||||
sdbus::UnixFd getUnixFd() const
|
||||
sdbus::UnixFd getUnixFd() const override
|
||||
{
|
||||
return sdbus::UnixFd{UNIX_FD_VALUE};
|
||||
}
|
||||
|
||||
ComplexType getComplex() const
|
||||
ComplexType getComplex() const override
|
||||
{
|
||||
return { // map
|
||||
{
|
||||
@@ -191,7 +191,7 @@ protected:
|
||||
};
|
||||
}
|
||||
|
||||
void throwError() const
|
||||
void throwError() const override
|
||||
{
|
||||
m_wasThrowErrorCalled = true;
|
||||
throw sdbus::createError(1, "A test error occurred");
|
||||
@@ -204,27 +204,27 @@ protected:
|
||||
emitSignalWithMap({});
|
||||
}
|
||||
|
||||
std::string state()
|
||||
std::string state() override
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
uint32_t action()
|
||||
uint32_t action() override
|
||||
{
|
||||
return m_action;
|
||||
}
|
||||
|
||||
void action(const uint32_t& value)
|
||||
void action(const uint32_t& value) override
|
||||
{
|
||||
m_action = value;
|
||||
}
|
||||
|
||||
bool blocking()
|
||||
bool blocking() override
|
||||
{
|
||||
return m_blocking;
|
||||
}
|
||||
|
||||
void blocking(const bool& value)
|
||||
void blocking(const bool& value) override
|
||||
{
|
||||
m_blocking = value;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ public:
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
TestingProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
~TestingProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
|
||||
@@ -97,7 +97,7 @@ protected:
|
||||
});
|
||||
|
||||
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
||||
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
||||
object_.registerMethod("getObjPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjPath(); });
|
||||
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getUnixFd(); });
|
||||
|
||||
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
|
||||
@@ -111,7 +111,7 @@ protected:
|
||||
|
||||
// registration of signals is optional, it is useful because of introspection
|
||||
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
|
||||
// Note: sd-bus of libsystemd up to v244 has a bug where it doesn't generate signal parameter names in introspection XML. Signal param names commented temporarily.
|
||||
// Note: sd-bus of libsystemd up to (including) v244 has a bug where it doesn't generate signal parameter names in introspection XML. Signal param names commented temporarily.
|
||||
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>(/*"aMap"*/);
|
||||
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>(/*"aVariant"*/);
|
||||
|
||||
@@ -168,7 +168,7 @@ protected:
|
||||
virtual uint32_t doOperation(uint32_t param) = 0;
|
||||
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
|
||||
virtual sdbus::Signature getSignature() const = 0;
|
||||
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
||||
virtual sdbus::ObjectPath getObjPath() const = 0;
|
||||
virtual sdbus::UnixFd getUnixFd() const = 0;
|
||||
virtual ComplexType getComplex() const = 0;
|
||||
virtual void throwError() const = 0;
|
||||
@@ -251,19 +251,35 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
|
||||
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
</method>
|
||||
<method name="getInt">
|
||||
<arg type="i" name="anInt" direction="out"/>
|
||||
<method name="getInt">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="i" name="anInt" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="i" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="getInts16FromStruct">
|
||||
<arg type="(yndsan)" direction="in"/>
|
||||
<arg type="an" direction="out"/>
|
||||
</method>
|
||||
<method name="getMapOfVariants">
|
||||
<method name="getMapOfVariants">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="ai" name="x" direction="in"/>
|
||||
<arg type="(vv)" name="y" direction="in"/>
|
||||
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>
|
||||
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="ai" direction="in"/>
|
||||
<arg type="(vv)" direction="in"/>
|
||||
<arg type="a{iv}" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="getObjectPath">
|
||||
<method name="getObjPath">
|
||||
<arg type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="getSignature">
|
||||
@@ -279,10 +295,19 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
|
||||
<method name="getUnixFd">
|
||||
<arg type="h" direction="out"/>
|
||||
</method>
|
||||
<method name="multiply">
|
||||
<method name="multiply">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="x" name="a" direction="in"/>
|
||||
<arg type="d" name="b" direction="in"/>
|
||||
<arg type="d" name="result" direction="out"/>
|
||||
<arg type="d" name="result" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="x" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
<arg type="d" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="multiplyWithNoReply">
|
||||
<arg type="x" direction="in"/>
|
||||
|
||||
@@ -156,15 +156,15 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
void doOperationClientSideAsync(uint32_t param)
|
||||
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param)
|
||||
{
|
||||
object_.callMethodAsync("doOperation")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
});
|
||||
return object_.callMethodAsync("doOperation")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
});
|
||||
}
|
||||
|
||||
void doErroneousOperationClientSideAsync()
|
||||
@@ -197,10 +197,10 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::ObjectPath getObjectPath()
|
||||
sdbus::ObjectPath getObjPath()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
object_.callMethod("getObjectPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
object_.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,5 +97,5 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
const char* objectPath = "/org/sdbuscpp/perftests";
|
||||
PerftestAdaptor server(*connection, objectPath);
|
||||
|
||||
connection->enterProcessingLoop();
|
||||
connection->enterEventLoop();
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ protected:
|
||||
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
|
||||
|
||||
public:
|
||||
void concatenate(const std::map<std::string, sdbus::Variant>& params)
|
||||
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
|
||||
{
|
||||
proxy_.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
|
||||
return proxy_.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -392,7 +392,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
|
||||
service2ThreadReady = true;
|
||||
con.enterProcessingLoop();
|
||||
con.enterEventLoop();
|
||||
});
|
||||
|
||||
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
|
||||
@@ -402,7 +402,7 @@ int main(int argc, char *argv[])
|
||||
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
|
||||
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH, false);
|
||||
service1ThreadReady = true;
|
||||
con.enterProcessingLoop();
|
||||
con.enterEventLoop();
|
||||
});
|
||||
|
||||
// Wait for both services to export their D-Bus objects
|
||||
@@ -480,8 +480,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// We could run the loop in a sync way, but we want it to run also when proxies are destroyed for better
|
||||
// coverage of multi-threaded scenarios, so we run it async and use condition variable for exit notification
|
||||
//con.enterProcessingLoop();
|
||||
con.enterProcessingLoopAsync();
|
||||
//con.enterEventLoop();
|
||||
con.enterEventLoopAsync();
|
||||
|
||||
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
|
||||
clientThreadExitCond.wait(lock, [&]{return clientThreadExit;});
|
||||
@@ -493,17 +493,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(loopDuration));
|
||||
|
||||
//clientConnection->leaveProcessingLoop();
|
||||
//clientConnection->leaveEventLoop();
|
||||
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
|
||||
clientThreadExit = true;
|
||||
lock.unlock();
|
||||
clientThreadExitCond.notify_one();
|
||||
clientThread.join();
|
||||
|
||||
service1Connection->leaveProcessingLoop();
|
||||
service1Connection->leaveEventLoop();
|
||||
service1Thread.join();
|
||||
|
||||
service2Connection->leaveProcessingLoop();
|
||||
service2Connection->leaveEventLoop();
|
||||
service2Thread.join();
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ TEST(AVariant, CanBeConstructedFromAComplexValue)
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant(value));
|
||||
ASSERT_NO_THROW(sdbus::Variant{value});
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeCopied)
|
||||
|
||||
@@ -164,6 +164,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto methodName = method->get("name");
|
||||
auto methodNameSafe = mangle_name(methodName);
|
||||
|
||||
auto annotations = getAnnotations(*method);
|
||||
bool async{false};
|
||||
@@ -219,7 +220,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
<< "[this]("
|
||||
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argTypeStr
|
||||
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "("
|
||||
<< "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
|
||||
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argStr << "); })"
|
||||
<< annotationRegistration << ";" << endl;
|
||||
@@ -227,7 +228,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
declarationSS << tab
|
||||
<< "virtual "
|
||||
<< (async ? "void" : outArgsToType(outArgs))
|
||||
<< " " << methodName
|
||||
<< " " << methodNameSafe
|
||||
<< "("
|
||||
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argTypeStr
|
||||
@@ -279,6 +280,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
|
||||
|
||||
auto nameWithCapFirstLetter = name;
|
||||
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
|
||||
nameWithCapFirstLetter = mangle_name(nameWithCapFirstLetter);
|
||||
|
||||
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl
|
||||
@@ -305,6 +307,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyNameSafe = mangle_name(propertyName);
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
@@ -336,23 +339,23 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
registrationSS << ".withGetter([this](){ return this->" << propertyName << "(); })";
|
||||
registrationSS << ".withGetter([this](){ return this->" << propertyNameSafe << "(); })";
|
||||
}
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
registrationSS
|
||||
<< ".withSetter([this](" << propertyTypeArg << ")"
|
||||
"{ this->" << propertyName << "(" << propertyArg << "); })";
|
||||
"{ this->" << propertyNameSafe << "(" << propertyArg << "); })";
|
||||
}
|
||||
|
||||
registrationSS << annotationRegistration;
|
||||
registrationSS << ";" << endl;
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
declarationSS << tab << "virtual " << propertyType << " " << propertyName << "() = 0;" << endl;
|
||||
declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl;
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
declarationSS << tab << "virtual void " << propertyName << "(" << propertyTypeArg << ") = 0;" << endl;
|
||||
declarationSS << tab << "virtual void " << propertyNameSafe << "(" << propertyTypeArg << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
|
||||
@@ -96,6 +96,7 @@ std::tuple<unsigned, std::string> BaseGenerator::generateNamespaces(const std::s
|
||||
{
|
||||
std::string nspace;
|
||||
getline(ss, nspace, '.');
|
||||
nspace = mangle_name(nspace);
|
||||
body << "namespace " << nspace << " {" << endl;
|
||||
++count;
|
||||
}
|
||||
@@ -125,17 +126,18 @@ std::tuple<std::string, std::string, std::string, std::string> BaseGenerator::ar
|
||||
{
|
||||
argName = "arg" + std::to_string(i);
|
||||
}
|
||||
auto argNameSafe = mangle_name(argName);
|
||||
auto type = signature_to_type(arg->get("type"));
|
||||
argStringsSS << "\"" << argName << "\"";
|
||||
if (!async)
|
||||
{
|
||||
argSS << argName;
|
||||
argTypeSS << "const " << type << "& " << argName;
|
||||
argSS << argNameSafe;
|
||||
argTypeSS << "const " << type << "& " << argNameSafe;
|
||||
}
|
||||
else
|
||||
{
|
||||
argSS << "std::move(" << argName << ")";
|
||||
argTypeSS << type << " " << argName;
|
||||
argSS << "std::move(" << argNameSafe << ")";
|
||||
argTypeSS << type << " " << argNameSafe;
|
||||
}
|
||||
typeSS << type;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto name = method->get("name");
|
||||
auto nameSafe = mangle_name(name);
|
||||
Nodes args = (*method)["arg"];
|
||||
Nodes inArgs = args.select("direction" , "in");
|
||||
Nodes outArgs = args.select("direction" , "out");
|
||||
@@ -173,7 +174,8 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
|
||||
std::string outArgStr, outArgTypeStr;
|
||||
std::tie(outArgStr, outArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(outArgs);
|
||||
|
||||
definitionSS << tab << (async ? "void" : retType) << " " << name << "(" << inArgTypeStr << ")" << endl
|
||||
const std::string realRetType = (async && !dontExpectReply ? "sdbus::PendingAsyncCall" : async ? "void" : retType);
|
||||
definitionSS << tab << realRetType << " " << nameSafe << "(" << inArgTypeStr << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
|
||||
if (!timeoutValue.empty())
|
||||
@@ -186,8 +188,8 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
|
||||
definitionSS << tab << tab << retType << " result;" << endl;
|
||||
}
|
||||
|
||||
definitionSS << tab << tab << "proxy_.callMethod" << (async ? "Async" : "") << "(\"" << name << "\")"
|
||||
".onInterface(INTERFACE_NAME)";
|
||||
definitionSS << tab << tab << (async && !dontExpectReply ? "return " : "")
|
||||
<< "proxy_.callMethod" << (async ? "Async" : "") << "(\"" << name << "\").onInterface(INTERFACE_NAME)";
|
||||
|
||||
if (!timeoutValue.empty())
|
||||
{
|
||||
@@ -259,6 +261,7 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyNameSafe = mangle_name(propertyName);
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
@@ -268,7 +271,7 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
propertySS << tab << propertyType << " " << propertyName << "()" << endl
|
||||
propertySS << tab << propertyType << " " << propertyNameSafe << "()" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "return proxy_.getProperty(\"" << propertyName << "\")"
|
||||
".onInterface(INTERFACE_NAME)";
|
||||
@@ -277,7 +280,7 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
propertySS << tab << "void " << propertyName << "(" << propertyTypeArg << ")" << endl
|
||||
propertySS << tab << "void " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "proxy_.setProperty(\"" << propertyName << "\")"
|
||||
".onInterface(INTERFACE_NAME)"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "generator_utils.h"
|
||||
#include "reserved_names.h"
|
||||
|
||||
|
||||
std::string underscorize(const std::string& str)
|
||||
@@ -142,3 +143,11 @@ std::string signature_to_type(const std::string& signature)
|
||||
_parse_signature(signature, type, i);
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string mangle_name(const std::string& name)
|
||||
{
|
||||
if (reserved_names.find(name) != reserved_names.end())
|
||||
return name + "_";
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -19,4 +19,6 @@ std::string underscorize(const std::string& str);
|
||||
|
||||
constexpr const char* getHeaderComment() noexcept { return "\n/*\n * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!\n */\n\n"; }
|
||||
|
||||
std::string mangle_name (const std::string& name);
|
||||
|
||||
#endif //__SDBUSCPP_TOOLS_GENERATOR_UTILS_H
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <set>
|
||||
|
||||
static const std::set<std::string> reserved_names = {
|
||||
// c++ keywords taken from https://en.cppreference.com/w/cpp/keyword
|
||||
"alignas",
|
||||
"alignof",
|
||||
"and",
|
||||
"and_eq",
|
||||
"asm",
|
||||
"atomic_cancel",
|
||||
"atomic_commit",
|
||||
"atomic_noexcept",
|
||||
"auto",
|
||||
"bitand",
|
||||
"bitor",
|
||||
"bool",
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"char",
|
||||
"char8_t",
|
||||
"char16_t",
|
||||
"char32_t",
|
||||
"class",
|
||||
"compl",
|
||||
"concept",
|
||||
"const",
|
||||
"consteval",
|
||||
"constexpr",
|
||||
"constinit",
|
||||
"const_cast",
|
||||
"continue",
|
||||
"co_await",
|
||||
"co_return",
|
||||
"co_yield",
|
||||
"decltype",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"double",
|
||||
"dynamic_cast",
|
||||
"else",
|
||||
"enum",
|
||||
"explicit",
|
||||
"export",
|
||||
"extern",
|
||||
"false",
|
||||
"float",
|
||||
"for",
|
||||
"friend",
|
||||
"goto",
|
||||
"if",
|
||||
"inline",
|
||||
"int",
|
||||
"long",
|
||||
"mutable",
|
||||
"namespace",
|
||||
"new",
|
||||
"noexcept",
|
||||
"not",
|
||||
"not_eq",
|
||||
"nullptr",
|
||||
"operator",
|
||||
"or",
|
||||
"or_eq",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"reflexpr",
|
||||
"register",
|
||||
"reinterpret_cast",
|
||||
"requires",
|
||||
"return",
|
||||
"short",
|
||||
"signed",
|
||||
"sizeof",
|
||||
"static",
|
||||
"static_assert",
|
||||
"static_cast",
|
||||
"struct",
|
||||
"switch",
|
||||
"synchronized",
|
||||
"template",
|
||||
"this",
|
||||
"thread_local",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"typedef",
|
||||
"typeid",
|
||||
"typename",
|
||||
"union",
|
||||
"unsigned",
|
||||
"using",
|
||||
"virtual",
|
||||
"void",
|
||||
"volatile",
|
||||
"wchar_t",
|
||||
"while",
|
||||
"xor",
|
||||
"xor_eq",
|
||||
// common defines
|
||||
"NULL"
|
||||
};
|
||||
@@ -149,7 +149,7 @@ int main(int argc, char **argv)
|
||||
|
||||
std::ifstream input(xmlFile);
|
||||
|
||||
if (input.bad())
|
||||
if (input.fail())
|
||||
{
|
||||
std::cerr << "Unable to open file " << xmlFile << endl;
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user