1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17#include "tbb/atomic.h"
18#if __TBB_CPP11_RVALUE_REF_PRESENT
19#include <utility> // std::move
20#endif
21
22#define HARNESS_NO_PARSE_COMMAND_LINE 1
23#include "harness_report.h"
24#include "harness_assert.h"
25
26bool CheckSignatures() {
27 // Checks that thread ids can be compared, in the way users would do it
28 THREAD::id id1, id2;
29 bool result = id1 == id2;
30 result |= id1 != id2;
31 result |= id1 < id2;
32 result |= id1 > id2;
33 result |= id1 <= id2;
34 result |= id1 >= id2;
35 tbb::tbb_hash<THREAD::id> hash;
36 return result |= hash(id1)==hash(id2);
37}
38
39static const int THRDS = 3;
40static const int THRDS_DETACH = 2;
41static tbb::atomic<int> sum;
42static tbb::atomic<int> BaseCount;
43static THREAD::id real_ids[THRDS+THRDS_DETACH];
44
45class Base {
46 mutable int copy_throws;
47 friend void RunTests();
48 friend void CheckExceptionSafety();
49 void operator=( const Base& ); // Deny access
50protected:
51 Base() : copy_throws(100) {++BaseCount;}
52 Base( const Base& c ) : copy_throws(c.copy_throws) {
53 if( --copy_throws<=0 )
54 __TBB_THROW(0);
55 ++BaseCount;
56 }
57 ~Base() {--BaseCount;}
58};
59
60template<int N>
61class Data: Base {
62 Data(); // Deny access
63 explicit Data(int v) : value(v) {}
64
65 friend void RunTests();
66 friend void CheckExceptionSafety();
67public:
68 int value;
69};
70
71#include "harness_barrier.h"
72
73class ThreadFunc: Base {
74 ThreadFunc() {}
75
76 static Harness::SpinBarrier init_barrier;
77
78 friend void RunTests();
79public:
80 void operator()(){
81 real_ids[0] = THIS_THREAD::get_id();
82 init_barrier.wait();
83
84 sum.fetch_and_add(1);
85 }
86 void operator()(int num){
87 real_ids[num] = THIS_THREAD::get_id();
88 init_barrier.wait();
89
90 sum.fetch_and_add(num);
91 }
92 void operator()(int num, Data<0> dx) {
93 real_ids[num] = THIS_THREAD::get_id();
94
95 const double WAIT = .1;
96#if _WIN32 || _WIN64
97 const double LONG_TOLERANCE = 0.120; // maximal scheduling quantum for Windows Server
98#else
99 const double LONG_TOLERANCE = 0.200; // reasonable upper bound
100#endif
101 tbb::tick_count::interval_t test_interval(WAIT);
102 tbb::tick_count t0 = tbb::tick_count::now();
103 THIS_THREAD_SLEEP ( test_interval );
104 tbb::tick_count t1 = tbb::tick_count::now();
105 double delta = ((t1-t0)-test_interval).seconds();
106 if(delta < 0.0)
107 REPORT("ERROR: Sleep interval too short (%g < %g)\n",
108 (t1-t0).seconds(), test_interval.seconds() );
109 if(delta > LONG_TOLERANCE)
110 REPORT("Warning: Sleep interval too long (%g outside long tolerance(%g))\n",
111 (t1-t0).seconds(), test_interval.seconds() + LONG_TOLERANCE);
112 init_barrier.wait();
113
114 sum.fetch_and_add(num);
115 sum.fetch_and_add(dx.value);
116 }
117 void operator()(Data<0> d) {
118 THIS_THREAD_SLEEP ( tbb::tick_count::interval_t(d.value*1.) );
119 }
120};
121
122Harness::SpinBarrier ThreadFunc::init_barrier(THRDS);
123
124void CheckRelations( const THREAD::id ids[], int n, bool duplicates_allowed ) {
125 for( int i=0; i<n; ++i ) {
126 const THREAD::id x = ids[i];
127 for( int j=0; j<n; ++j ) {
128 const THREAD::id y = ids[j];
129 ASSERT( (x==y)==!(x!=y), NULL );
130 ASSERT( (x<y)==!(x>=y), NULL );
131 ASSERT( (x>y)==!(x<=y), NULL );
132 ASSERT( (x<y)+(x==y)+(x>y)==1, NULL );
133 ASSERT( x!=y || i==j || duplicates_allowed, NULL );
134 for( int k=0; k<n; ++k ) {
135 const THREAD::id z = ids[j];
136 ASSERT( !(x<y && y<z) || x<z, "< is not transitive" );
137 }
138 }
139 }
140}
141
142class AnotherThreadFunc: Base {
143public:
144 void operator()() {}
145 void operator()(const Data<1>&) {}
146 void operator()(const Data<1>&, const Data<2>&) {}
147 friend void CheckExceptionSafety();
148};
149
150#if TBB_USE_EXCEPTIONS
151void CheckExceptionSafety() {
152 int original_count = BaseCount;
153 // d loops over number of copies before throw occurs
154 for( int d=1; d<=3; ++d ) {
155 // Try all combinations of throw/nothrow for f, x, and y's copy constructor.
156 for( int i=0; i<8; ++i ) {
157 {
158 const AnotherThreadFunc f = AnotherThreadFunc();
159 if( i&1 ) f.copy_throws = d;
160 const Data<1> x(0);
161 if( i&2 ) x.copy_throws = d;
162 const Data<2> y(0);
163 if( i&4 ) y.copy_throws = d;
164 bool exception_caught = false;
165 for( int j=0; j<3; ++j ) {
166 try {
167 switch(j) {
168 case 0: {THREAD t(f); t.join();} break;
169 case 1: {THREAD t(f,x); t.join();} break;
170 case 2: {THREAD t(f,x,y); t.join();} break;
171 }
172 } catch(...) {
173 exception_caught = true;
174 }
175 ASSERT( !exception_caught||(i&((1<<(j+1))-1))!=0, NULL );
176 }
177 }
178 ASSERT( BaseCount==original_count, "object leak detected" );
179 }
180 }
181}
182#endif /* TBB_USE_EXCEPTIONS */
183
184#include <cstdio>
185
186#if __TBB_CPP11_RVALUE_REF_PRESENT
187THREAD returnThread() {
188 return THREAD();
189}
190#endif
191
192void RunTests() {
193
194 ThreadFunc t;
195 Data<0> d100(100), d1(1), d0(0);
196 const THREAD::id id_zero;
197 THREAD::id id0, uniq_ids[THRDS];
198
199 THREAD thrs[THRDS];
200 THREAD thr;
201 THREAD thr0(t);
202 THREAD thr1(t, 2);
203 THREAD thr2(t, 1, d100);
204
205 ASSERT( thr0.get_id() != id_zero, NULL );
206 id0 = thr0.get_id();
207 tbb::move(thrs[0], thr0);
208 ASSERT( thr0.get_id() == id_zero, NULL );
209 ASSERT( thrs[0].get_id() == id0, NULL );
210
211 THREAD::native_handle_type h1 = thr1.native_handle();
212 THREAD::native_handle_type h2 = thr2.native_handle();
213 THREAD::id id1 = thr1.get_id();
214 THREAD::id id2 = thr2.get_id();
215 tbb::swap(thr1, thr2);
216 ASSERT( thr1.native_handle() == h2, NULL );
217 ASSERT( thr2.native_handle() == h1, NULL );
218 ASSERT( thr1.get_id() == id2, NULL );
219 ASSERT( thr2.get_id() == id1, NULL );
220#if __TBB_CPP11_RVALUE_REF_PRESENT
221 {
222 THREAD tmp_thr(std::move(thr1));
223 ASSERT( tmp_thr.native_handle() == h2 && tmp_thr.get_id() == id2, NULL );
224 thr1 = std::move(tmp_thr);
225 ASSERT( thr1.native_handle() == h2 && thr1.get_id() == id2, NULL );
226 }
227#endif
228
229 thr1.swap(thr2);
230 ASSERT( thr1.native_handle() == h1, NULL );
231 ASSERT( thr2.native_handle() == h2, NULL );
232 ASSERT( thr1.get_id() == id1, NULL );
233 ASSERT( thr2.get_id() == id2, NULL );
234 thr1.swap(thr2);
235
236 tbb::move(thrs[1], thr1);
237 ASSERT( thr1.get_id() == id_zero, NULL );
238
239#if __TBB_CPP11_RVALUE_REF_PRESENT
240 thrs[2] = returnThread();
241 ASSERT( thrs[2].get_id() == id_zero, NULL );
242#endif
243 tbb::move(thrs[2], thr2);
244 ASSERT( thr2.get_id() == id_zero, NULL );
245
246 for (int i=0; i<THRDS; i++)
247 uniq_ids[i] = thrs[i].get_id();
248
249 ASSERT( thrs[2].joinable(), NULL );
250
251 for (int i=0; i<THRDS; i++)
252 thrs[i].join();
253
254#if !__TBB_WIN8UI_SUPPORT
255 // TODO: to find out the way to find thread_id without GetThreadId and other
256 // desktop functions.
257 // Now tbb_thread does have its own thread_id that stores std::thread object
258 // Test will fail in case it is run in desktop mode against New Windows*8 UI library
259 for (int i=0; i<THRDS; i++)
260 ASSERT( real_ids[i] == uniq_ids[i], NULL );
261#endif
262
263 int current_sum = sum;
264 ASSERT( current_sum == 104, NULL );
265 ASSERT( ! thrs[2].joinable(), NULL );
266 ASSERT( BaseCount==4, "object leak detected" );
267
268#if TBB_USE_EXCEPTIONS
269 CheckExceptionSafety();
270#endif
271
272 // Note: all tests involving BaseCount should be put before the tests
273 // involing detached threads, because there is no way of knowing when
274 // a detached thread destroys its arguments.
275
276 THREAD thr_detach_0(t, d0);
277 real_ids[THRDS] = thr_detach_0.get_id();
278 thr_detach_0.detach();
279 ASSERT( thr_detach_0.get_id() == id_zero, NULL );
280
281 THREAD thr_detach_1(t, d1);
282 real_ids[THRDS+1] = thr_detach_1.get_id();
283 thr_detach_1.detach();
284 ASSERT( thr_detach_1.get_id() == id_zero, NULL );
285
286 CheckRelations(real_ids, THRDS+THRDS_DETACH, true);
287
288 CheckRelations(uniq_ids, THRDS, false);
289
290 for (int i=0; i<2; i++) {
291 AnotherThreadFunc empty_func;
292 THREAD thr_to(empty_func), thr_from(empty_func);
293 THREAD::id from_id = thr_from.get_id();
294 if (i) thr_to.join();
295#if __TBB_CPP11_RVALUE_REF_PRESENT
296 thr_to = std::move(thr_from);
297#else
298 thr_to = thr_from;
299#endif
300 ASSERT( thr_from.get_id() == THREAD::id(), NULL );
301 ASSERT( thr_to.get_id() == from_id, NULL );
302 }
303
304 ASSERT( THREAD::hardware_concurrency() > 0, NULL);
305}
306