| 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 | |
| 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: |
| 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 | |
| 60 | template<int N> |
| 61 | class Data: Base { |
| 62 | Data(); // Deny access |
| 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 { |
| 74 | ThreadFunc() {} |
| 75 | |
| 76 | static Harness::SpinBarrier init_barrier; |
| 77 | |
| 78 | friend void RunTests(); |
| 79 | public: |
| 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 | |
| 122 | Harness::SpinBarrier ThreadFunc::init_barrier(THRDS); |
| 123 | |
| 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: |
| 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 |
| 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 |
| 187 | THREAD returnThread() { |
| 188 | return THREAD(); |
| 189 | } |
| 190 | #endif |
| 191 | |
| 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 | |