1 | // (C) Copyright 2006-7 Anthony Williams |
2 | // Distributed under the Boost Software License, Version 1.0. (See |
3 | // accompanying file LICENSE_1_0.txt or copy at |
4 | // http://www.boost.org/LICENSE_1_0.txt) |
5 | |
6 | #define BOOST_THREAD_VERSION 2 |
7 | #define BOOST_THREAD_PROVIDES_INTERRUPTIONS |
8 | #define BOOST_TEST_MODULE Boost.Threads: once test suite |
9 | |
10 | #include <boost/test/unit_test.hpp> |
11 | #include <boost/thread/thread.hpp> |
12 | #include <boost/thread/mutex.hpp> |
13 | #include <boost/thread/once.hpp> |
14 | #include <iostream> |
15 | |
16 | #include <boost/thread/detail/log.hpp> |
17 | |
18 | boost::once_flag flag=BOOST_ONCE_INIT; |
19 | int var_to_init=0; |
20 | boost::mutex m; |
21 | |
22 | void initialize_variable() |
23 | { |
24 | // ensure that if multiple threads get in here, they are serialized, so we can see the effect |
25 | boost::unique_lock<boost::mutex> lock(m); |
26 | ++var_to_init; |
27 | } |
28 | |
29 | |
30 | void call_once_thread() |
31 | { |
32 | unsigned const loop_count=100; |
33 | int my_once_value=0; |
34 | for(unsigned i=0;i<loop_count;++i) |
35 | { |
36 | boost::call_once(flag, f: &initialize_variable); |
37 | my_once_value=var_to_init; |
38 | if(my_once_value!=1) |
39 | { |
40 | break; |
41 | } |
42 | } |
43 | boost::unique_lock<boost::mutex> lock(m); |
44 | BOOST_CHECK_EQUAL(my_once_value, 1); |
45 | } |
46 | |
47 | BOOST_AUTO_TEST_CASE(test_call_once) |
48 | { |
49 | BOOST_DETAIL_THREAD_LOG; |
50 | |
51 | unsigned const num_threads=20; |
52 | boost::thread_group group; |
53 | |
54 | try |
55 | { |
56 | for(unsigned i=0;i<num_threads;++i) |
57 | { |
58 | group.create_thread(threadfunc: &call_once_thread); |
59 | } |
60 | group.join_all(); |
61 | } |
62 | catch(...) |
63 | { |
64 | group.interrupt_all(); |
65 | group.join_all(); |
66 | throw; |
67 | } |
68 | |
69 | BOOST_CHECK_EQUAL(var_to_init,1); |
70 | } |
71 | |
72 | int var_to_init_with_functor=0; |
73 | |
74 | struct increment_value |
75 | { |
76 | int* value; |
77 | explicit increment_value(int* value_): |
78 | value(value_) |
79 | {} |
80 | |
81 | void operator()() const |
82 | { |
83 | boost::unique_lock<boost::mutex> lock(m); |
84 | ++(*value); |
85 | } |
86 | }; |
87 | |
88 | void call_once_with_functor() |
89 | { |
90 | unsigned const loop_count=100; |
91 | int my_once_value=0; |
92 | static boost::once_flag functor_flag=BOOST_ONCE_INIT; |
93 | for(unsigned i=0;i<loop_count;++i) |
94 | { |
95 | boost::call_once(flag&: functor_flag, f: increment_value(&var_to_init_with_functor)); |
96 | my_once_value=var_to_init_with_functor; |
97 | if(my_once_value!=1) |
98 | { |
99 | break; |
100 | } |
101 | } |
102 | boost::unique_lock<boost::mutex> lock(m); |
103 | BOOST_CHECK_EQUAL(my_once_value, 1); |
104 | } |
105 | |
106 | BOOST_AUTO_TEST_CASE(test_call_once_arbitrary_functor) |
107 | { |
108 | BOOST_DETAIL_THREAD_LOG; |
109 | |
110 | unsigned const num_threads=20; |
111 | boost::thread_group group; |
112 | |
113 | try |
114 | { |
115 | for(unsigned i=0;i<num_threads;++i) |
116 | { |
117 | group.create_thread(threadfunc: &call_once_with_functor); |
118 | } |
119 | group.join_all(); |
120 | } |
121 | catch(...) |
122 | { |
123 | group.interrupt_all(); |
124 | group.join_all(); |
125 | throw; |
126 | } |
127 | |
128 | BOOST_CHECK_EQUAL(var_to_init_with_functor,1); |
129 | } |
130 | |
131 | |
132 | struct throw_before_third_pass |
133 | { |
134 | struct my_exception |
135 | {}; |
136 | |
137 | static unsigned pass_counter; |
138 | |
139 | void operator()() const |
140 | { |
141 | boost::unique_lock<boost::mutex> lock(m); |
142 | ++pass_counter; |
143 | if(pass_counter<3) |
144 | { |
145 | throw my_exception(); |
146 | } |
147 | } |
148 | }; |
149 | |
150 | unsigned throw_before_third_pass::pass_counter=0; |
151 | unsigned exception_counter=0; |
152 | |
153 | void call_once_with_exception() |
154 | { |
155 | static boost::once_flag functor_flag=BOOST_ONCE_INIT; |
156 | try |
157 | { |
158 | boost::call_once(flag&: functor_flag, f: throw_before_third_pass()); |
159 | } |
160 | catch(throw_before_third_pass::my_exception) |
161 | { |
162 | boost::unique_lock<boost::mutex> lock(m); |
163 | ++exception_counter; |
164 | } |
165 | } |
166 | |
167 | BOOST_AUTO_TEST_CASE(test_call_once_retried_on_exception) |
168 | { |
169 | BOOST_DETAIL_THREAD_LOG; |
170 | unsigned const num_threads=20; |
171 | boost::thread_group group; |
172 | |
173 | try |
174 | { |
175 | for(unsigned i=0;i<num_threads;++i) |
176 | { |
177 | group.create_thread(threadfunc: &call_once_with_exception); |
178 | } |
179 | group.join_all(); |
180 | } |
181 | catch(...) |
182 | { |
183 | group.interrupt_all(); |
184 | group.join_all(); |
185 | throw; |
186 | } |
187 | |
188 | BOOST_CHECK_EQUAL(throw_before_third_pass::pass_counter,3u); |
189 | BOOST_CHECK_EQUAL(exception_counter,2u); |
190 | } |
191 | |
192 | |
193 | |