Merge lp:~thomas-voss/location-service/factor-out-runtime into lp:location-service/15.04
- factor-out-runtime
- Merge into 15.04
Proposed by
Thomas Voß
Status: | Merged |
---|---|
Approved by: | Alberto Mardegan |
Approved revision: | 138 |
Merged at revision: | 205 |
Proposed branch: | lp:~thomas-voss/location-service/factor-out-runtime |
Merge into: | lp:location-service/15.04 |
Diff against target: |
613 lines (+351/-72) 9 files modified
src/location_service/com/ubuntu/location/CMakeLists.txt (+1/-0) src/location_service/com/ubuntu/location/service/daemon.cpp (+10/-43) src/location_service/com/ubuntu/location/service/provider_daemon.cpp (+11/-16) src/location_service/com/ubuntu/location/service/runtime.cpp (+109/-0) src/location_service/com/ubuntu/location/service/runtime.h (+90/-0) tests/CMakeLists.txt (+1/-0) tests/acceptance_tests.cpp (+10/-12) tests/position_test.cpp (+1/-1) tests/runtime_test.cpp (+118/-0) |
To merge this branch: | bzr merge lp:~thomas-voss/location-service/factor-out-runtime |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Mardegan (community) | Approve | ||
Review via email: mp+278674@code.launchpad.net |
This proposal supersedes a proposal from 2015-11-19.
Commit message
- Factor out service::Runtime from daemon.cpp into its own .h/.cpp pair of files.
- Add test cases around correct operation of service::Runtime.
Description of the change
- Factor out service::Runtime from daemon.cpp into its own .h/.cpp pair of files.
- Add test cases around correct operation of service::Runtime.
To post a comment you must log in.
- 138. By Thomas Voß
-
Remove dependency on robustify-...
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/location_service/com/ubuntu/location/CMakeLists.txt' |
2 | --- src/location_service/com/ubuntu/location/CMakeLists.txt 2015-04-23 14:48:44 +0000 |
3 | +++ src/location_service/com/ubuntu/location/CMakeLists.txt 2015-11-26 08:30:38 +0000 |
4 | @@ -38,6 +38,7 @@ |
5 | service/harvester.cpp |
6 | service/demultiplexing_reporter.h |
7 | service/demultiplexing_reporter.cpp |
8 | + service/runtime.cpp |
9 | service/runtime_tests.h |
10 | service/runtime_tests.cpp |
11 | service/trust_store_permission_manager.cpp |
12 | |
13 | === modified file 'src/location_service/com/ubuntu/location/service/daemon.cpp' |
14 | --- src/location_service/com/ubuntu/location/service/daemon.cpp 2015-10-23 13:43:32 +0000 |
15 | +++ src/location_service/com/ubuntu/location/service/daemon.cpp 2015-11-26 08:30:38 +0000 |
16 | @@ -20,6 +20,7 @@ |
17 | #include <com/ubuntu/location/boost_ptree_settings.h> |
18 | #include <com/ubuntu/location/provider_factory.h> |
19 | |
20 | +#include <com/ubuntu/location/logging.h> |
21 | #include <com/ubuntu/location/connectivity/dummy_connectivity_manager.h> |
22 | |
23 | #include <com/ubuntu/location/service/default_configuration.h> |
24 | @@ -32,6 +33,7 @@ |
25 | |
26 | #include "program_options.h" |
27 | #include "daemon.h" |
28 | +#include "runtime.h" |
29 | |
30 | #include <core/dbus/announcer.h> |
31 | #include <core/dbus/resolver.h> |
32 | @@ -39,6 +41,8 @@ |
33 | |
34 | #include <core/posix/signal.h> |
35 | |
36 | +#include <boost/asio.hpp> |
37 | + |
38 | #include <system_error> |
39 | #include <thread> |
40 | |
41 | @@ -177,6 +181,8 @@ |
42 | trap->stop(); |
43 | }); |
44 | |
45 | + auto runtime = location::service::Runtime::create(); |
46 | + |
47 | const location::Configuration empty_provider_configuration; |
48 | |
49 | std::set<location::Provider::Ptr> instantiated_providers; |
50 | @@ -203,8 +209,10 @@ |
51 | } |
52 | } |
53 | |
54 | - config.incoming->install_executor(dbus::asio::make_executor(config.incoming)); |
55 | - config.outgoing->install_executor(dbus::asio::make_executor(config.outgoing)); |
56 | + config.incoming->install_executor(dbus::asio::make_executor(config.incoming, runtime->service())); |
57 | + config.outgoing->install_executor(dbus::asio::make_executor(config.outgoing, runtime->service())); |
58 | + |
59 | + runtime->start(); |
60 | |
61 | location::service::DefaultConfiguration dc; |
62 | |
63 | @@ -222,50 +230,9 @@ |
64 | }; |
65 | |
66 | auto location_service = std::make_shared<location::service::Implementation>(configuration); |
67 | - // We need to ensure that any exception raised by the executor does not crash the app |
68 | - // and also gets logged. |
69 | - auto execute = [] (std::shared_ptr<core::dbus::Bus> bus) { |
70 | - while(true) |
71 | - { |
72 | - try |
73 | - { |
74 | - VLOG(10) << "Starting a bus executor"; |
75 | - bus->run(); |
76 | - break; // run() exited normally |
77 | - } |
78 | - catch (const std::exception& e) |
79 | - { |
80 | - LOG(WARNING) << e.what(); |
81 | - } |
82 | - catch (...) |
83 | - { |
84 | - LOG(WARNING) << "Unexpected exception was raised by the bus executor"; |
85 | - } |
86 | - } |
87 | - }; |
88 | - |
89 | - std::thread t1{execute, config.incoming}; |
90 | - std::thread t2{execute, config.incoming}; |
91 | - std::thread t3{execute, config.incoming}; |
92 | - std::thread t4{execute, config.outgoing}; |
93 | |
94 | trap->run(); |
95 | |
96 | - config.incoming->stop(); |
97 | - config.outgoing->stop(); |
98 | - |
99 | - if (t1.joinable()) |
100 | - t1.join(); |
101 | - |
102 | - if (t2.joinable()) |
103 | - t2.join(); |
104 | - |
105 | - if (t3.joinable()) |
106 | - t3.join(); |
107 | - |
108 | - if (t4.joinable()) |
109 | - t4.join(); |
110 | - |
111 | return EXIT_SUCCESS; |
112 | } |
113 | |
114 | |
115 | === modified file 'src/location_service/com/ubuntu/location/service/provider_daemon.cpp' |
116 | --- src/location_service/com/ubuntu/location/service/provider_daemon.cpp 2014-10-27 21:58:16 +0000 |
117 | +++ src/location_service/com/ubuntu/location/service/provider_daemon.cpp 2015-11-26 08:30:38 +0000 |
118 | @@ -25,6 +25,7 @@ |
119 | |
120 | #include <com/ubuntu/location/service/configuration.h> |
121 | #include <com/ubuntu/location/service/program_options.h> |
122 | +#include <com/ubuntu/location/service/runtime.h> |
123 | |
124 | #include <core/dbus/asio/executor.h> |
125 | |
126 | @@ -65,7 +66,6 @@ |
127 | location::service::ProviderDaemon::Configuration result; |
128 | |
129 | result.connection = factory(mutable_daemon_options().bus()); |
130 | - result.connection->install_executor(core::dbus::asio::make_executor(result.connection)); |
131 | |
132 | auto service = core::dbus::Service::add_service( |
133 | result.connection, |
134 | @@ -108,7 +108,7 @@ |
135 | return result; |
136 | } |
137 | |
138 | -int location::service::ProviderDaemon::main(const location::service::ProviderDaemon::Configuration& configuration) |
139 | +int location::service::ProviderDaemon::main(const location::service::ProviderDaemon::Configuration& config) |
140 | { |
141 | auto trap = core::posix::trap_signals_for_all_subsequent_threads( |
142 | { |
143 | @@ -121,27 +121,22 @@ |
144 | trap->stop(); |
145 | }); |
146 | |
147 | - std::thread worker |
148 | - { |
149 | - [configuration]() |
150 | - { |
151 | - configuration.connection->run(); |
152 | - } |
153 | - }; |
154 | + auto runtime = location::service::Runtime::create(1); |
155 | + |
156 | + config.connection->install_executor(core::dbus::asio::make_executor(config.connection, runtime->service())); |
157 | |
158 | auto skeleton = location::providers::remote::skeleton::create_with_configuration(location::providers::remote::skeleton::Configuration |
159 | { |
160 | - configuration.object, |
161 | - configuration.connection, |
162 | - configuration.provider |
163 | + config.object, |
164 | + config.connection, |
165 | + config.provider |
166 | }); |
167 | |
168 | + runtime->start(); |
169 | + |
170 | trap->run(); |
171 | |
172 | - configuration.connection->stop(); |
173 | - |
174 | - if (worker.joinable()) |
175 | - worker.join(); |
176 | + config.connection->stop(); |
177 | |
178 | return EXIT_SUCCESS; |
179 | } |
180 | |
181 | === added file 'src/location_service/com/ubuntu/location/service/runtime.cpp' |
182 | --- src/location_service/com/ubuntu/location/service/runtime.cpp 1970-01-01 00:00:00 +0000 |
183 | +++ src/location_service/com/ubuntu/location/service/runtime.cpp 2015-11-26 08:30:38 +0000 |
184 | @@ -0,0 +1,109 @@ |
185 | +/* |
186 | + * Copyright © 2015 Canonical Ltd. |
187 | + * |
188 | + * This program is free software: you can redistribute it and/or modify it |
189 | + * under the terms of the GNU Lesser General Public License version 3, |
190 | + * as published by the Free Software Foundation. |
191 | + * |
192 | + * This program is distributed in the hope that it will be useful, |
193 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
194 | + * MERCHANTABILITY or FITNESS FOR A PARTIlocationAR PURPOSE. See the |
195 | + * GNU Lesser General Public License for more details. |
196 | + * |
197 | + * You should have received a copy of the GNU Lesser General Public License |
198 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
199 | + * |
200 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
201 | + */ |
202 | +#include <com/ubuntu/location/service/runtime.h> |
203 | + |
204 | +#include <com/ubuntu/location/logging.h> |
205 | + |
206 | +namespace culs = com::ubuntu::location::service; |
207 | + |
208 | +namespace |
209 | +{ |
210 | +// exception_safe_run runs service, catching all exceptions and |
211 | +// restarting operation until an explicit shutdown has been requested. |
212 | +// |
213 | +// TODO(tvoss): Catching all exceptions is risky as they might signal unrecoverable |
214 | +// errors. We should enable calling code to decide whether an exception should be considered |
215 | +// fatal or not. |
216 | +void exception_safe_run(boost::asio::io_service& service) |
217 | +{ |
218 | + while (true) |
219 | + { |
220 | + try |
221 | + { |
222 | + service.run(); |
223 | + // a clean return from run only happens in case of |
224 | + // stop() being called (we are keeping the service alive with |
225 | + // a service::work instance). |
226 | + break; |
227 | + } |
228 | + catch (const std::exception& e) |
229 | + { |
230 | + LOG(WARNING) << e.what(); |
231 | + } |
232 | + catch (...) |
233 | + { |
234 | + LOG(WARNING) << "Unknown exception caught while executing boost::asio::io_service"; |
235 | + } |
236 | + } |
237 | +} |
238 | +} |
239 | + |
240 | +std::shared_ptr<culs::Runtime> culs::Runtime::create(std::uint32_t pool_size) |
241 | +{ |
242 | + return std::shared_ptr<culs::Runtime>(new culs::Runtime(pool_size)); |
243 | +} |
244 | + |
245 | +culs::Runtime::Runtime(std::uint32_t pool_size) |
246 | + : pool_size_{pool_size}, |
247 | + service_{pool_size_}, |
248 | + strand_{service_}, |
249 | + keep_alive_{service_} |
250 | +{ |
251 | +} |
252 | + |
253 | +culs::Runtime::~Runtime() |
254 | +{ |
255 | + try |
256 | + { |
257 | + stop(); |
258 | + } catch(...) |
259 | + { |
260 | + // Dropping all exceptions to satisfy the nothrow guarantee. |
261 | + } |
262 | +} |
263 | + |
264 | +void culs::Runtime::start() |
265 | +{ |
266 | + for (unsigned int i = 0; i < pool_size_; i++) |
267 | + workers_.push_back(std::thread{exception_safe_run, std::ref(service_)}); |
268 | +} |
269 | + |
270 | +void culs::Runtime::stop() |
271 | +{ |
272 | + service_.stop(); |
273 | + |
274 | + for (auto& worker : workers_) |
275 | + if (worker.joinable()) |
276 | + worker.join(); |
277 | +} |
278 | + |
279 | +std::function<void(std::function<void()>)> culs::Runtime::to_dispatcher_functional() |
280 | +{ |
281 | + // We have to make sure that we stay alive for as long as |
282 | + // calling code requires the dispatcher to work. |
283 | + auto sp = shared_from_this(); |
284 | + return [sp](std::function<void()> task) |
285 | + { |
286 | + sp->strand_.post(task); |
287 | + }; |
288 | +} |
289 | + |
290 | +boost::asio::io_service& culs::Runtime::service() |
291 | +{ |
292 | + return service_; |
293 | +} |
294 | |
295 | === added file 'src/location_service/com/ubuntu/location/service/runtime.h' |
296 | --- src/location_service/com/ubuntu/location/service/runtime.h 1970-01-01 00:00:00 +0000 |
297 | +++ src/location_service/com/ubuntu/location/service/runtime.h 2015-11-26 08:30:38 +0000 |
298 | @@ -0,0 +1,90 @@ |
299 | +/* |
300 | + * Copyright © 2015 Canonical Ltd. |
301 | + * |
302 | + * This program is free software: you can redistribute it and/or modify it |
303 | + * under the terms of the GNU Lesser General Public License version 3, |
304 | + * as published by the Free Software Foundation. |
305 | + * |
306 | + * This program is distributed in the hope that it will be useful, |
307 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
308 | + * MERCHANTABILITY or FITNESS FOR A PARTIlocationAR PURPOSE. See the |
309 | + * GNU Lesser General Public License for more details. |
310 | + * |
311 | + * You should have received a copy of the GNU Lesser General Public License |
312 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
313 | + * |
314 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
315 | + */ |
316 | +#ifndef COM_UBUNTU_LOCATION_SERVICE_RUNTIME_H_ |
317 | +#define COM_UBUNTU_LOCATION_SERVICE_RUNTIME_H_ |
318 | + |
319 | +#include <boost/asio.hpp> |
320 | + |
321 | +#include <functional> |
322 | +#include <memory> |
323 | +#include <thread> |
324 | +#include <vector> |
325 | + |
326 | +#include <cstdint> |
327 | + |
328 | +namespace com |
329 | +{ |
330 | +namespace ubuntu |
331 | +{ |
332 | +namespace location |
333 | +{ |
334 | +namespace service |
335 | +{ |
336 | +// We bundle our "global" runtime dependencies here, specifically |
337 | +// a dispatcher to decouple multiple in-process providers from one |
338 | +// another , forcing execution to a well known set of threads. |
339 | +class Runtime : public std::enable_shared_from_this<Runtime> |
340 | +{ |
341 | +public: |
342 | + // Our default concurrency setup. |
343 | + static constexpr const std::uint32_t worker_threads = 2; |
344 | + |
345 | + // create returns a Runtime instance with pool_size worker threads |
346 | + // executing the underlying service. |
347 | + static std::shared_ptr<Runtime> create(std::uint32_t pool_size = worker_threads); |
348 | + |
349 | + Runtime(const Runtime&) = delete; |
350 | + Runtime(Runtime&&) = delete; |
351 | + // Tears down the runtime, stopping all worker threads. |
352 | + ~Runtime() noexcept(true); |
353 | + Runtime& operator=(const Runtime&) = delete; |
354 | + Runtime& operator=(Runtime&&) = delete; |
355 | + |
356 | + // start executes the underlying io_service on a thread pool with |
357 | + // the size configured at creation time. |
358 | + void start(); |
359 | + |
360 | + // stop cleanly shuts down a Runtime instance, |
361 | + // joining all worker threads. |
362 | + void stop(); |
363 | + |
364 | + // to_dispatcher_functional returns a function for integration |
365 | + // with components that expect a dispatcher for operation. |
366 | + std::function<void(std::function<void()>)> to_dispatcher_functional(); |
367 | + |
368 | + // service returns the underlying boost::asio::io_service that is executed |
369 | + // by the Runtime. |
370 | + boost::asio::io_service& service(); |
371 | + |
372 | +private: |
373 | + // Runtime constructs a new instance, firing up pool_size |
374 | + // worker threads. |
375 | + Runtime(std::uint32_t pool_size); |
376 | + |
377 | + std::uint32_t pool_size_; |
378 | + boost::asio::io_service service_; |
379 | + boost::asio::io_service::strand strand_; |
380 | + boost::asio::io_service::work keep_alive_; |
381 | + std::vector<std::thread> workers_; |
382 | +}; |
383 | +} |
384 | +} |
385 | +} |
386 | +} |
387 | + |
388 | +#endif // COM_UBUNTU_LOCATION_SERVICE_RUNTIME_H_ |
389 | |
390 | === modified file 'tests/CMakeLists.txt' |
391 | --- tests/CMakeLists.txt 2015-04-23 17:04:09 +0000 |
392 | +++ tests/CMakeLists.txt 2015-11-26 08:30:38 +0000 |
393 | @@ -92,6 +92,7 @@ |
394 | LOCATION_SERVICE_ADD_TEST(provider_test provider_test.cpp) |
395 | LOCATION_SERVICE_ADD_TEST(wgs84_test wgs84_test.cpp) |
396 | LOCATION_SERVICE_ADD_TEST(trust_store_permission_manager_test trust_store_permission_manager_test.cpp) |
397 | +LOCATION_SERVICE_ADD_TEST(runtime_test runtime_test.cpp) |
398 | |
399 | # Provider-specific test-cases go here. |
400 | if (LOCATION_SERVICE_ENABLE_GPS_PROVIDER) |
401 | |
402 | === modified file 'tests/acceptance_tests.cpp' |
403 | --- tests/acceptance_tests.cpp 2015-02-13 13:19:18 +0000 |
404 | +++ tests/acceptance_tests.cpp 2015-11-26 08:30:38 +0000 |
405 | @@ -38,12 +38,14 @@ |
406 | #include <com/ubuntu/location/service/stub.h> |
407 | |
408 | #include <core/dbus/announcer.h> |
409 | +#include <core/dbus/bus.h> |
410 | #include <core/dbus/fixture.h> |
411 | #include <core/dbus/resolver.h> |
412 | |
413 | #include <core/dbus/asio/executor.h> |
414 | |
415 | #include <core/posix/signal.h> |
416 | +#include <core/posix/this_process.h> |
417 | |
418 | #include <core/testing/cross_process_sync.h> |
419 | #include <core/testing/fork_and_run.h> |
420 | @@ -708,7 +710,7 @@ |
421 | |
422 | options.add(Keys::update_period, |
423 | "Update period length for dummy::Provider setup.", |
424 | - std::uint32_t{100}); |
425 | + std::uint32_t{10}); |
426 | |
427 | options.add(Keys::client_count, |
428 | "Number of clients that should be fired up.", |
429 | @@ -761,6 +763,8 @@ |
430 | }; |
431 | } |
432 | |
433 | +#include "did_finish_successfully.h" |
434 | + |
435 | TEST_F(LocationServiceStandaloneLoad, MultipleClientsConnectingAndDisconnectingWorks) |
436 | { |
437 | EXPECT_TRUE(trust_store_is_set_up_for_testing); |
438 | @@ -801,8 +805,8 @@ |
439 | }; |
440 | |
441 | cul::service::Daemon::Configuration config; |
442 | - config.incoming = session_bus(); |
443 | - config.outgoing = session_bus(); |
444 | + config.incoming = std::make_shared<core::dbus::Bus>(core::posix::this_process::env::get_or_throw("DBUS_SESSION_BUS_ADDRESS")); |
445 | + config.outgoing = std::make_shared<core::dbus::Bus>(core::posix::this_process::env::get_or_throw("DBUS_SESSION_BUS_ADDRESS")); |
446 | config.is_testing_enabled = false; |
447 | config.providers = |
448 | { |
449 | @@ -829,7 +833,7 @@ |
450 | status; |
451 | }, core::posix::StandardStream::empty); |
452 | |
453 | - std::this_thread::sleep_for(std::chrono::seconds{2}); |
454 | + std::this_thread::sleep_for(std::chrono::seconds{15}); |
455 | |
456 | auto client = [this]() |
457 | { |
458 | @@ -957,17 +961,11 @@ |
459 | { |
460 | VLOG(1) << "Stopping client...: " << client.pid(); |
461 | client.send_signal_or_throw(core::posix::Signal::sig_term); |
462 | - auto result = client.wait_for(core::posix::wait::Flags::untraced); |
463 | - |
464 | - EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status); |
465 | - EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status); |
466 | + EXPECT_TRUE(did_finish_successfully(client.wait_for(core::posix::wait::Flags::untraced))); |
467 | } |
468 | |
469 | VLOG(1) << "Cleaned up clients, shutting down the service..."; |
470 | |
471 | server.send_signal_or_throw(core::posix::Signal::sig_term); |
472 | - auto result = server.wait_for(core::posix::wait::Flags::untraced); |
473 | - |
474 | - EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status); |
475 | - EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status); |
476 | + EXPECT_TRUE(did_finish_successfully(server.wait_for(core::posix::wait::Flags::untraced))); |
477 | } |
478 | |
479 | === modified file 'tests/position_test.cpp' |
480 | --- tests/position_test.cpp 2014-06-20 07:40:34 +0000 |
481 | +++ tests/position_test.cpp 2015-11-26 08:30:38 +0000 |
482 | @@ -40,7 +40,7 @@ |
483 | cul::wgs84::Latitude{}, |
484 | cul::wgs84::Longitude{}, |
485 | cul::wgs84::Altitude{}}; |
486 | - EXPECT_TRUE(p.altitude); |
487 | + EXPECT_TRUE(p.altitude ? true : false); |
488 | } |
489 | |
490 | #include <com/ubuntu/location/codec.h> |
491 | |
492 | === added file 'tests/runtime_test.cpp' |
493 | --- tests/runtime_test.cpp 1970-01-01 00:00:00 +0000 |
494 | +++ tests/runtime_test.cpp 2015-11-26 08:30:38 +0000 |
495 | @@ -0,0 +1,118 @@ |
496 | +/* |
497 | + * Copyright © 2015Canonical Ltd. |
498 | + * |
499 | + * This program is free software: you can redistribute it and/or modify it |
500 | + * under the terms of the GNU Lesser General Public License version 3, |
501 | + * as published by the Free Software Foundation. |
502 | + * |
503 | + * This program is distributed in the hope that it will be useful, |
504 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
505 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
506 | + * GNU Lesser General Public License for more details. |
507 | + * |
508 | + * You should have received a copy of the GNU Lesser General Public License |
509 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
510 | + * |
511 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
512 | + */ |
513 | +#include <com/ubuntu/location/service/runtime.h> |
514 | + |
515 | +#include <gtest/gtest.h> |
516 | + |
517 | +#include <condition_variable> |
518 | +#include <thread> |
519 | + |
520 | +namespace culs = com::ubuntu::location::service; |
521 | + |
522 | +TEST(Runtime, cleanly_shuts_down_threads) |
523 | +{ |
524 | + culs::Runtime::create(); |
525 | +} |
526 | + |
527 | +TEST(Runtime, executes_service) |
528 | +{ |
529 | + std::mutex m; |
530 | + std::unique_lock<std::mutex> ul{m}; |
531 | + std::condition_variable wc; |
532 | + |
533 | + bool signaled = false; |
534 | + |
535 | + auto rt = culs::Runtime::create(); |
536 | + rt->start(); |
537 | + boost::asio::deadline_timer timer{rt->service(), boost::posix_time::milliseconds(500)}; |
538 | + timer.async_wait([&wc, &signaled](const boost::system::error_code&) |
539 | + { |
540 | + signaled = true; |
541 | + wc.notify_all(); |
542 | + }); |
543 | + |
544 | + auto result = wc.wait_for(ul, std::chrono::seconds{1}, [&signaled]() { return signaled; }); |
545 | + EXPECT_TRUE(result); |
546 | +} |
547 | + |
548 | +TEST(Runtime, catches_exceptions_thrown_from_handlers) |
549 | +{ |
550 | + std::mutex m; |
551 | + std::unique_lock<std::mutex> ul{m}; |
552 | + std::condition_variable wc; |
553 | + |
554 | + bool signaled = false; |
555 | + |
556 | + auto rt = culs::Runtime::create(); |
557 | + rt->start(); |
558 | + boost::asio::deadline_timer fast{rt->service(), boost::posix_time::milliseconds(100)}; |
559 | + fast.async_wait([](const boost::system::error_code&) |
560 | + { |
561 | + throw std::runtime_error{"Should not propagate"}; |
562 | + }); |
563 | + |
564 | + boost::asio::deadline_timer slow{rt->service(), boost::posix_time::milliseconds(500)}; |
565 | + slow.async_wait([&wc, &signaled](const boost::system::error_code&) |
566 | + { |
567 | + signaled = true; |
568 | + wc.notify_all(); |
569 | + }); |
570 | + |
571 | + auto result = wc.wait_for(ul, std::chrono::seconds{1}, [&signaled]() { return signaled; }); |
572 | + EXPECT_TRUE(result); |
573 | +} |
574 | + |
575 | +// sets_up_pool_of_threads ensures that the pool size |
576 | +// passed to the Runtime on construction is honored. The idea is simple: |
577 | +// We set up two deadline timers, fast and slow. fast fires before slow, |
578 | +// with fast blocking in a wait on a common condition_variable. When slow |
579 | +// fires, it notifies the condition variable, thereby unblocking the handler of fast, |
580 | +// enabling clean shutdown without errors and timeouts. This only works if the |
581 | +// pool contains at least 2 threads. Otherwise, the handler of slow would not be executed |
582 | +// until the handler of fast times out in the wait, marking the test as failed. |
583 | +TEST(Runtime, sets_up_pool_of_threads) |
584 | +{ |
585 | + struct State |
586 | + { |
587 | + bool signaled{false}; |
588 | + std::mutex m; |
589 | + std::condition_variable wc; |
590 | + }; |
591 | + |
592 | + auto state = std::make_shared<State>(); |
593 | + |
594 | + auto rt = culs::Runtime::create(2); |
595 | + rt->start(); |
596 | + boost::asio::deadline_timer fast{rt->service(), boost::posix_time::milliseconds(100)}; |
597 | + fast.async_wait([state](const boost::system::error_code&) |
598 | + { |
599 | + std::unique_lock<std::mutex> ul{state->m}; |
600 | + EXPECT_TRUE(state->wc.wait_for(ul, std::chrono::seconds{1}, [state]() { return state->signaled; })); |
601 | + }); |
602 | + |
603 | + boost::asio::deadline_timer slow{rt->service(), boost::posix_time::milliseconds(500)}; |
604 | + slow.async_wait([state](const boost::system::error_code&) |
605 | + { |
606 | + state->signaled = true; |
607 | + state->wc.notify_all(); |
608 | + }); |
609 | + |
610 | + std::unique_lock<std::mutex> ul{state->m}; |
611 | + auto result = state->wc.wait_for(ul, std::chrono::seconds{1}, [state]() { return state->signaled; }); |
612 | + EXPECT_TRUE(result); |
613 | +} |
LGTM!