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