]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | // Copyright Oliver Kowalke 2013. | |
3 | // Distributed under the Boost Software License, Version 1.0. | |
4 | // (See accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // This test is based on the tests of Boost.Thread | |
8 | ||
9 | #include <chrono> | |
10 | #include <mutex> | |
11 | #include <sstream> | |
12 | #include <string> | |
13 | ||
14 | #include <boost/assert.hpp> | |
15 | #include <boost/test/unit_test.hpp> | |
16 | ||
17 | #include <boost/fiber/all.hpp> | |
18 | ||
19 | int value1 = 0; | |
20 | std::string value2 = ""; | |
21 | ||
22 | struct X { | |
23 | int value; | |
24 | ||
25 | void foo( int i) { | |
26 | value = i; | |
27 | } | |
28 | }; | |
29 | ||
30 | class copyable { | |
31 | public: | |
32 | bool state; | |
33 | int value; | |
34 | ||
35 | copyable() : | |
36 | state( false), | |
37 | value( -1) { | |
38 | } | |
39 | ||
40 | copyable( int v) : | |
41 | state( true), | |
42 | value( v) { | |
43 | } | |
44 | ||
45 | void operator()() { | |
46 | value1 = value; | |
47 | } | |
48 | }; | |
49 | ||
50 | class moveable { | |
51 | public: | |
52 | bool state; | |
53 | int value; | |
54 | ||
55 | moveable() : | |
56 | state( false), | |
57 | value( -1) { | |
58 | } | |
59 | ||
60 | moveable( int v) : | |
61 | state( true), | |
62 | value( v) { | |
63 | } | |
64 | ||
65 | moveable( moveable && other) : | |
66 | state( other.state), | |
67 | value( other.value) { | |
68 | other.state = false; | |
69 | other.value = -1; | |
70 | } | |
71 | ||
72 | moveable & operator=( moveable && other) { | |
73 | if ( this == & other) return * this; | |
74 | state = other.state; | |
75 | value = other.value; | |
76 | other.state = false; | |
77 | other.value = -1; | |
78 | return * this; | |
79 | } | |
80 | ||
81 | moveable( moveable const& other) = delete; | |
82 | moveable & operator=( moveable const& other) = delete; | |
83 | ||
84 | void operator()() { | |
85 | value1 = value; | |
86 | } | |
87 | }; | |
88 | ||
89 | class detachable { | |
90 | private: | |
91 | int alive_count_; | |
92 | ||
93 | public: | |
94 | static int alive_count; | |
95 | static bool was_running; | |
96 | ||
97 | detachable() : | |
98 | alive_count_( 1) { | |
99 | ++alive_count; | |
100 | } | |
101 | ||
102 | detachable( detachable const& g) : | |
103 | alive_count_( g.alive_count_) { | |
104 | ++alive_count; | |
105 | } | |
106 | ||
107 | ~detachable() { | |
108 | alive_count_ = 0; | |
109 | --alive_count; | |
110 | } | |
111 | ||
112 | void operator()() { | |
113 | BOOST_CHECK_EQUAL(1, alive_count_); | |
114 | was_running = true; | |
115 | } | |
116 | }; | |
117 | ||
118 | int detachable::alive_count = 0; | |
119 | bool detachable::was_running = false; | |
120 | ||
121 | void fn1() { | |
122 | value1 = 1; | |
123 | } | |
124 | ||
125 | void fn2( int i, std::string const& s) { | |
126 | value1 = i; | |
127 | value2 = s; | |
128 | } | |
129 | ||
130 | void fn3( int & i) { | |
131 | i = 1; | |
132 | boost::this_fiber::yield(); | |
133 | i = 1; | |
134 | boost::this_fiber::yield(); | |
135 | i = 2; | |
136 | boost::this_fiber::yield(); | |
137 | i = 3; | |
138 | boost::this_fiber::yield(); | |
139 | i = 5; | |
140 | boost::this_fiber::yield(); | |
141 | i = 8; | |
142 | } | |
143 | ||
144 | void fn4() { | |
145 | boost::this_fiber::yield(); | |
146 | } | |
147 | ||
148 | void fn5() { | |
149 | boost::fibers::fiber f( boost::fibers::launch::post, fn4); | |
150 | BOOST_CHECK( f.joinable() ); | |
151 | f.join(); | |
152 | BOOST_CHECK( ! f.joinable() ); | |
153 | } | |
154 | ||
155 | void test_scheduler_dtor() { | |
156 | boost::fibers::context * ctx( | |
157 | boost::fibers::context::active() ); | |
158 | (void)ctx; | |
159 | } | |
160 | ||
161 | void test_join_fn() { | |
162 | { | |
163 | value1 = 0; | |
164 | boost::fibers::fiber f( boost::fibers::launch::post, fn1); | |
165 | f.join(); | |
166 | BOOST_CHECK_EQUAL( value1, 1); | |
167 | } | |
168 | { | |
169 | value1 = 0; | |
170 | value2 = ""; | |
171 | boost::fibers::fiber f( boost::fibers::launch::post, fn2, 3, "abc"); | |
172 | f.join(); | |
173 | BOOST_CHECK_EQUAL( value1, 3); | |
174 | BOOST_CHECK_EQUAL( value2, "abc"); | |
175 | } | |
176 | } | |
177 | ||
178 | void test_join_memfn() { | |
179 | X x = {0}; | |
180 | BOOST_CHECK_EQUAL( x.value, 0); | |
181 | boost::fibers::fiber( boost::fibers::launch::post, & X::foo, & x, 3).join(); | |
182 | BOOST_CHECK_EQUAL( x.value, 3); | |
183 | } | |
184 | ||
185 | void test_join_copyable() { | |
186 | value1 = 0; | |
187 | copyable cp( 3); | |
188 | BOOST_CHECK( cp.state); | |
189 | BOOST_CHECK_EQUAL( value1, 0); | |
190 | boost::fibers::fiber f( boost::fibers::launch::post, cp); | |
191 | f.join(); | |
192 | BOOST_CHECK( cp.state); | |
193 | BOOST_CHECK_EQUAL( value1, 3); | |
194 | } | |
195 | ||
196 | void test_join_moveable() { | |
197 | value1 = 0; | |
198 | moveable mv( 7); | |
199 | BOOST_CHECK( mv.state); | |
200 | BOOST_CHECK_EQUAL( value1, 0); | |
201 | boost::fibers::fiber f( boost::fibers::launch::post, std::move( mv) ); | |
202 | f.join(); | |
203 | BOOST_CHECK( ! mv.state); | |
204 | BOOST_CHECK_EQUAL( value1, 7); | |
205 | } | |
206 | ||
207 | void test_join_lambda() { | |
208 | { | |
209 | value1 = 0; | |
210 | value2 = ""; | |
211 | int i = 3; | |
212 | std::string abc("abc"); | |
213 | boost::fibers::fiber f( | |
214 | boost::fibers::launch::post, [i,abc]() { | |
215 | value1 = i; | |
216 | value2 = abc; | |
217 | }); | |
218 | f.join(); | |
219 | BOOST_CHECK_EQUAL( value1, 3); | |
220 | BOOST_CHECK_EQUAL( value2, "abc"); | |
221 | } | |
222 | { | |
223 | value1 = 0; | |
224 | value2 = ""; | |
225 | int i = 3; | |
226 | std::string abc("abc"); | |
227 | boost::fibers::fiber f( | |
228 | boost::fibers::launch::post, [](int i, std::string const& abc) { | |
229 | value1 = i; | |
230 | value2 = abc; | |
231 | }, | |
232 | i, abc); | |
233 | f.join(); | |
234 | BOOST_CHECK_EQUAL( value1, 3); | |
235 | BOOST_CHECK_EQUAL( value2, "abc"); | |
236 | } | |
237 | } | |
238 | ||
239 | void test_join_bind() { | |
240 | { | |
241 | value1 = 0; | |
242 | value2 = ""; | |
243 | int i = 3; | |
244 | std::string abc("abc"); | |
245 | boost::fibers::fiber f( | |
246 | boost::fibers::launch::post, std::bind( | |
247 | [i,abc]() { | |
248 | value1 = i; | |
249 | value2 = abc; | |
250 | } | |
251 | )); | |
252 | f.join(); | |
253 | BOOST_CHECK_EQUAL( value1, 3); | |
254 | BOOST_CHECK_EQUAL( value2, "abc"); | |
255 | } | |
256 | { | |
257 | value1 = 0; | |
258 | value2 = ""; | |
259 | std::string abc("abc"); | |
260 | boost::fibers::fiber f( | |
261 | boost::fibers::launch::post, std::bind( | |
262 | [](std::string & str) { | |
263 | value1 = 3; | |
264 | value2 = str; | |
265 | }, | |
266 | abc | |
267 | )); | |
268 | f.join(); | |
269 | BOOST_CHECK_EQUAL( value1, 3); | |
270 | BOOST_CHECK_EQUAL( value2, "abc"); | |
271 | } | |
272 | { | |
273 | value1 = 0; | |
274 | value2 = ""; | |
275 | std::string abc("abc"); | |
276 | boost::fibers::fiber f( | |
277 | boost::fibers::launch::post, std::bind( | |
278 | []( std::string & str) { | |
279 | value1 = 3; | |
280 | value2 = str; | |
281 | }, | |
282 | std::placeholders::_1 | |
283 | ), | |
284 | std::ref( abc) ); | |
285 | f.join(); | |
286 | BOOST_CHECK_EQUAL( value1, 3); | |
287 | BOOST_CHECK_EQUAL( value2, "abc"); | |
288 | } | |
289 | } | |
290 | ||
291 | void test_join_in_fiber() { | |
292 | // spawn fiber f | |
293 | // f spawns an new fiber f' in its fiber-fn | |
294 | // f' yields in its fiber-fn | |
295 | // f joins s' and gets suspended (waiting on s') | |
296 | boost::fibers::fiber f( boost::fibers::launch::post, fn5); | |
297 | BOOST_CHECK( f.joinable() ); | |
298 | // join() resumes f + f' which completes | |
299 | f.join(); | |
300 | BOOST_CHECK( ! f.joinable() ); | |
301 | } | |
302 | ||
303 | void test_move_fiber() { | |
304 | boost::fibers::fiber f1; | |
305 | BOOST_CHECK( ! f1.joinable() ); | |
306 | boost::fibers::fiber f2( boost::fibers::launch::post, fn1); | |
307 | BOOST_CHECK( f2.joinable() ); | |
308 | f1 = std::move( f2); | |
309 | BOOST_CHECK( f1.joinable() ); | |
310 | BOOST_CHECK( ! f2.joinable() ); | |
311 | f1.join(); | |
312 | BOOST_CHECK( ! f1.joinable() ); | |
313 | BOOST_CHECK( ! f2.joinable() ); | |
314 | } | |
315 | ||
316 | void test_id() { | |
317 | boost::fibers::fiber f1; | |
318 | boost::fibers::fiber f2( boost::fibers::launch::post, fn1); | |
319 | BOOST_CHECK( ! f1.joinable() ); | |
320 | BOOST_CHECK( f2.joinable() ); | |
321 | ||
322 | BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f1.get_id() ); | |
323 | BOOST_CHECK( boost::fibers::fiber::id() != f2.get_id() ); | |
324 | ||
325 | boost::fibers::fiber f3( boost::fibers::launch::post, fn1); | |
326 | BOOST_CHECK( f2.get_id() != f3.get_id() ); | |
327 | ||
328 | f1 = std::move( f2); | |
329 | BOOST_CHECK( f1.joinable() ); | |
330 | BOOST_CHECK( ! f2.joinable() ); | |
331 | ||
332 | BOOST_CHECK( boost::fibers::fiber::id() != f1.get_id() ); | |
333 | BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f2.get_id() ); | |
334 | ||
335 | BOOST_CHECK( ! f2.joinable() ); | |
336 | ||
337 | f1.join(); | |
338 | f3.join(); | |
339 | } | |
340 | ||
341 | void test_yield() { | |
342 | int v1 = 0, v2 = 0; | |
343 | BOOST_CHECK_EQUAL( 0, v1); | |
344 | BOOST_CHECK_EQUAL( 0, v2); | |
345 | boost::fibers::fiber f1( boost::fibers::launch::post, fn3, std::ref( v1) ); | |
346 | boost::fibers::fiber f2( boost::fibers::launch::post, fn3, std::ref( v2) ); | |
347 | f1.join(); | |
348 | f2.join(); | |
349 | BOOST_CHECK( ! f1.joinable() ); | |
350 | BOOST_CHECK( ! f2.joinable() ); | |
351 | BOOST_CHECK_EQUAL( 8, v1); | |
352 | BOOST_CHECK_EQUAL( 8, v2); | |
353 | } | |
354 | ||
355 | void test_sleep_for() { | |
356 | typedef std::chrono::system_clock Clock; | |
357 | typedef Clock::time_point time_point; | |
358 | std::chrono::milliseconds ms(500); | |
359 | time_point t0 = Clock::now(); | |
360 | boost::this_fiber::sleep_for(ms); | |
361 | time_point t1 = Clock::now(); | |
362 | std::chrono::nanoseconds ns = (t1 - t0) - ms; | |
363 | std::chrono::nanoseconds err = ms / 10; | |
364 | // This test is spurious as it depends on the time the fiber system switches the fiber | |
365 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); | |
366 | } | |
367 | ||
368 | void test_sleep_until() { | |
369 | { | |
370 | typedef std::chrono::steady_clock Clock; | |
371 | typedef Clock::time_point time_point; | |
372 | std::chrono::milliseconds ms(500); | |
373 | time_point t0 = Clock::now(); | |
374 | boost::this_fiber::sleep_until(t0 + ms); | |
375 | time_point t1 = Clock::now(); | |
376 | std::chrono::nanoseconds ns = (t1 - t0) - ms; | |
377 | std::chrono::nanoseconds err = ms / 10; | |
378 | // This test is spurious as it depends on the time the thread system switches the threads | |
379 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); | |
380 | } | |
381 | { | |
382 | typedef std::chrono::system_clock Clock; | |
383 | typedef Clock::time_point time_point; | |
384 | std::chrono::milliseconds ms(500); | |
385 | time_point t0 = Clock::now(); | |
386 | boost::this_fiber::sleep_until(t0 + ms); | |
387 | time_point t1 = Clock::now(); | |
388 | std::chrono::nanoseconds ns = (t1 - t0) - ms; | |
389 | std::chrono::nanoseconds err = ms / 10; | |
390 | // This test is spurious as it depends on the time the thread system switches the threads | |
391 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); | |
392 | } | |
393 | } | |
394 | ||
395 | void do_wait( boost::fibers::barrier* b) { | |
396 | b->wait(); | |
397 | } | |
398 | ||
399 | void test_detach() { | |
400 | { | |
401 | boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); | |
402 | BOOST_CHECK( f.joinable() ); | |
403 | f.detach(); | |
404 | BOOST_CHECK( ! f.joinable() ); | |
405 | boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); | |
406 | BOOST_CHECK( detachable::was_running); | |
407 | BOOST_CHECK_EQUAL( 0, detachable::alive_count); | |
408 | } | |
409 | { | |
410 | boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); | |
411 | BOOST_CHECK( f.joinable() ); | |
412 | boost::this_fiber::yield(); | |
413 | f.detach(); | |
414 | BOOST_CHECK( ! f.joinable() ); | |
415 | boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); | |
416 | BOOST_CHECK( detachable::was_running); | |
417 | BOOST_CHECK_EQUAL( 0, detachable::alive_count); | |
418 | } | |
419 | } | |
420 | ||
421 | boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { | |
422 | boost::unit_test::test_suite * test = | |
423 | BOOST_TEST_SUITE("Boost.Fiber: fiber test suite"); | |
424 | ||
425 | test->add( BOOST_TEST_CASE( & test_scheduler_dtor) ); | |
426 | test->add( BOOST_TEST_CASE( & test_join_fn) ); | |
427 | test->add( BOOST_TEST_CASE( & test_join_memfn) ); | |
428 | test->add( BOOST_TEST_CASE( & test_join_copyable) ); | |
429 | test->add( BOOST_TEST_CASE( & test_join_moveable) ); | |
430 | test->add( BOOST_TEST_CASE( & test_join_lambda) ); | |
431 | test->add( BOOST_TEST_CASE( & test_join_bind) ); | |
432 | test->add( BOOST_TEST_CASE( & test_join_in_fiber) ); | |
433 | test->add( BOOST_TEST_CASE( & test_move_fiber) ); | |
434 | test->add( BOOST_TEST_CASE( & test_move_fiber) ); | |
435 | test->add( BOOST_TEST_CASE( & test_yield) ); | |
436 | test->add( BOOST_TEST_CASE( & test_sleep_for) ); | |
437 | test->add( BOOST_TEST_CASE( & test_sleep_until) ); | |
438 | test->add( BOOST_TEST_CASE( & test_detach) ); | |
439 | ||
440 | return test; | |
441 | } |