Mir

Merge lp:~robertcarr/mir/socket-messenger-race into lp:mir

Proposed by Robert Ancell
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 1127
Proposed branch: lp:~robertcarr/mir/socket-messenger-race
Merge into: lp:mir
Diff against target: 273 lines (+92/-18)
8 files modified
src/server/frontend/event_sender.cpp (+10/-1)
src/server/frontend/fd_sets.h (+33/-0)
src/server/frontend/message_sender.h (+3/-1)
src/server/frontend/protobuf_message_processor.cpp (+12/-8)
src/server/frontend/protobuf_message_processor.h (+4/-0)
src/server/frontend/socket_messenger.cpp (+22/-5)
src/server/frontend/socket_messenger.h (+5/-1)
tests/unit-tests/frontend/test_event_sender.cpp (+3/-2)
To merge this branch: bzr merge lp:~robertcarr/mir/socket-messenger-race
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Alan Griffiths Approve
Daniel van Vugt Abstain
Kevin DuBois Pending
Review via email: mp+187647@code.launchpad.net

This proposal supersedes a proposal from 2013-09-23.

Commit message

socket_messenger: Protect whole_message with a lock.

Description of the change

It seems to me that socket messenger may be used from any thread (as a result of the shell generating various events, i.e. focus/unfocus).

This means that we need to protect whole_message with a lock.

To post a comment you must log in.
Revision history for this message
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal

should be ok from mirserver api, no break

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote : Posted in a previous version of this proposal

should retarget to development branch...

other than that though, the fix makes sense

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

I'm not sure this is adequate.

Unless I miss something, the lock needs to be held across the related send() and send_fds() calls originating in ProtobufMessageProcessor::send_response() - not just the send() call.

And that is best approached by changing interfaces so that the body and fds are passed to a single call.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I'm not sure this is adequate.

Unless I miss something, the lock needs to be held across the related send() and send_fds() calls originating in ProtobufMessageProcessor::send_response() - not just the send() call.

And that is best approached by changing interfaces so that the body and fds are passed to a single call.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Reworked the interfaces some to hold the lock across calls.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

177 boost::system::error_code err;
178 ba::write(*socket, ba::buffer(whole_message), err);
179 +
180 + for (auto const& fds : fd_set)
181 + send_fds_locked(lg, fds);

I think that if the call to write fails we should skip the call(s) to send_fds_locked().

~~~~

91 + send_response(id, static_cast<google::protobuf::Message*>(response), {surface_fd, buffer_fd});;

Now that there is a third parameter to the "google::protobuf::Message*" overload of send_response() we don't need the cast to disambiguate.

review: Needs Fixing
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

+typedef std::initializer_list<std::vector<int32_t>> FdSets;

What's the use case for multiple sets of FDs?

review: Needs Information
Revision history for this message
Robert Carr (robertcarr) wrote :

Removed the error code usage so ba::write will throw, and catch the exceptions in processor/communicator (obsoleting socket-messenger-reporting).

Removed the casts.

Alf:

void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Surface* response)
{
    auto const& surface_fd = extract_fds_from(response);
    const auto& buffer_fd = response->has_buffer() ?
        extract_fds_from(response->mutable_buffer()) :
        std::vector<int32_t>();

    send_response(id, response, {surface_fd, buffer_fd});;
    resource_cache->free_resource(response);
}

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

> > What's the use case for multiple sets of FDs?
>
> send_response(id, response, {surface_fd, buffer_fd});

This is a pre-existing situation, made more obvious (to me) because of the interface change:

Since we don't process the different sets of FDs differently, this interface seems a bit strange (vs accepting a single vector containing all the FDs), and requires two sendmsg() calls on the server side, and two rcvmsg() calls on the client side. Unless I am missing something, we could send/receive the FDs in one message, although I am not sure if it is worth the trouble, since we cache FDs on the client side. Still, something to keep in mind...

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

void mfd::ProtobufMessageProcessor::send_response(
...
133 -
134 - sender->send(send_response_buffer);
135 + try
136 + {
137 + sender->send(send_response_buffer, fd_sets);
138 + }
139 + catch (std::exception const& error)
140 + {
141 + report->exception_handled(display_server.get(),
142 + id, error);
143 + }
144 }

Exceptions should not be eaten here. They should propagate to ProtobufMessageProcessor::dispatch() - which handles them correctly.

review: Needs Fixing
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> Exceptions should not be eaten here. They should propagate to ProtobufMessageProcessor::dispatch() - which handles them correctly.

Since we are moving towards reinstating previous send failure behavior, we should also change send_fds_locked() to throw in case of failure.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Like this? (r1090)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

134 - sender->send(send_response_buffer);
135 + try
136 + {
137 + sender->send(send_response_buffer, fd_sets);
138 + }
139 + catch (std::exception const& error)
140 + {
141 + report->exception_handled(display_server.get(),
142 + id, error);
143 + }

Exceptions should not be eaten here. They should propagate to ProtobufMessageProcessor::dispatch() - which handles them correctly.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Iterated.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

If there's a related bug please link it.

But regardless, I'm not familiar with this code and there seem to be enough opinions. So abstain for now.

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

74 virtual void send(std::string const& body) = 0;
...
76 + virtual void send(std::string const& body, FdSets const& fds) = 0;

Do we really need both versions?

send(x) ought to be the same as send(x, {});

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

That seems to be approval from everyone who had an opinion so good enough...

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/frontend/event_sender.cpp'
2--- src/server/frontend/event_sender.cpp 2013-08-28 03:41:48 +0000
3+++ src/server/frontend/event_sender.cpp 2013-10-08 18:30:35 +0000
4@@ -80,5 +80,14 @@
5 mir::protobuf::wire::Result result;
6 result.add_events(send_buffer);
7 result.SerializeToString(&send_buffer);
8- sender->send(send_buffer);
9+
10+ try
11+ {
12+ sender->send(send_buffer);
13+ }
14+ catch (std::exception const& error)
15+ {
16+ // TODO: We should report this state.
17+ (void) error;
18+ }
19 }
20
21=== added file 'src/server/frontend/fd_sets.h'
22--- src/server/frontend/fd_sets.h 1970-01-01 00:00:00 +0000
23+++ src/server/frontend/fd_sets.h 2013-10-08 18:30:35 +0000
24@@ -0,0 +1,33 @@
25+/*
26+ * Copyright © 2013 Canonical Ltd.
27+ *
28+ * This program is free software: you can redistribute it and/or modify it
29+ * under the terms of the GNU General Public License version 3,
30+ * as published by the Free Software Foundation.
31+ *
32+ * This program is distributed in the hope that it will be useful,
33+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
34+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35+ * GNU General Public License for more details.
36+ *
37+ * You should have received a copy of the GNU General Public License
38+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
39+ *
40+ * Authored by: Robert Carr <robert.carr@canonical.com>
41+ */
42+#ifndef MIR_FRONTEND_FD_SETS_H_
43+#define MIR_FRONTEND_FD_SETS_H_
44+
45+#include <vector>
46+#include <initializer_list>
47+#include <stdint.h>
48+
49+namespace mir
50+{
51+namespace frontend
52+{
53+typedef std::initializer_list<std::vector<int32_t>> FdSets;
54+}
55+} // namespace mir
56+
57+#endif // MIR_FRONTEND_FD_SETS_H_
58
59=== modified file 'src/server/frontend/message_sender.h'
60--- src/server/frontend/message_sender.h 2013-08-28 03:41:48 +0000
61+++ src/server/frontend/message_sender.h 2013-10-08 18:30:35 +0000
62@@ -18,6 +18,8 @@
63 #ifndef MIR_FRONTEND_MESSAGE_SENDER_H_
64 #define MIR_FRONTEND_MESSAGE_SENDER_H_
65
66+#include "fd_sets.h"
67+
68 #include <vector>
69 #include <boost/asio.hpp>
70
71@@ -31,7 +33,7 @@
72 {
73 public:
74 virtual void send(std::string const& body) = 0;
75- virtual void send_fds(std::vector<int32_t> const& fd) = 0;
76+ virtual void send(std::string const& body, FdSets const& fds) = 0;
77
78 protected:
79 MessageSender() = default;
80
81=== modified file 'src/server/frontend/protobuf_message_processor.cpp'
82--- src/server/frontend/protobuf_message_processor.cpp 2013-08-29 03:48:16 +0000
83+++ src/server/frontend/protobuf_message_processor.cpp 2013-10-08 18:30:35 +0000
84@@ -50,8 +50,7 @@
85 void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Buffer* response)
86 {
87 const auto& fd = extract_fds_from(response);
88- send_response(id, static_cast<google::protobuf::Message*>(response));
89- sender->send_fds(fd);
90+ send_response(id, response, {fd});
91 resource_cache->free_resource(response);
92 }
93
94@@ -61,8 +60,7 @@
95 extract_fds_from(response->mutable_platform()) :
96 std::vector<int32_t>();
97
98- send_response(id, static_cast<google::protobuf::Message*>(response));
99- sender->send_fds(fd);
100+ send_response(id, response, {fd});
101 resource_cache->free_resource(response);
102 }
103
104@@ -73,9 +71,7 @@
105 extract_fds_from(response->mutable_buffer()) :
106 std::vector<int32_t>();
107
108- send_response(id, static_cast<google::protobuf::Message*>(response));
109- sender->send_fds(surface_fd);
110- sender->send_fds(buffer_fd);
111+ send_response(id, response, {surface_fd, buffer_fd});;
112 resource_cache->free_resource(response);
113 }
114
115@@ -126,6 +122,14 @@
116 ::google::protobuf::uint32 id,
117 google::protobuf::Message* response)
118 {
119+ send_response(id, response, FdSets());
120+}
121+
122+void mfd::ProtobufMessageProcessor::send_response(
123+ ::google::protobuf::uint32 id,
124+ google::protobuf::Message* response,
125+ FdSets const& fd_sets)
126+{
127 response->SerializeToString(&send_response_buffer);
128
129 send_response_result.set_id(id);
130@@ -133,7 +137,7 @@
131
132 send_response_result.SerializeToString(&send_response_buffer);
133
134- sender->send(send_response_buffer);
135+ sender->send(send_response_buffer, fd_sets);
136 }
137
138 bool mfd::ProtobufMessageProcessor::dispatch(mir::protobuf::wire::Invocation const& invocation)
139
140=== modified file 'src/server/frontend/protobuf_message_processor.h'
141--- src/server/frontend/protobuf_message_processor.h 2013-08-28 03:41:48 +0000
142+++ src/server/frontend/protobuf_message_processor.h 2013-10-08 18:30:35 +0000
143@@ -20,6 +20,8 @@
144 #ifndef MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_
145 #define MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_
146
147+#include "fd_sets.h"
148+
149 #include "message_processor.h"
150 #include "message_sender.h"
151
152@@ -57,6 +59,8 @@
153
154 private:
155 void send_response(::google::protobuf::uint32 id, google::protobuf::Message* response);
156+ void send_response(::google::protobuf::uint32 id, google::protobuf::Message* response,
157+ FdSets const& fd_sets);
158
159 template<class ResultMessage>
160 void send_response(::google::protobuf::uint32 id, ResultMessage* response);
161
162=== modified file 'src/server/frontend/socket_messenger.cpp'
163--- src/server/frontend/socket_messenger.cpp 2013-09-25 14:18:18 +0000
164+++ src/server/frontend/socket_messenger.cpp 2013-10-08 18:30:35 +0000
165@@ -19,7 +19,13 @@
166 #include "socket_messenger.h"
167 #include "mir/frontend/client_constants.h"
168
169-namespace mfd = mir::frontend::detail;
170+#include <errno.h>
171+#include <string.h>
172+
173+#include <stdexcept>
174+
175+namespace mf = mir::frontend;
176+namespace mfd = mf::detail;
177 namespace bs = boost::system;
178 namespace ba = boost::asio;
179
180@@ -43,12 +49,19 @@
181
182 void mfd::SocketMessenger::send(std::string const& body)
183 {
184+ send(body, mf::FdSets());
185+}
186+
187+void mfd::SocketMessenger::send(std::string const& body, FdSets const& fd_set)
188+{
189 const size_t size = body.size();
190 const unsigned char header_bytes[2] =
191 {
192 static_cast<unsigned char>((size >> 8) & 0xff),
193 static_cast<unsigned char>((size >> 0) & 0xff)
194 };
195+
196+ std::unique_lock<std::mutex> lg(message_lock);
197
198 whole_message.resize(sizeof header_bytes + size);
199 std::copy(header_bytes, header_bytes + sizeof header_bytes, whole_message.begin());
200@@ -59,11 +72,13 @@
201 // function has completed (if it would be executed asynchronously.
202 // NOTE: we rely on this synchronous behavior as per the comment in
203 // mf::SessionMediator::create_surface
204- boost::system::error_code err;
205- ba::write(*socket, ba::buffer(whole_message), err);
206+ ba::write(*socket, ba::buffer(whole_message));
207+
208+ for (auto const& fds : fd_set)
209+ send_fds_locked(lg, fds);
210 }
211
212-void mfd::SocketMessenger::send_fds(std::vector<int32_t> const& fds)
213+void mfd::SocketMessenger::send_fds_locked(std::unique_lock<std::mutex> const&, std::vector<int32_t> const& fds)
214 {
215 auto n_fds = fds.size();
216 if (n_fds > 0)
217@@ -97,7 +112,9 @@
218 for (auto &fd: fds)
219 data[i++] = fd;
220
221- sendmsg(socket->native_handle(), &header, 0);
222+ auto sent = sendmsg(socket->native_handle(), &header, 0);
223+ if (sent < 0)
224+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to send fds: " + std::string(strerror(errno))));
225 }
226 }
227
228
229=== modified file 'src/server/frontend/socket_messenger.h'
230--- src/server/frontend/socket_messenger.h 2013-08-28 03:41:48 +0000
231+++ src/server/frontend/socket_messenger.h 2013-10-08 18:30:35 +0000
232@@ -35,14 +35,18 @@
233 SocketMessenger(std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket);
234
235 void send(std::string const& body);
236- void send_fds(std::vector<int32_t> const& fds);
237+ void send(std::string const& body, FdSets const& fd_set);
238
239 void async_receive_msg(MirReadHandler const& handler, boost::asio::streambuf& buffer, size_t size);
240 pid_t client_pid();
241
242 private:
243 std::shared_ptr<boost::asio::local::stream_protocol::socket> socket;
244+
245+ std::mutex message_lock;
246 std::vector<char> whole_message;
247+
248+ void send_fds_locked(std::unique_lock<std::mutex> const& lock, std::vector<int32_t> const& fds);
249 };
250 }
251 }
252
253=== modified file 'tests/unit-tests/frontend/test_event_sender.cpp'
254--- tests/unit-tests/frontend/test_event_sender.cpp 2013-08-28 03:41:48 +0000
255+++ tests/unit-tests/frontend/test_event_sender.cpp 2013-10-08 18:30:35 +0000
256@@ -30,7 +30,8 @@
257
258 namespace mt=mir::test;
259 namespace mtd=mir::test::doubles;
260-namespace mfd=mir::frontend::detail;
261+namespace mf=mir::frontend;
262+namespace mfd=mf::detail;
263 namespace geom=mir::geometry;
264
265 namespace
266@@ -38,7 +39,7 @@
267 struct MockMsgSender : public mfd::MessageSender
268 {
269 MOCK_METHOD1(send, void(std::string const&));
270- MOCK_METHOD1(send_fds, void(std::vector<int32_t> const&));
271+ MOCK_METHOD2(send, void(std::string const&, mf::FdSets const&));
272 };
273 }
274

Subscribers

People subscribed via source and target branches