Merge lp:~afrantzis/mir/display-server-main-loop-1 into lp:~mir-team/mir/trunk
- display-server-main-loop-1
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Alan Griffiths |
Approved revision: | no longer in the source branch. |
Merged at revision: | 576 |
Proposed branch: | lp:~afrantzis/mir/display-server-main-loop-1 |
Merge into: | lp:~mir-team/mir/trunk |
Diff against target: |
1089 lines (+487/-392) 18 files modified
include/server/mir/asio_main_loop.h (+53/-0) include/server/mir/default_server_configuration.h (+3/-0) include/server/mir/main_loop.h (+48/-0) include/server/mir/server_configuration.h (+3/-0) include/server/mir/signal_dispatcher.h (+0/-61) src/server/CMakeLists.txt (+1/-1) src/server/asio_main_loop.cpp (+96/-0) src/server/default_server_configuration.cpp (+10/-0) src/server/display_server.cpp (+5/-16) src/server/run_mir.cpp (+14/-21) src/server/signal_dispatcher.cpp (+0/-170) tests/integration-tests/CMakeLists.txt (+1/-0) tests/integration-tests/cucumber/test_session_management_context.cpp (+1/-0) tests/integration-tests/process/CMakeLists.txt (+0/-1) tests/integration-tests/process/test_signal_dispatcher.cpp (+0/-121) tests/integration-tests/test_display_server_main_loop_events.cpp (+102/-0) tests/unit-tests/CMakeLists.txt (+6/-1) tests/unit-tests/test_asio_main_loop.cpp (+144/-0) |
To merge this branch: | bzr merge lp:~afrantzis/mir/display-server-main-loop-1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Alan Griffiths | Approve | ||
Robert Ancell | Pending | ||
Review via email: mp+157693@code.launchpad.net |
This proposal supersedes a proposal from 2013-04-05.
Commit message
server: Use a main loop to handle events in the display server
Description of the change
server: Use a main loop to handle events in the display server
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
Robert Ancell (robert-ancell) wrote : Posted in a previous version of this proposal | # |
LGTM
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
361 + auto asio_event = std::static_
...
370 + auto asio_event = std::static_
These casts look wrong - there should not be undefined behaviour if a conforming parameter is supplied.
I'm still trying to decide if the casts should be dynamic, if the function signature is wrong, or if some other way to recover type information is appropriate (e.g. double dispatch).
~~~~
982 +TEST(DisplaySe
983 +{
984 + ServerConfig server_config;
985 +
986 + mir::run_
987 + [](mir:
988 + {
989 + kill(getpid(), SIGINT);
990 + });
991 +}
Does this really do what it claims? (In run_mir() init() is called before invoking run() - so it may not be the best way to test the required behaviour.)
~~~~
482 + std::shared_
Why does display server need a dependency on MainLoopFactory? AFAICS it just needs a MainLoop.
Can't MainLoopFactory be subsumed into DefaultServerCo
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
> 361 + auto asio_event = std::static_
> ...
> 370 + auto asio_event = std::static_
>
> These casts look wrong - there should not be undefined behaviour if a conforming parameter is supplied.
>
> I'm still trying to decide if the casts should be dynamic, if the function signature is wrong, or if some other
> way to recover type information is appropriate (e.g. double dispatch).
The rationale for this is that there should be a single factory in use providing compatible events (i.e. that can be safely downcasted) , but this is indeed a fragile assumption. I am happy to change this to a dynamic_cast (or we can discuss more about the other options).
> Does this really do what it claims? (In run_mir() init() is called before invoking run() -
> so it may not be the best > way to test the required behaviour.
At the moment we don't have a reliable mechanism to detect when the server has actually started. However, we start queuing signal events from the moment we create the DisplayServer, not from the moment we start running it, so the code above will work fine. I considered adding a way to notify the caller that the display server has actually started, e.g., a display_
> 482 + std::shared_
> Why does display server need a dependency on MainLoopFactory? AFAICS it just needs a MainLoop.
> Can't MainLoopFactory be subsumed into DefaultServerCo
The Display server needs to create a stop event, which needs a handle to a main loop factory.
Also, in the near future the Platform will need to be able to create a platform events (e.g. PauseResume), which again need the main loop factory. The main loop factory can be supplied either at Platform construction time, or at event creation time(platform-
So one option is:
1. DisplayConfigur
2. DisplayServer uses the main loop factory to create the stop event
3. DisplayServer passes the main loop factory into platform-
Alternatively the main loop factory can be subsumed into the configuration:
1. DisplayConfigur
2. DisplayConfigur
3. The factory is supplied at Platform creation time through mg::create_
I am fine with moving to the second option.
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
> > 361 + auto asio_event = std::static_
> > ...
> > 370 + auto asio_event = std::static_
> >
> > These casts look wrong - there should not be undefined behaviour if a
> conforming parameter is supplied.
> >
> > I'm still trying to decide if the casts should be dynamic, if the function
> signature is wrong, or if some other
> > way to recover type information is appropriate (e.g. double dispatch).
>
> The rationale for this is that there should be a single factory in use
> providing compatible events (i.e. that can be safely downcasted) , but this is
> indeed a fragile assumption. I am happy to change this to a dynamic_cast (or
> we can discuss more about the other options).
I now think this relates to my concerns about roles. (see below)
> > Does this really do what it claims? (In run_mir() init() is called before
> invoking run() -
> > so it may not be the best > way to test the required behaviour.
>
> At the moment we don't have a reliable mechanism to detect when the server has
> actually started. However, we start queuing signal events from the moment we
> create the DisplayServer, not from the moment we start running it, so the code
> above will work fine. I considered adding a way to notify the caller that the
> display server has actually started, e.g., a display_
> but I didn't want to overload this MP.
OK
> > 482 + std::shared_
> > Why does display server need a dependency on MainLoopFactory? AFAICS it just
> needs a MainLoop.
> > Can't MainLoopFactory be subsumed into DefaultServerCo
>
> The Display server needs to create a stop event, which needs a handle to a
> main loop factory.
Why is that the role of the DisplayServer? AFAICS it has no need to know anything about StopEvent.
The equivalent registration was in run_mir() which seems a better place to me.
> Also, in the near future the Platform will need to be able to create a
> platform events (e.g. PauseResume), which again need the main loop factory.
> The main loop factory can be supplied either at Platform construction time, or
> at event creation time(platform-
>
> So one option is:
>
> 1. DisplayConfigur
> 2. DisplayServer uses the main loop factory to create the stop event
> 3. DisplayServer passes the main loop factory into
> platform-
If it is more often for creating events I don't think that MainLoopFactory is the best name.
But do the event objects even need to be visible outside the implementation? Can't the whole interface be:
void MainLoop:
> Alternatively the main loop factory can be subsumed into the configuration:
>
> 1. DisplayConfigur
> 2. DisplayConfigur
> 3. The factory is supplied at Platform creation time through
> mg::create_
> parameterless platform-
>
> I a...
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
> Why is that the role of the DisplayServer? AFAICS it has no need to know
> anything about StopEvent.
>
> The equivalent registration was in run_mir() which seems a better place to me.
> But do the event objects even need to be visible outside the implementation?
> Can't the whole interface be:
>
> void MainLoop:
> std::function<
Joining pieces from the discussion so far, the proposal becomes (leaving out details
not related to this MP):
=======
class MainLoop
{
void register_
/* In the future */
void register_
};
std::shared_
mir::run_
{
DisplayServer server{conf}
conf.
....
}
... somewhere in display_server.cpp:
// main_loop = conf.the_
platform-
=======
I like this.
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
> > Why is that the role of the DisplayServer? AFAICS it has no need to know
> > anything about StopEvent.
> >
> > The equivalent registration was in run_mir() which seems a better place to
> me.
>
> > But do the event objects even need to be visible outside the implementation?
> > Can't the whole interface be:
> >
> > void MainLoop:
> > std::function<
>
> Joining pieces from the discussion so far, the proposal becomes (leaving out
> details
> not related to this MP):
>
> =======
> class MainLoop
> {
> void register_
> std::function<
>
> /* In the future */
> void register_
> };
>
> std::shared_
>
> mir::run_
> {
> DisplayServer server{conf}
> conf.the_
> [&server]
> ....
> }
>
> ... somewhere in display_server.cpp:
>
> // main_loop = conf.the_
> platform-
> =======
>
> I like this.
+1
BTW Is it worth also providing a forwarding wrapper for register_
template<typename Signals...>
void register_
Alexandros Frantzis (afrantzis) wrote : | # |
> BTW Is it worth also providing a forwarding wrapper for register_
> creation of an initializer list in client code:
> template<typename Signals...>
> void register_
I left this for a future MP. I guess that in that case it would also make sense to reverse the arguments of the non-templated function for uniformity:
register_
Alan Griffiths (alan-griffiths) wrote : | # |
Much clearer. A couple of nits:
114 + virtual ~MainLoop() {}
Are we really expecting destructors to throw?
~~~~
456 +#include <iostream>
Should be unnecessary
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:572
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
> 114 + virtual ~MainLoop() {}
> Are we really expecting destructors to throw?
Fixed.
> 456 +#include <iostream>
> Should be unnecessary
Already fixed in 573, probably the diff didn't refresh quickly enough.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:574
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === added file 'include/server/mir/asio_main_loop.h' |
2 | --- include/server/mir/asio_main_loop.h 1970-01-01 00:00:00 +0000 |
3 | +++ include/server/mir/asio_main_loop.h 2013-04-08 16:58:20 +0000 |
4 | @@ -0,0 +1,53 @@ |
5 | +/* |
6 | + * Copyright © 2013 Canonical Ltd. |
7 | + * |
8 | + * This program is free software: you can redistribute it and/or modify it |
9 | + * under the terms of the GNU Lesser General Public License version 3, |
10 | + * as published by the Free Software Foundation. |
11 | + * |
12 | + * This program is distributed in the hope that it will be useful, |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | + * GNU General Public License for more details. |
16 | + * |
17 | + * You should have received a copy of the GNU Lesser General Public License |
18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | + * |
20 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
21 | + */ |
22 | + |
23 | +#ifndef MIR_ASIO_MAIN_LOOP_H_ |
24 | +#define MIR_ASIO_MAIN_LOOP_H_ |
25 | + |
26 | +#include "mir/main_loop.h" |
27 | + |
28 | +#include <boost/asio.hpp> |
29 | +#include <memory> |
30 | +#include <vector> |
31 | + |
32 | +namespace mir |
33 | +{ |
34 | + |
35 | +class AsioMainLoop : public MainLoop |
36 | +{ |
37 | +public: |
38 | + AsioMainLoop(); |
39 | + ~AsioMainLoop() noexcept(true); |
40 | + |
41 | + void run(); |
42 | + void stop(); |
43 | + |
44 | + void register_signal_handler( |
45 | + std::initializer_list<int> signals, |
46 | + std::function<void(int)> const& handler); |
47 | + |
48 | +private: |
49 | + class SignalHandler; |
50 | + |
51 | + boost::asio::io_service io; |
52 | + std::vector<std::unique_ptr<SignalHandler>> signal_handlers; |
53 | +}; |
54 | + |
55 | +} |
56 | + |
57 | +#endif /* MIR_ASIO_MAIN_LOOP_H */ |
58 | |
59 | === modified file 'include/server/mir/default_server_configuration.h' |
60 | --- include/server/mir/default_server_configuration.h 2013-03-27 15:12:09 +0000 |
61 | +++ include/server/mir/default_server_configuration.h 2013-04-08 16:58:20 +0000 |
62 | @@ -123,6 +123,8 @@ |
63 | virtual std::shared_ptr<shell::SurfaceBuilder> the_surface_builder(); |
64 | virtual std::shared_ptr<time::TimeSource> the_time_source(); |
65 | |
66 | + virtual std::shared_ptr<MainLoop> the_main_loop(); |
67 | + |
68 | protected: |
69 | virtual std::shared_ptr<options::Option> the_options() const; |
70 | virtual std::shared_ptr<input::InputChannelFactory> the_input_channel_factory(); |
71 | @@ -149,6 +151,7 @@ |
72 | CachedPtr<graphics::DisplayReport> display_report; |
73 | CachedPtr<surfaces::SurfaceController> surface_controller; |
74 | CachedPtr<time::TimeSource> time_source; |
75 | + CachedPtr<MainLoop> main_loop; |
76 | |
77 | private: |
78 | std::shared_ptr<options::Option> options; |
79 | |
80 | === added file 'include/server/mir/main_loop.h' |
81 | --- include/server/mir/main_loop.h 1970-01-01 00:00:00 +0000 |
82 | +++ include/server/mir/main_loop.h 2013-04-08 16:58:20 +0000 |
83 | @@ -0,0 +1,48 @@ |
84 | +/* |
85 | + * Copyright © 2013 Canonical Ltd. |
86 | + * |
87 | + * This program is free software: you can redistribute it and/or modify it |
88 | + * under the terms of the GNU Lesser General Public License version 3, |
89 | + * as published by the Free Software Foundation. |
90 | + * |
91 | + * This program is distributed in the hope that it will be useful, |
92 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
93 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
94 | + * GNU General Public License for more details. |
95 | + * |
96 | + * You should have received a copy of the GNU Lesser General Public License |
97 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
98 | + * |
99 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
100 | + */ |
101 | + |
102 | +#ifndef MIR_MAIN_LOOP_H_ |
103 | +#define MIR_MAIN_LOOP_H_ |
104 | + |
105 | +#include <functional> |
106 | +#include <initializer_list> |
107 | + |
108 | +namespace mir |
109 | +{ |
110 | + |
111 | +class MainLoop |
112 | +{ |
113 | +public: |
114 | + virtual ~MainLoop() = default; |
115 | + |
116 | + virtual void run() = 0; |
117 | + virtual void stop() = 0; |
118 | + |
119 | + virtual void register_signal_handler( |
120 | + std::initializer_list<int> signals, |
121 | + std::function<void(int)> const& handler) = 0; |
122 | + |
123 | +protected: |
124 | + MainLoop() = default; |
125 | + MainLoop(MainLoop const&) = delete; |
126 | + MainLoop& operator=(MainLoop const&) = delete; |
127 | +}; |
128 | + |
129 | +} |
130 | + |
131 | +#endif /* MIR_MAIN_LOOP_H_ */ |
132 | |
133 | === modified file 'include/server/mir/server_configuration.h' |
134 | --- include/server/mir/server_configuration.h 2013-03-22 17:13:10 +0000 |
135 | +++ include/server/mir/server_configuration.h 2013-04-08 16:58:20 +0000 |
136 | @@ -41,6 +41,8 @@ |
137 | class EventFilter; |
138 | } |
139 | |
140 | +class MainLoop; |
141 | + |
142 | class ServerConfiguration |
143 | { |
144 | public: |
145 | @@ -49,6 +51,7 @@ |
146 | virtual std::shared_ptr<graphics::Display> the_display() = 0; |
147 | virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0; |
148 | virtual std::shared_ptr<input::InputManager> the_input_manager() = 0; |
149 | + virtual std::shared_ptr<MainLoop> the_main_loop() = 0; |
150 | |
151 | protected: |
152 | ServerConfiguration() = default; |
153 | |
154 | === removed file 'include/server/mir/signal_dispatcher.h' |
155 | --- include/server/mir/signal_dispatcher.h 2013-03-28 12:13:18 +0000 |
156 | +++ include/server/mir/signal_dispatcher.h 1970-01-01 00:00:00 +0000 |
157 | @@ -1,61 +0,0 @@ |
158 | -/* |
159 | - * Copyright © 2012 Canonical Ltd. |
160 | - * |
161 | - * This program is free software: you can redistribute it and/or modify it |
162 | - * under the terms of the GNU Lesser General Public License version 3, |
163 | - * as published by the Free Software Foundation. |
164 | - * |
165 | - * This program is distributed in the hope that it will be useful, |
166 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
167 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
168 | - * GNU General Public License for more details. |
169 | - * |
170 | - * You should have received a copy of the GNU Lesser General Public License |
171 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
172 | - * |
173 | - * Authored by: Thomas Voss <thomas.voss@canonical.com> |
174 | - */ |
175 | - |
176 | -#ifndef MIR_SIGNAL_DISPATCHER_H_ |
177 | -#define MIR_SIGNAL_DISPATCHER_H_ |
178 | - |
179 | -#include <boost/signals2.hpp> |
180 | - |
181 | -#include <memory> |
182 | - |
183 | -namespace mir |
184 | -{ |
185 | - |
186 | -// Singleton class that decouples |
187 | -// signal handling from reacting to |
188 | -// signals to ensure that the actual signal |
189 | -// handler only calls async signal-safe functions |
190 | -// according to man 7 signal. |
191 | -class SignalDispatcher |
192 | -{ |
193 | -public: |
194 | - struct Constructor; |
195 | - |
196 | - typedef boost::signals2::signal<void(int)> SignalType; |
197 | - |
198 | - static std::shared_ptr<SignalDispatcher> instance(); |
199 | - |
200 | - ~SignalDispatcher(); |
201 | - |
202 | - SignalType& signal_channel(); |
203 | - |
204 | - void enable_for(int signal); |
205 | - |
206 | -private: |
207 | - friend struct Constructor; |
208 | - |
209 | - SignalDispatcher(); |
210 | - SignalDispatcher(const SignalDispatcher&) = delete; |
211 | - SignalDispatcher& operator=(const SignalDispatcher&) = delete; |
212 | - |
213 | - struct Private; |
214 | - std::unique_ptr<Private> p; |
215 | -}; |
216 | -} |
217 | - |
218 | -#endif // MIR_SIGNAL_DISPATCHER_H_ |
219 | |
220 | === modified file 'src/server/CMakeLists.txt' |
221 | --- src/server/CMakeLists.txt 2013-04-08 02:55:16 +0000 |
222 | +++ src/server/CMakeLists.txt 2013-04-08 16:58:20 +0000 |
223 | @@ -25,7 +25,7 @@ |
224 | run_mir.cpp |
225 | display_server.cpp |
226 | default_server_configuration.cpp |
227 | - signal_dispatcher.cpp |
228 | + asio_main_loop.cpp |
229 | ) |
230 | |
231 | set(MIRSERVER_LINKAGE SHARED) |
232 | |
233 | === added file 'src/server/asio_main_loop.cpp' |
234 | --- src/server/asio_main_loop.cpp 1970-01-01 00:00:00 +0000 |
235 | +++ src/server/asio_main_loop.cpp 2013-04-08 16:58:20 +0000 |
236 | @@ -0,0 +1,96 @@ |
237 | +/* |
238 | + * Copyright © 2013 Canonical Ltd. |
239 | + * |
240 | + * This program is free software: you can redistribute it and/or modify it |
241 | + * under the terms of the GNU Lesser General Public License version 3, |
242 | + * as published by the Free Software Foundation. |
243 | + * |
244 | + * This program is distributed in the hope that it will be useful, |
245 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
246 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
247 | + * GNU General Public License for more details. |
248 | + * |
249 | + * You should have received a copy of the GNU Lesser General Public License |
250 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
251 | + * |
252 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
253 | + */ |
254 | + |
255 | +#include "mir/asio_main_loop.h" |
256 | + |
257 | +#include <cassert> |
258 | + |
259 | +class mir::AsioMainLoop::SignalHandler |
260 | +{ |
261 | +public: |
262 | + SignalHandler(boost::asio::io_service& io, |
263 | + std::initializer_list<int> signals, |
264 | + std::function<void(int)> const& handler) |
265 | + : signal_set{io}, |
266 | + handler{handler} |
267 | + { |
268 | + for (auto sig : signals) |
269 | + signal_set.add(sig); |
270 | + } |
271 | + |
272 | + void async_wait() |
273 | + { |
274 | + signal_set.async_wait( |
275 | + std::bind(&SignalHandler::handle, this, |
276 | + std::placeholders::_1, std::placeholders::_2)); |
277 | + } |
278 | + |
279 | +private: |
280 | + void handle(boost::system::error_code err, int sig) |
281 | + { |
282 | + if (!err) |
283 | + { |
284 | + handler(sig); |
285 | + signal_set.async_wait( |
286 | + std::bind(&SignalHandler::handle, this, |
287 | + std::placeholders::_1, std::placeholders::_2)); |
288 | + } |
289 | + } |
290 | + |
291 | + boost::asio::signal_set signal_set; |
292 | + std::function<void(int)> handler; |
293 | +}; |
294 | + |
295 | +/* |
296 | + * We need to define an empty constructor and destructor in the .cpp file, |
297 | + * so that we can use unique_ptr to hold SignalHandler. Otherwise, users |
298 | + * of AsioMainLoop end up creating default constructors and destructors |
299 | + * that don't have complete type information for SignalHandler and fail |
300 | + * to compile. |
301 | + */ |
302 | +mir::AsioMainLoop::AsioMainLoop() |
303 | +{ |
304 | +} |
305 | + |
306 | +mir::AsioMainLoop::~AsioMainLoop() noexcept(true) |
307 | +{ |
308 | +} |
309 | + |
310 | +void mir::AsioMainLoop::run() |
311 | +{ |
312 | + io.run(); |
313 | +} |
314 | + |
315 | +void mir::AsioMainLoop::stop() |
316 | +{ |
317 | + io.stop(); |
318 | +} |
319 | + |
320 | +void mir::AsioMainLoop::register_signal_handler( |
321 | + std::initializer_list<int> signals, |
322 | + std::function<void(int)> const& handler) |
323 | +{ |
324 | + assert(handler); |
325 | + |
326 | + auto sig_handler = std::unique_ptr<SignalHandler>{ |
327 | + new SignalHandler{io, signals, handler}}; |
328 | + |
329 | + sig_handler->async_wait(); |
330 | + |
331 | + signal_handlers.push_back(std::move(sig_handler)); |
332 | +} |
333 | |
334 | === modified file 'src/server/default_server_configuration.cpp' |
335 | --- src/server/default_server_configuration.cpp 2013-04-02 20:56:09 +0000 |
336 | +++ src/server/default_server_configuration.cpp 2013-04-08 16:58:20 +0000 |
337 | @@ -18,6 +18,7 @@ |
338 | |
339 | #include "mir/default_server_configuration.h" |
340 | #include "mir/abnormal_exit.h" |
341 | +#include "mir/asio_main_loop.h" |
342 | |
343 | #include "mir/options/program_option.h" |
344 | #include "mir/compositor/buffer_allocation_strategy.h" |
345 | @@ -515,3 +516,12 @@ |
346 | return std::make_shared<mir::time::HighResolutionClock>(); |
347 | }); |
348 | } |
349 | + |
350 | +std::shared_ptr<mir::MainLoop> mir::DefaultServerConfiguration::the_main_loop() |
351 | +{ |
352 | + return main_loop( |
353 | + []() |
354 | + { |
355 | + return std::make_shared<mir::AsioMainLoop>(); |
356 | + }); |
357 | +} |
358 | |
359 | === modified file 'src/server/display_server.cpp' |
360 | --- src/server/display_server.cpp 2013-03-21 03:32:59 +0000 |
361 | +++ src/server/display_server.cpp 2013-04-08 16:58:20 +0000 |
362 | @@ -20,6 +20,7 @@ |
363 | |
364 | #include "mir/display_server.h" |
365 | #include "mir/server_configuration.h" |
366 | +#include "mir/main_loop.h" |
367 | |
368 | #include "mir/compositor/compositor.h" |
369 | #include "mir/frontend/shell.h" |
370 | @@ -27,11 +28,6 @@ |
371 | #include "mir/graphics/display.h" |
372 | #include "mir/input/input_manager.h" |
373 | |
374 | -#include <mutex> |
375 | -#include <thread> |
376 | -#include <mutex> |
377 | -#include <condition_variable> |
378 | - |
379 | namespace mc = mir::compositor; |
380 | namespace mf = mir::frontend; |
381 | namespace mg = mir::graphics; |
382 | @@ -45,7 +41,7 @@ |
383 | shell{config.the_frontend_shell()}, |
384 | communicator{config.the_communicator()}, |
385 | input_manager{config.the_input_manager()}, |
386 | - exit(false) |
387 | + main_loop{config.the_main_loop()} |
388 | { |
389 | } |
390 | |
391 | @@ -54,9 +50,7 @@ |
392 | std::shared_ptr<frontend::Shell> shell; |
393 | std::shared_ptr<mf::Communicator> communicator; |
394 | std::shared_ptr<mi::InputManager> input_manager; |
395 | - std::mutex exit_guard; |
396 | - std::condition_variable exit_cv; |
397 | - bool exit; |
398 | + std::shared_ptr<mir::MainLoop> main_loop; |
399 | }; |
400 | |
401 | mir::DisplayServer::DisplayServer(ServerConfiguration& config) : |
402 | @@ -72,14 +66,11 @@ |
403 | |
404 | void mir::DisplayServer::run() |
405 | { |
406 | - std::unique_lock<std::mutex> lk(p->exit_guard); |
407 | - |
408 | p->communicator->start(); |
409 | p->compositor->start(); |
410 | p->input_manager->start(); |
411 | |
412 | - while (!p->exit) |
413 | - p->exit_cv.wait(lk); |
414 | + p->main_loop->run(); |
415 | |
416 | p->input_manager->stop(); |
417 | p->compositor->stop(); |
418 | @@ -87,7 +78,5 @@ |
419 | |
420 | void mir::DisplayServer::stop() |
421 | { |
422 | - std::unique_lock<std::mutex> lk(p->exit_guard); |
423 | - p->exit = true; |
424 | - p->exit_cv.notify_one(); |
425 | + p->main_loop->stop(); |
426 | } |
427 | |
428 | === modified file 'src/server/run_mir.cpp' |
429 | --- src/server/run_mir.cpp 2013-03-28 12:52:24 +0000 |
430 | +++ src/server/run_mir.cpp 2013-04-08 16:58:20 +0000 |
431 | @@ -18,34 +18,27 @@ |
432 | |
433 | #include "mir/run_mir.h" |
434 | #include "mir/display_server.h" |
435 | -#include "mir/signal_dispatcher.h" |
436 | +#include "mir/main_loop.h" |
437 | +#include "mir/server_configuration.h" |
438 | |
439 | -#include <atomic> |
440 | -#include <thread> |
441 | #include <csignal> |
442 | - |
443 | -namespace |
444 | -{ |
445 | -std::atomic<mir::DisplayServer*> signal_display_server; |
446 | - |
447 | -void signal_terminate(int) |
448 | -{ |
449 | - while (!signal_display_server.load()) |
450 | - std::this_thread::yield(); |
451 | - |
452 | - signal_display_server.load()->stop(); |
453 | -} |
454 | -} |
455 | +#include <cassert> |
456 | |
457 | void mir::run_mir(ServerConfiguration& config, std::function<void(DisplayServer&)> init) |
458 | { |
459 | - auto const sdp = SignalDispatcher::instance(); |
460 | - sdp->enable_for(SIGINT); |
461 | - sdp->enable_for(SIGTERM); |
462 | - sdp->signal_channel().connect(&signal_terminate); |
463 | + DisplayServer* server_ptr{nullptr}; |
464 | + auto main_loop = config.the_main_loop(); |
465 | + |
466 | + main_loop->register_signal_handler( |
467 | + {SIGINT, SIGTERM}, |
468 | + [&server_ptr](int) |
469 | + { |
470 | + assert(server_ptr); |
471 | + server_ptr->stop(); |
472 | + }); |
473 | |
474 | DisplayServer server(config); |
475 | - signal_display_server.store(&server); |
476 | + server_ptr = &server; |
477 | |
478 | init(server); |
479 | server.run(); |
480 | |
481 | === removed file 'src/server/signal_dispatcher.cpp' |
482 | --- src/server/signal_dispatcher.cpp 2013-04-02 15:43:52 +0000 |
483 | +++ src/server/signal_dispatcher.cpp 1970-01-01 00:00:00 +0000 |
484 | @@ -1,170 +0,0 @@ |
485 | -/* |
486 | - * Copyright © 2012 Canonical Ltd. |
487 | - * |
488 | - * This program is free software: you can redistribute it and/or modify |
489 | - * it under the terms of the GNU General Public License version 3 as |
490 | - * published by the Free Software Foundation. |
491 | - * |
492 | - * This program is distributed in the hope that it will be useful, |
493 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
494 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
495 | - * GNU General Public License for more details. |
496 | - * |
497 | - * You should have received a copy of the GNU General Public License |
498 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
499 | - * |
500 | - * Authored by: Thomas Voss <thomas.voss@canonical.com> |
501 | - */ |
502 | - |
503 | -#include "mir/signal_dispatcher.h" |
504 | - |
505 | -#include <mutex> |
506 | -#include <thread> |
507 | -#include <signal.h> |
508 | -#include <sys/types.h> |
509 | -#include <sys/socket.h> |
510 | - |
511 | - |
512 | -namespace |
513 | -{ |
514 | - |
515 | -struct EventSocketPair |
516 | -{ |
517 | - typedef unsigned int SocketIdentifier; |
518 | - |
519 | - static const SocketIdentifier socket_a = 0; |
520 | - static const SocketIdentifier socket_b = 1; |
521 | - |
522 | - EventSocketPair() |
523 | - { |
524 | - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) |
525 | - throw std::runtime_error("Couldn't create socketpair"); |
526 | - } |
527 | - |
528 | - ~EventSocketPair() |
529 | - { |
530 | - ::close(sockets[0]); |
531 | - ::close(sockets[1]); |
532 | - } |
533 | - |
534 | - void write_signal_to(SocketIdentifier id, int signal) |
535 | - { |
536 | - if (::write(sockets[id], &signal, sizeof(signal)) < 0) |
537 | - { |
538 | - // TODO: We cannot throw an exception in code that is |
539 | - // called from a signal handler. Reenable once we have |
540 | - // signal-safe logging in place. |
541 | - // throw std::runtime_error("Problem writing to socket"); |
542 | - } |
543 | - } |
544 | - |
545 | - int read_signal_from(SocketIdentifier id) |
546 | - { |
547 | - int signal{0}; |
548 | - ssize_t nread{0}; |
549 | - |
550 | - while (nread <= 0) |
551 | - { |
552 | - nread = ::read(sockets[id], &signal, sizeof(signal)); |
553 | - |
554 | - if (nread == 0) |
555 | - throw std::runtime_error("Write end of socket has been closed"); |
556 | - else if (nread > 0 && nread < static_cast<ssize_t>(sizeof(signal))) |
557 | - throw std::runtime_error("Incomplete message retrieved"); |
558 | - else if (nread < 0 && errno != EINTR) |
559 | - throw std::runtime_error("Problem reading from socket"); |
560 | - } |
561 | - |
562 | - return signal; |
563 | - } |
564 | - |
565 | - int sockets[2]; |
566 | -} event_socket_pair; |
567 | - |
568 | -} |
569 | - |
570 | -struct mir::SignalDispatcher::Constructor |
571 | -{ |
572 | - static SignalDispatcher* construct() |
573 | - { |
574 | - return new SignalDispatcher(); |
575 | - } |
576 | -}; |
577 | - |
578 | -namespace |
579 | -{ |
580 | -std::shared_ptr<mir::SignalDispatcher> instance; |
581 | -std::once_flag init_flag; |
582 | - |
583 | -void init() |
584 | -{ |
585 | - instance.reset(mir::SignalDispatcher::Constructor::construct()); |
586 | -} |
587 | - |
588 | -void signal_handler(int signal) |
589 | -{ |
590 | - event_socket_pair.write_signal_to( |
591 | - EventSocketPair::socket_a, |
592 | - signal); |
593 | -} |
594 | - |
595 | -} |
596 | - |
597 | -struct mir::SignalDispatcher::Private |
598 | -{ |
599 | - Private() : worker_thread(std::bind(&Private::worker, this)) |
600 | - { |
601 | - worker_thread.detach(); |
602 | - } |
603 | - |
604 | - void worker() |
605 | - { |
606 | - while(true) |
607 | - { |
608 | - // Todo: Use polling here. |
609 | - try |
610 | - { |
611 | - int s = event_socket_pair.read_signal_from( |
612 | - EventSocketPair::socket_b); |
613 | - signal_channel(s); |
614 | - } catch(...) |
615 | - { |
616 | - break; |
617 | - } |
618 | - } |
619 | - } |
620 | - |
621 | - std::thread worker_thread; |
622 | - mir::SignalDispatcher::SignalType signal_channel; |
623 | -}; |
624 | - |
625 | -std::shared_ptr<mir::SignalDispatcher> mir::SignalDispatcher::instance() |
626 | -{ |
627 | - std::call_once(::init_flag, ::init); |
628 | - |
629 | - return ::instance; |
630 | -} |
631 | - |
632 | -mir::SignalDispatcher::SignalDispatcher() : p(new Private()) |
633 | -{ |
634 | -} |
635 | - |
636 | -mir::SignalDispatcher::~SignalDispatcher() |
637 | -{ |
638 | -} |
639 | - |
640 | -mir::SignalDispatcher::SignalType& mir::SignalDispatcher::signal_channel() |
641 | -{ |
642 | - return p->signal_channel; |
643 | -} |
644 | - |
645 | -void mir::SignalDispatcher::enable_for(int signal) |
646 | -{ |
647 | - struct sigaction action; |
648 | - ::memset(&action, 0, sizeof(action)); |
649 | - ::sigemptyset(&action.sa_mask); |
650 | - ::sigaddset(&action.sa_mask, signal); |
651 | - action.sa_handler = ::signal_handler; |
652 | - action.sa_flags = SA_RESTART | SA_NODEFER; |
653 | - ::sigaction(signal, &action, NULL); |
654 | -} |
655 | |
656 | === modified file 'tests/integration-tests/CMakeLists.txt' |
657 | --- tests/integration-tests/CMakeLists.txt 2013-04-02 21:01:03 +0000 |
658 | +++ tests/integration-tests/CMakeLists.txt 2013-04-08 16:58:20 +0000 |
659 | @@ -5,6 +5,7 @@ |
660 | test_surfaceloop.cpp |
661 | test_error_reporting.cpp |
662 | test_display_info.cpp |
663 | + test_display_server_main_loop_events.cpp |
664 | ) |
665 | |
666 | add_subdirectory(client/) |
667 | |
668 | === modified file 'tests/integration-tests/cucumber/test_session_management_context.cpp' |
669 | --- tests/integration-tests/cucumber/test_session_management_context.cpp 2013-03-29 16:51:35 +0000 |
670 | +++ tests/integration-tests/cucumber/test_session_management_context.cpp 2013-04-08 16:58:20 +0000 |
671 | @@ -52,6 +52,7 @@ |
672 | MOCK_METHOD0(the_input_manager, std::shared_ptr<mi::InputManager>()); |
673 | MOCK_METHOD0(the_display, std::shared_ptr<mg::Display>()); |
674 | MOCK_METHOD0(the_compositor, std::shared_ptr<mc::Compositor>()); |
675 | + MOCK_METHOD0(the_main_loop, std::shared_ptr<mir::MainLoop>()); |
676 | }; |
677 | |
678 | MATCHER_P(NamedWindowWithNoGeometry, name, "") |
679 | |
680 | === modified file 'tests/integration-tests/process/CMakeLists.txt' |
681 | --- tests/integration-tests/process/CMakeLists.txt 2013-03-13 04:54:15 +0000 |
682 | +++ tests/integration-tests/process/CMakeLists.txt 2013-04-08 16:58:20 +0000 |
683 | @@ -1,6 +1,5 @@ |
684 | list(APPEND INTEGRATION_TESTS_SRCS |
685 | ${CMAKE_CURRENT_SOURCE_DIR}/test_process.cpp |
686 | - ${CMAKE_CURRENT_SOURCE_DIR}/test_signal_dispatcher.cpp |
687 | ) |
688 | |
689 | set( |
690 | |
691 | === removed file 'tests/integration-tests/process/test_signal_dispatcher.cpp' |
692 | --- tests/integration-tests/process/test_signal_dispatcher.cpp 2013-03-28 12:13:18 +0000 |
693 | +++ tests/integration-tests/process/test_signal_dispatcher.cpp 1970-01-01 00:00:00 +0000 |
694 | @@ -1,121 +0,0 @@ |
695 | -/* |
696 | - * Copyright © 2012 Canonical Ltd. |
697 | - * |
698 | - * This program is free software: you can redistribute it and/or modify |
699 | - * it under the terms of the GNU General Public License version 3 as |
700 | - * published by the Free Software Foundation. |
701 | - * |
702 | - * This program is distributed in the hope that it will be useful, |
703 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
704 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
705 | - * GNU General Public License for more details. |
706 | - * |
707 | - * You should have received a copy of the GNU General Public License |
708 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
709 | - * |
710 | - * Authored by: Thomas Voss <thomas.voss@canonical.com> |
711 | - */ |
712 | - |
713 | -#include "mir/signal_dispatcher.h" |
714 | -#include "mir_test_framework/process.h" |
715 | - |
716 | -#include <chrono> |
717 | -#include <thread> |
718 | -#include <mutex> |
719 | -#include <condition_variable> |
720 | -#include <gtest/gtest.h> |
721 | - |
722 | -namespace mtf = mir_test_framework; |
723 | - |
724 | -struct SignalCollector |
725 | -{ |
726 | - SignalCollector() : signal(-1) |
727 | - { |
728 | - } |
729 | - |
730 | - void operator()(int s) |
731 | - { |
732 | - std::unique_lock<std::mutex> lock(mutex); |
733 | - signal = s; |
734 | - cv.notify_all(); |
735 | - } |
736 | - |
737 | - int load() |
738 | - { |
739 | - unsigned int counter = 0; |
740 | - std::unique_lock<std::mutex> lock(mutex); |
741 | - while (signal == -1 && counter < 100) |
742 | - { |
743 | - cv.wait_for(lock, std::chrono::milliseconds(10)); |
744 | - counter++; |
745 | - } |
746 | - return signal; |
747 | - } |
748 | - |
749 | - int signal; |
750 | - |
751 | - std::mutex mutex; |
752 | - std::condition_variable cv; |
753 | - |
754 | - SignalCollector(SignalCollector const&) = delete; |
755 | -}; |
756 | - |
757 | -void a_main_function_accessing_the_signal_dispatcher() |
758 | -{ |
759 | - // Ensure that the SignalDispatcher has been created. |
760 | - mir::SignalDispatcher::instance(); |
761 | - // Don't return (and exit) before the parent process has a chance to signal |
762 | - std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
763 | -} |
764 | - |
765 | -template<int signal> |
766 | -void a_main_function_collecting_received_signals() |
767 | -{ |
768 | - mir::SignalDispatcher::instance()->enable_for(signal); |
769 | - SignalCollector sc; |
770 | - boost::signals2::scoped_connection conn( |
771 | - mir::SignalDispatcher::instance()->signal_channel().connect(boost::ref(sc))); |
772 | - |
773 | - ::kill(getpid(), signal); |
774 | - |
775 | - EXPECT_EQ(signal, sc.load()); |
776 | -} |
777 | - |
778 | -int a_successful_exit_function() |
779 | -{ |
780 | - return EXIT_SUCCESS; |
781 | -} |
782 | - |
783 | -int a_gtest_exit_function() |
784 | -{ |
785 | - return ::testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS; |
786 | -} |
787 | - |
788 | - |
789 | -TEST(SignalDispatcher, |
790 | - DISABLED_a_default_dispatcher_does_not_catch_any_signals) |
791 | -{ |
792 | - auto p = mtf::fork_and_run_in_a_different_process( |
793 | - a_main_function_accessing_the_signal_dispatcher, |
794 | - a_successful_exit_function); |
795 | - |
796 | - p->terminate(); |
797 | - |
798 | - EXPECT_TRUE(p->wait_for_termination().signalled()); |
799 | -} |
800 | - |
801 | -TEST(SignalDispatcher, |
802 | - DISABLED_enabling_a_signal_results_in_signal_channel_delivering_the_signal) |
803 | -{ |
804 | - auto p = mtf::fork_and_run_in_a_different_process( |
805 | - a_main_function_collecting_received_signals<SIGTERM>, |
806 | - a_successful_exit_function); |
807 | - |
808 | - EXPECT_TRUE(p->wait_for_termination().succeeded()); |
809 | - |
810 | - p = mtf::fork_and_run_in_a_different_process( |
811 | - a_main_function_collecting_received_signals<SIGINT>, |
812 | - a_successful_exit_function); |
813 | - |
814 | - EXPECT_TRUE(p->wait_for_termination().succeeded()); |
815 | -} |
816 | |
817 | === added file 'tests/integration-tests/test_display_server_main_loop_events.cpp' |
818 | --- tests/integration-tests/test_display_server_main_loop_events.cpp 1970-01-01 00:00:00 +0000 |
819 | +++ tests/integration-tests/test_display_server_main_loop_events.cpp 2013-04-08 16:58:20 +0000 |
820 | @@ -0,0 +1,102 @@ |
821 | +/* |
822 | + * Copyright © 2013 Canonical Ltd. |
823 | + * |
824 | + * This program is free software: you can redistribute it and/or modify |
825 | + * it under the terms of the GNU General Public License version 3 as |
826 | + * published by the Free Software Foundation. |
827 | + * |
828 | + * This program is distributed in the hope that it will be useful, |
829 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
830 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
831 | + * GNU General Public License for more details. |
832 | + * |
833 | + * You should have received a copy of the GNU General Public License |
834 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
835 | + * |
836 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
837 | + */ |
838 | + |
839 | +#include "mir/compositor/compositor.h" |
840 | + |
841 | +#include "mir_test_framework/testing_server_configuration.h" |
842 | +#include "mir_test_doubles/mock_input_manager.h" |
843 | +#include "mir/run_mir.h" |
844 | + |
845 | +#include <gtest/gtest.h> |
846 | +#include <gmock/gmock.h> |
847 | + |
848 | +#include <sys/types.h> |
849 | +#include <unistd.h> |
850 | + |
851 | +namespace mi = mir::input; |
852 | +namespace mc = mir::compositor; |
853 | +namespace mtd = mir::test::doubles; |
854 | +namespace mtf = mir_test_framework; |
855 | + |
856 | +namespace |
857 | +{ |
858 | + |
859 | +class MockCompositor : public mc::Compositor |
860 | +{ |
861 | +public: |
862 | + MOCK_METHOD0(start, void()); |
863 | + MOCK_METHOD0(stop, void()); |
864 | +}; |
865 | + |
866 | +class ServerConfig : public mtf::TestingServerConfiguration |
867 | +{ |
868 | +public: |
869 | + std::shared_ptr<mi::InputManager> the_input_manager() override |
870 | + { |
871 | + if (!mock_input_manager) |
872 | + { |
873 | + mock_input_manager = std::make_shared<mtd::MockInputManager>(); |
874 | + |
875 | + EXPECT_CALL(*mock_input_manager, start()).Times(1); |
876 | + EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
877 | + } |
878 | + |
879 | + return mock_input_manager; |
880 | + } |
881 | + |
882 | + std::shared_ptr<mc::Compositor> the_compositor() override |
883 | + { |
884 | + if (!mock_compositor) |
885 | + { |
886 | + mock_compositor = std::make_shared<MockCompositor>(); |
887 | + |
888 | + EXPECT_CALL(*mock_compositor, start()).Times(1); |
889 | + EXPECT_CALL(*mock_compositor, stop()).Times(1); |
890 | + } |
891 | + |
892 | + return mock_compositor; |
893 | + } |
894 | + |
895 | +private: |
896 | + std::shared_ptr<mtd::MockInputManager> mock_input_manager; |
897 | + std::shared_ptr<MockCompositor> mock_compositor; |
898 | +}; |
899 | + |
900 | +} |
901 | + |
902 | +TEST(DisplayServerMainLoopEvents, display_server_shuts_down_properly_on_sigint) |
903 | +{ |
904 | + ServerConfig server_config; |
905 | + |
906 | + mir::run_mir(server_config, |
907 | + [](mir::DisplayServer&) |
908 | + { |
909 | + kill(getpid(), SIGINT); |
910 | + }); |
911 | +} |
912 | + |
913 | +TEST(DisplayServerMainLoopEvents, display_server_shuts_down_properly_on_sigterm) |
914 | +{ |
915 | + ServerConfig server_config; |
916 | + |
917 | + mir::run_mir(server_config, |
918 | + [](mir::DisplayServer&) |
919 | + { |
920 | + kill(getpid(), SIGTERM); |
921 | + }); |
922 | +} |
923 | |
924 | === modified file 'tests/unit-tests/CMakeLists.txt' |
925 | --- tests/unit-tests/CMakeLists.txt 2013-04-02 21:01:03 +0000 |
926 | +++ tests/unit-tests/CMakeLists.txt 2013-04-08 16:58:20 +0000 |
927 | @@ -1,7 +1,12 @@ |
928 | add_definitions(-DTEST_RECORDINGS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/input_recordings/") |
929 | include_directories(${DRM_INCLUDE_DIRS} ${GBM_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}) |
930 | |
931 | -set(UNIT_TEST_SOURCES test_gmock_fixes.cpp) |
932 | +set( |
933 | + UNIT_TEST_SOURCES |
934 | + |
935 | + test_gmock_fixes.cpp |
936 | + test_asio_main_loop.cpp |
937 | +) |
938 | |
939 | add_subdirectory(options/) |
940 | add_subdirectory(client/) |
941 | |
942 | === added file 'tests/unit-tests/test_asio_main_loop.cpp' |
943 | --- tests/unit-tests/test_asio_main_loop.cpp 1970-01-01 00:00:00 +0000 |
944 | +++ tests/unit-tests/test_asio_main_loop.cpp 2013-04-08 16:58:20 +0000 |
945 | @@ -0,0 +1,144 @@ |
946 | +/* |
947 | + * Copyright © 2013 Canonical Ltd. |
948 | + * |
949 | + * This program is free software: you can redistribute it and/or modify |
950 | + * it under the terms of the GNU General Public License version 3 as |
951 | + * published by the Free Software Foundation. |
952 | + * |
953 | + * This program is distributed in the hope that it will be useful, |
954 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
955 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
956 | + * GNU General Public License for more details. |
957 | + * |
958 | + * You should have received a copy of the GNU General Public License |
959 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
960 | + * |
961 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
962 | + */ |
963 | + |
964 | +#include "mir/asio_main_loop.h" |
965 | + |
966 | +#include <gtest/gtest.h> |
967 | + |
968 | +#include <thread> |
969 | +#include <atomic> |
970 | + |
971 | +#include <sys/types.h> |
972 | +#include <unistd.h> |
973 | + |
974 | +TEST(AsioMainLoopTest, signal_handled) |
975 | +{ |
976 | + int const signum{SIGUSR1}; |
977 | + int handled_signum{0}; |
978 | + |
979 | + mir::AsioMainLoop ml; |
980 | + |
981 | + ml.register_signal_handler( |
982 | + {signum}, |
983 | + [&handled_signum, &ml](int sig) |
984 | + { |
985 | + handled_signum = sig; |
986 | + ml.stop(); |
987 | + }); |
988 | + |
989 | + kill(getpid(), signum); |
990 | + |
991 | + ml.run(); |
992 | + |
993 | + ASSERT_EQ(signum, handled_signum); |
994 | +} |
995 | + |
996 | + |
997 | +TEST(AsioMainLoopTest, multiple_signals_handled) |
998 | +{ |
999 | + std::vector<int> const signals{SIGUSR1, SIGUSR2}; |
1000 | + size_t const num_signals_to_send{10}; |
1001 | + std::vector<int> handled_signals; |
1002 | + std::atomic<unsigned int> num_handled_signals{0}; |
1003 | + |
1004 | + mir::AsioMainLoop ml; |
1005 | + |
1006 | + ml.register_signal_handler( |
1007 | + {signals[0], signals[1]}, |
1008 | + [&handled_signals, &num_handled_signals](int sig) |
1009 | + { |
1010 | + handled_signals.push_back(sig); |
1011 | + ++num_handled_signals; |
1012 | + }); |
1013 | + |
1014 | + |
1015 | + std::thread signal_sending_thread( |
1016 | + [&ml, num_signals_to_send, &signals, &num_handled_signals] |
1017 | + { |
1018 | + for (size_t i = 0; i < num_signals_to_send; i++) |
1019 | + { |
1020 | + kill(getpid(), signals[i % signals.size()]); |
1021 | + while (num_handled_signals <= i) std::this_thread::yield(); |
1022 | + } |
1023 | + ml.stop(); |
1024 | + }); |
1025 | + |
1026 | + ml.run(); |
1027 | + |
1028 | + signal_sending_thread.join(); |
1029 | + |
1030 | + ASSERT_EQ(num_signals_to_send, handled_signals.size()); |
1031 | + |
1032 | + for (size_t i = 0; i < num_signals_to_send; i++) |
1033 | + ASSERT_EQ(signals[i % signals.size()], handled_signals[i]) << " index " << i; |
1034 | +} |
1035 | + |
1036 | +TEST(AsioMainLoopTest, all_registered_handlers_are_called) |
1037 | +{ |
1038 | + int const signum{SIGUSR1}; |
1039 | + std::vector<int> handled_signum{0,0,0}; |
1040 | + |
1041 | + mir::AsioMainLoop ml; |
1042 | + |
1043 | + ml.register_signal_handler( |
1044 | + {signum}, |
1045 | + [&handled_signum, &ml](int sig) |
1046 | + { |
1047 | + handled_signum[0] = sig; |
1048 | + if (handled_signum[0] != 0 && |
1049 | + handled_signum[1] != 0 && |
1050 | + handled_signum[2] != 0) |
1051 | + { |
1052 | + ml.stop(); |
1053 | + } |
1054 | + }); |
1055 | + |
1056 | + ml.register_signal_handler( |
1057 | + {signum}, |
1058 | + [&handled_signum, &ml](int sig) |
1059 | + { |
1060 | + handled_signum[1] = sig; |
1061 | + if (handled_signum[0] != 0 && |
1062 | + handled_signum[1] != 0 && |
1063 | + handled_signum[2] != 0) |
1064 | + { |
1065 | + ml.stop(); |
1066 | + } |
1067 | + }); |
1068 | + |
1069 | + ml.register_signal_handler( |
1070 | + {signum}, |
1071 | + [&handled_signum, &ml](int sig) |
1072 | + { |
1073 | + handled_signum[2] = sig; |
1074 | + if (handled_signum[0] != 0 && |
1075 | + handled_signum[1] != 0 && |
1076 | + handled_signum[2] != 0) |
1077 | + { |
1078 | + ml.stop(); |
1079 | + } |
1080 | + }); |
1081 | + |
1082 | + kill(getpid(), signum); |
1083 | + |
1084 | + ml.run(); |
1085 | + |
1086 | + ASSERT_EQ(signum, handled_signum[0]); |
1087 | + ASSERT_EQ(signum, handled_signum[1]); |
1088 | + ASSERT_EQ(signum, handled_signum[2]); |
1089 | +} |
PASSED: Continuous integration, rev:570 jenkins. qa.ubuntu. com/job/ mir-ci/ 322/ jenkins. qa.ubuntu. com/job/ mir-android- raring- i386-build/ 294 jenkins. qa.ubuntu. com/job/ mir-clang- raring- amd64-build/ 177 jenkins. qa.ubuntu. com/job/ mir-quantal- amd64-ci/ 327 jenkins. qa.ubuntu. com/job/ mir-quantal- amd64-ci/ 327/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-vm- ci-build/ ./distribution= precise, flavor= amd64/135
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ mir-ci/ 322/rebuild
http://