|
|
|
@ -33,6 +33,8 @@
|
|
|
|
|
#include "ScopeGuard.h"
|
|
|
|
|
#include <systemd/sd-bus.h>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
|
|
namespace sdbus {
|
|
|
|
|
|
|
|
|
@ -862,15 +864,70 @@ void Signal::setDestination(const std::string& destination)
|
|
|
|
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// Pseudo-connection lifetime handling. In standard cases, we could do with simply function-local static pseudo
|
|
|
|
|
// connection instance below. However, it may happen that client's sdbus-c++ objects outlive this static connection
|
|
|
|
|
// instance (because they are used in global application objects that were created before this connection, and thus
|
|
|
|
|
// are destroyed later). This by itself sounds like a smell in client's application design, but it is downright bad
|
|
|
|
|
// in sdbus-c++ because it has no control over when client's dependent statics get destroyed. A "Phoenix" pattern
|
|
|
|
|
// (see Modern C++ Design - Generic Programming and Design Patterns Applied, by Andrei Alexandrescu) is applied to fix
|
|
|
|
|
// this by re-creating the connection again in such cases and keeping it alive until the next exit handler is invoked.
|
|
|
|
|
// The pattern is combined with double-checked locking pattern to ensure safe re-creation across threads/CPU cores.
|
|
|
|
|
// Another common solution is global sdbus-c++ startup/shutdown functions, but that would be an intrusive change.
|
|
|
|
|
|
|
|
|
|
// Working notes (TODO: please remove)
|
|
|
|
|
// https://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/
|
|
|
|
|
// https://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/
|
|
|
|
|
|
|
|
|
|
constinit std::atomic<sdbus::internal::IConnection*> pseudoConnectionPtr;
|
|
|
|
|
constinit std::mutex pseudoConnectionMutex;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
|
|
|
|
|
{
|
|
|
|
|
auto deleter = [](sdbus::internal::IConnection* con)
|
|
|
|
|
{
|
|
|
|
|
delete con;
|
|
|
|
|
pseudoConnectionPtr.store(nullptr, std::memory_order_release);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto pseudoConnection = internal::createPseudoConnection();
|
|
|
|
|
pseudoConnectionPtr.store(pseudoConnection.get(), std::memory_order_release);
|
|
|
|
|
return {pseudoConnection.release(), std::move(deleter)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sdbus::internal::IConnection& getPseudoConnectionInstance()
|
|
|
|
|
{
|
|
|
|
|
static auto pseudoConnection = createPseudoConnection();
|
|
|
|
|
|
|
|
|
|
if (pseudoConnectionPtr.load(std::memory_order_consume) == nullptr)
|
|
|
|
|
{
|
|
|
|
|
// The connection has been destroyed already (we are in exit handlers).
|
|
|
|
|
// Double-checked locking pattern is used to re-create the connection in a thread-safe manner.
|
|
|
|
|
std::lock_guard lock(pseudoConnectionMutex);
|
|
|
|
|
if (pseudoConnectionPtr.load(std::memory_order_consume) == nullptr)
|
|
|
|
|
{
|
|
|
|
|
pseudoConnection = createPseudoConnection(); // Phoenix rising from the ashes
|
|
|
|
|
atexit([]() { pseudoConnection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
|
|
|
|
|
pseudoConnectionPtr.store(pseudoConnection.get(), std::memory_order_release);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(pseudoConnection != nullptr);
|
|
|
|
|
|
|
|
|
|
return *pseudoConnectionPtr.load(std::memory_order_relaxed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlainMessage createPlainMessage()
|
|
|
|
|
{
|
|
|
|
|
//static auto connection = internal::createConnection();
|
|
|
|
|
// Let's create a pseudo connection -- one that does not really connect to the real bus.
|
|
|
|
|
// This is a bit of a hack, but it enables use to work with D-Bus message locally without
|
|
|
|
|
// the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code.
|
|
|
|
|
// Additionally, it's light-weight and fast solution.
|
|
|
|
|
static auto connection = internal::createPseudoConnection();
|
|
|
|
|
return connection->createPlainMessage();
|
|
|
|
|
auto& connection = getPseudoConnectionInstance();
|
|
|
|
|
return connection.createPlainMessage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|