1 /*
2     Copyright (c) 2005-2020 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 
CheckSignatures()26 bool 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 
39 static const int THRDS = 3;
40 static const int THRDS_DETACH = 2;
41 static tbb::atomic<int> sum;
42 static tbb::atomic<int> BaseCount;
43 static THREAD::id real_ids[THRDS+THRDS_DETACH];
44 
45 class Base {
46     mutable int copy_throws;
47     friend void RunTests();
48     friend void CheckExceptionSafety();
49     void operator=( const Base& );   // Deny access
50 protected:
Base()51     Base() : copy_throws(100) {++BaseCount;}
Base(const Base & c)52     Base( const Base& c ) : copy_throws(c.copy_throws) {
53         if( --copy_throws<=0 )
54             __TBB_THROW(0);
55         ++BaseCount;
56     }
~Base()57     ~Base() {--BaseCount;}
58 };
59 
60 template<int N>
61 class Data: Base {
62     Data();                          // Deny access
Data(int v)63     explicit Data(int v) : value(v) {}
64 
65     friend void RunTests();
66     friend void CheckExceptionSafety();
67 public:
68     int value;
69 };
70 
71 #include "harness_barrier.h"
72 
73 class ThreadFunc: Base {
ThreadFunc()74     ThreadFunc() {}
75 
76     static Harness::SpinBarrier init_barrier;
77 
78     friend void RunTests();
79 public:
operator()80     void operator()(){
81         real_ids[0] = THIS_THREAD::get_id();
82         init_barrier.wait();
83 
84         sum.fetch_and_add(1);
85     }
operator()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     }
operator()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     }
operator()117     void operator()(Data<0> d) {
118         THIS_THREAD_SLEEP ( tbb::tick_count::interval_t(d.value*1.) );
119     }
120 };
121 
122 Harness::SpinBarrier ThreadFunc::init_barrier(THRDS);
123 
CheckRelations(const THREAD::id ids[],int n,bool duplicates_allowed)124 void 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 
142 class AnotherThreadFunc: Base {
143 public:
operator()144     void operator()() {}
operator()145     void operator()(const Data<1>&) {}
operator()146     void operator()(const Data<1>&, const Data<2>&) {}
147     friend void CheckExceptionSafety();
148 };
149 
150 #if TBB_USE_EXCEPTIONS
CheckExceptionSafety()151 void 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
returnThread()187 THREAD returnThread() {
188     return THREAD();
189 }
190 #endif
191 
RunTests()192 void 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