Merge lp:~robertcarr/mir/socket-messenger-race into lp:mir
- socket-messenger-race
- Merge into development-branch
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 |
Related bugs: |
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.
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1078
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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
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 ProtobufMessage
And that is best approached by changing interfaces so that the body and fds are passed to a single call.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1078
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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 ProtobufMessage
And that is best approached by changing interfaces so that the body and fds are passed to a single call.
Robert Carr (robertcarr) wrote : | # |
Reworked the interfaces some to hold the lock across calls.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1081
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1082
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1083
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
177 boost::
178 ba::write(*socket, ba::buffer(
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_
Now that there is a third parameter to the "google:
Alexandros Frantzis (afrantzis) wrote : | # |
+typedef std::initialize
What's the use case for multiple sets of FDs?
Robert Carr (robertcarr) wrote : | # |
Removed the error code usage so ba::write will throw, and catch the exceptions in processor/
Removed the casts.
Alf:
void mfd::ProtobufMe
{
auto const& surface_fd = extract_
const auto& buffer_fd = response-
send_
resource_
}
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1087
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1088
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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...
Alan Griffiths (alan-griffiths) wrote : | # |
void mfd::ProtobufMe
...
133 -
134 - sender-
135 + try
136 + {
137 + sender-
138 + }
139 + catch (std::exception const& error)
140 + {
141 + report-
142 + id, error);
143 + }
144 }
Exceptions should not be eaten here. They should propagate to ProtobufMessage
Alexandros Frantzis (afrantzis) wrote : | # |
> Exceptions should not be eaten here. They should propagate to ProtobufMessage
Since we are moving towards reinstating previous send failure behavior, we should also change send_fds_locked() to throw in case of failure.
Robert Carr (robertcarr) wrote : | # |
Like this? (r1090)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1090
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1091
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
134 - sender-
135 + try
136 + {
137 + sender-
138 + }
139 + catch (std::exception const& error)
140 + {
141 + report-
142 + id, error);
143 + }
Exceptions should not be eaten here. They should propagate to ProtobufMessage
Robert Carr (robertcarr) wrote : | # |
Iterated.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1093
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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.
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, {});
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
Daniel van Vugt (vanvugt) wrote : | # |
That seems to be approval from everyone who had an opinion so good enough...
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
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 |
should be ok from mirserver api, no break