~phablet-team/dbus-cpp/trunk

« back to all changes in this revision

Viewing changes to src/core/dbus/pending_call_impl.h

  • Committer: CI bot
  • Author(s): thomas-voss
  • Date: 2014-10-02 20:48:52 UTC
  • mfrom: (77.1.7 fix-invalid-reads-in-executor)
  • Revision ID: ps-jenkins@lists.canonical.com-20141002204852-l2aebvjxfppdo3y1
Always unref pending calls, add load test to ensure correct behavior.
Make sure that timeouts are never notified after destruction. Fixes: 1367836
Approved by: Alberto Aguirre, PS Jenkins bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
#include <mutex>
28
28
 
 
29
namespace
 
30
{
 
31
// Better save than sorry. We wrap common error handling here.
 
32
bool is_pending_call_completed(DBusPendingCall* pending_call)
 
33
{
 
34
    if (not pending_call)
 
35
        return false;
 
36
 
 
37
    return dbus_pending_call_get_completed(pending_call) == TRUE;
 
38
}
 
39
}
29
40
namespace core
30
41
{
31
42
namespace dbus
45
56
    {
46
57
        auto wrapper = static_cast<Wrapper*>(cookie);
47
58
 
48
 
        auto message = dbus_pending_call_steal_reply(call);
 
59
        if (not wrapper)
 
60
            return;
49
61
 
50
 
        if (message)
 
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.
 
65
        struct Scope
51
66
        {
52
 
            wrapper->pending_call->notify(Message::from_raw_message(message));
53
 
        }
 
67
            ~Scope()
 
68
            {
 
69
                // We take over ownership of the wrapper and destroy on scope exit.
 
70
                delete wrapper;
 
71
                // We always unref this message, even if notify_locked or
 
72
                // from_raw_message throws.
 
73
                if (message) dbus_message_unref(message);
 
74
            }
 
75
 
 
76
            Wrapper* wrapper;
 
77
            // We only need this later on, when we have stolen the reply
 
78
            // from the pending call.
 
79
            DBusMessage* message;
 
80
        } scope{wrapper, nullptr};
 
81
 
 
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))
 
86
            return;
 
87
 
 
88
        scope.message = dbus_pending_call_steal_reply(call);
 
89
 
 
90
        if (scope.message)
 
91
            wrapper->pending_call->notify_locked(Message::from_raw_message(scope.message));
54
92
    }
55
93
 
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)
57
96
    {
58
 
        std::lock_guard<std::mutex> lg(guard);
59
 
 
60
97
        message = msg;
61
98
 
62
99
        if (callback)
71
108
public:
72
109
    inline static core::dbus::PendingCall::Ptr create(DBusPendingCall* call)
73
110
    {
 
111
        if (not call) throw std::runtime_error
 
112
        {
 
113
            "core::dbus::PendingCall cannot be constructed for null object."
 
114
        };
 
115
 
74
116
        auto result = std::shared_ptr<core::dbus::impl::PendingCall>
75
117
        {
76
118
            new core::dbus::impl::PendingCall{call}
77
119
        };
78
120
 
 
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.
 
126
        struct Scope
 
127
        {
 
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.
 
130
            ~Scope()
 
131
            {
 
132
                dbus_pending_call_unref(call);
 
133
                if (armed) delete wrapper;
 
134
            }
 
135
 
 
136
            // Disarms the scope for handling the wrapper instance.
 
137
            // Think about it like giving up ownership as we handed over
 
138
            // to libdbus.
 
139
            void disarm_for_wrapper()
 
140
            {
 
141
                armed = false;
 
142
            }
 
143
 
 
144
            // The raw call instance.
 
145
            DBusPendingCall* call;
 
146
            // True if the wrapper instance is still owned by the scope.
 
147
            bool armed;
 
148
            // The wrapper instance.
 
149
            Wrapper* wrapper;
 
150
        } scope
 
151
        {
 
152
            // The raw call that we got handed from the caller.
 
153
            call,
 
154
            // Yes, we still own the Wrapper instance.
 
155
            true, new Wrapper{result}
 
156
        };
 
157
 
 
158
        // We synchronize to avoid races on construction.
 
159
        std::lock_guard<std::mutex> lg{result->guard};
 
160
 
79
161
        if (FALSE == dbus_pending_call_set_notify(
80
 
                result->pending_call,
81
 
                PendingCall::on_pending_call_completed,
82
 
                new Wrapper{result},
83
 
                [](void* data)
84
 
                {
85
 
                    delete static_cast<Wrapper*>(data);
86
 
                }))
 
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))
87
168
        {
88
169
            throw std::runtime_error("Error setting up pending call notification.");
89
170
        }
90
171
 
91
172
        // And here comes the beauty of libdbus, and its racy architecture:
92
 
        {
93
 
            std::lock_guard<std::mutex> lg(result->guard);
94
 
            if (TRUE == dbus_pending_call_get_completed(call))
 
173
        {            
 
174
            if (is_pending_call_completed(call))
95
175
            {
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);
 
179
 
 
180
                if (msg)
 
181
                {
 
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);
 
186
                }
 
187
            }
 
188
            else
 
189
            {
 
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();
100
194
            }
101
195
        }
102
196
 
103
 
        return std::dynamic_pointer_cast<core::dbus::PendingCall>(result);
 
197
        return result;
104
198
    }
105
199
 
106
200
    void cancel()
122
216
    PendingCall(DBusPendingCall* call)
123
217
        : pending_call(call)
124
218
    {
125
 
    }
 
219
    }    
126
220
};
127
221
}
128
222
}