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
initialize_variable()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
call_once_thread()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, &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
BOOST_AUTO_TEST_CASE(test_call_once)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(&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;
increment_valueincrement_value77 explicit increment_value(int* value_):
78 value(value_)
79 {}
80
operator ()increment_value81 void operator()() const
82 {
83 boost::unique_lock<boost::mutex> lock(m);
84 ++(*value);
85 }
86 };
87
call_once_with_functor()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(functor_flag, 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
BOOST_AUTO_TEST_CASE(test_call_once_arbitrary_functor)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(&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
operator ()throw_before_third_pass139 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
call_once_with_exception()153 void call_once_with_exception()
154 {
155 static boost::once_flag functor_flag=BOOST_ONCE_INIT;
156 try
157 {
158 boost::call_once(functor_flag, 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
BOOST_AUTO_TEST_CASE(test_call_once_retried_on_exception)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(&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