46
57
auto wrapper = static_cast<Wrapper*>(cookie);
48
auto message = dbus_pending_call_steal_reply(call);
62
// We tie cleanup of the wrapper to the scope of the callback.
63
// With that, even an exception being thrown would _not_ result
64
// in an instance being leaked.
52
wrapper->pending_call->notify(Message::from_raw_message(message));
69
// We take over ownership of the wrapper and destroy on scope exit.
71
// We always unref this message, even if notify_locked or
72
// from_raw_message throws.
73
if (message) dbus_message_unref(message);
77
// We only need this later on, when we have stolen the reply
78
// from the pending call.
80
} scope{wrapper, nullptr};
82
// We synchronize to avoid races on construction.
83
std::lock_guard<std::mutex> lg{wrapper->pending_call->guard};
84
// And we only steal the reply if the call actually completed.
85
if (not is_pending_call_completed(call))
88
scope.message = dbus_pending_call_steal_reply(call);
91
wrapper->pending_call->notify_locked(Message::from_raw_message(scope.message));
56
void notify(const Message::Ptr& msg)
94
// Announce incoming reply and invoke the callback if set.
95
void notify_locked(const Message::Ptr& msg)
58
std::lock_guard<std::mutex> lg(guard);
72
109
inline static core::dbus::PendingCall::Ptr create(DBusPendingCall* call)
111
if (not call) throw std::runtime_error
113
"core::dbus::PendingCall cannot be constructed for null object."
74
116
auto result = std::shared_ptr<core::dbus::impl::PendingCall>
76
118
new core::dbus::impl::PendingCall{call}
121
// Our scope contains two objects that are dynamically created:
122
// * The actual PendingCall implementation (managed by a shared pointer)
123
// * A helper object of type Wrapper for passing around the pending call instance.
124
// The latter one needs some manual memory mgmt. until we are sure that it got handed
125
// over to libdbus correctly.
128
// Whenever we go out of scope, we unref the call (we do not need it anymore)
129
// and we potentially (if armed) delete the wrapper.
132
dbus_pending_call_unref(call);
133
if (armed) delete wrapper;
136
// Disarms the scope for handling the wrapper instance.
137
// Think about it like giving up ownership as we handed over
139
void disarm_for_wrapper()
144
// The raw call instance.
145
DBusPendingCall* call;
146
// True if the wrapper instance is still owned by the scope.
148
// The wrapper instance.
152
// The raw call that we got handed from the caller.
154
// Yes, we still own the Wrapper instance.
155
true, new Wrapper{result}
158
// We synchronize to avoid races on construction.
159
std::lock_guard<std::mutex> lg{result->guard};
79
161
if (FALSE == dbus_pending_call_set_notify(
81
PendingCall::on_pending_call_completed,
85
delete static_cast<Wrapper*>(data);
162
// We dispatch to the static on_pending_call_completed when
163
// the call completes.
164
result->pending_call, PendingCall::on_pending_call_completed,
165
// We pass in our helper object and do not specify a deleter.
166
// Please refer to the source-code comments in on_pending_call_completed.
167
scope.wrapper, nullptr))
88
169
throw std::runtime_error("Error setting up pending call notification.");
91
172
// And here comes the beauty of libdbus, and its racy architecture:
93
std::lock_guard<std::mutex> lg(result->guard);
94
if (TRUE == dbus_pending_call_get_completed(call))
174
if (is_pending_call_completed(call))
96
176
// We took too long while setting up the pending call notification.
97
177
// For that we now have to inject the message here.
98
178
auto msg = dbus_pending_call_steal_reply(call);
99
result->message = Message::from_raw_message(msg);
182
result->message = Message::from_raw_message(msg);
183
// We decrease the reference count as Message::from_raw_message
184
// always refs the object that it is passed.
185
dbus_message_unref(msg);
190
// We need the wrapper to be alive, so we disarm the scope.
191
// Please note that this is the only "good" path through this
192
// mess of setup and notification functions.
193
scope.disarm_for_wrapper();
103
return std::dynamic_pointer_cast<core::dbus::PendingCall>(result);