2 Copyright (c) 2005-2019 Intel Corporation
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "tbb/tbb_exception.h"
19 #include "tbb/atomic.h"
20 #if USE_TASK_SCHEDULER_OBSERVER
21 #include "tbb/task_scheduler_observer.h"
24 #include "harness_concurrency_tracker.h"
27 Harness::tid_t g_Master = 0;
28 const char * g_Orig_Wakeup_Msg = "Missed wakeup or machine is overloaded?";
29 const char * g_Wakeup_Msg = g_Orig_Wakeup_Msg;
31 tbb::atomic<intptr_t> g_CurExecuted,
32 g_ExecutedAtLastCatch,
33 g_ExecutedAtFirstCatch,
35 g_MasterExecutedThrow, // number of times master entered exception code
36 g_NonMasterExecutedThrow, // number of times nonmaster entered exception code
38 volatile bool g_ExceptionCaught = false,
39 g_UnknownException = false;
41 #if USE_TASK_SCHEDULER_OBSERVER
42 tbb::atomic<intptr_t> g_ActualMaxThreads;
43 tbb::atomic<intptr_t> g_ActualCurrentThreads;
46 volatile bool g_ThrowException = true,
47 // g_Flog is true for nested construct tests with catches (exceptions are not allowed to
48 // propagate to the tbb construct itself.)
50 g_MasterExecuted = false,
51 g_NonMasterExecuted = false;
53 bool g_ExceptionInMaster = false;
54 bool g_SolitaryException = false;
55 bool g_NestedPipelines = false;
57 //! Number of exceptions propagated into the user code (i.e. intercepted by the tests)
58 tbb::atomic<intptr_t> g_NumExceptionsCaught;
60 //-----------------------------------------------------------
62 #if USE_TASK_SCHEDULER_OBSERVER
63 class eh_test_observer : public tbb::task_scheduler_observer {
65 void on_scheduler_entry(bool is_worker) __TBB_override {
66 if(is_worker) { // we've already counted the master
67 size_t p = ++g_ActualCurrentThreads;
68 size_t q = g_ActualMaxThreads;
70 q = g_ActualMaxThreads.compare_and_swap(p,q);
74 // size_t q = g_ActualMaxThreads;
77 void on_scheduler_exit(bool is_worker) __TBB_override {
79 --g_ActualCurrentThreads;
84 //-----------------------------------------------------------
86 inline void ResetEhGlobals ( bool throwException = true, bool flog = false ) {
87 Harness::ConcurrencyTracker::Reset();
88 g_CurExecuted = g_ExecutedAtLastCatch = g_ExecutedAtFirstCatch = 0;
89 g_ExceptionCaught = false;
90 g_UnknownException = false;
91 g_NestedPipelines = false;
92 g_ThrowException = throwException;
93 g_MasterExecutedThrow = 0;
94 g_NonMasterExecutedThrow = 0;
96 g_MasterExecuted = false;
97 g_NonMasterExecuted = false;
98 #if USE_TASK_SCHEDULER_OBSERVER
99 g_ActualMaxThreads = 1; // count master
100 g_ActualCurrentThreads = 1; // count master
102 g_ExceptionsThrown = g_NumExceptionsCaught = g_PipelinesStarted = 0;
105 #if TBB_USE_EXCEPTIONS
106 class test_exception : public std::exception {
107 const char* my_description;
109 test_exception ( const char* description ) : my_description(description) {}
111 const char* what() const throw() __TBB_override { return my_description; }
114 class solitary_test_exception : public test_exception {
116 solitary_test_exception ( const char* description ) : test_exception(description) {}
119 #if TBB_USE_CAPTURED_EXCEPTION
120 typedef tbb::captured_exception PropagatedException;
121 #define EXCEPTION_NAME(e) e.name()
123 typedef test_exception PropagatedException;
124 #define EXCEPTION_NAME(e) typeid(e).name()
127 #define EXCEPTION_DESCR "Test exception"
129 #if HARNESS_EH_SIMPLE_MODE
131 static void ThrowTestException () {
132 ++g_ExceptionsThrown;
133 throw test_exception(EXCEPTION_DESCR);
136 #else /* !HARNESS_EH_SIMPLE_MODE */
138 static void ThrowTestException ( intptr_t threshold ) {
139 bool inMaster = (Harness::CurrentTid() == g_Master);
140 if ( !g_ThrowException || // if we're not supposed to throw
141 (!g_Flog && // if we're not catching throw in bodies and
142 (g_ExceptionInMaster ^ inMaster)) ) { // we're the master and not expected to throw
143 // or are the master and the master is not the one to throw (??)
146 while ( Existed() < threshold )
148 if ( !g_SolitaryException ) {
149 ++g_ExceptionsThrown;
150 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
151 throw test_exception(EXCEPTION_DESCR);
153 // g_SolitaryException == true
154 if(g_NestedPipelines) {
155 // only throw exception if we have started at least two inner pipelines
157 if(g_PipelinesStarted >= 3) {
158 if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 ) {
159 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
160 throw solitary_test_exception(EXCEPTION_DESCR);
165 if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 ) {
166 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
167 throw solitary_test_exception(EXCEPTION_DESCR);
171 #endif /* !HARNESS_EH_SIMPLE_MODE */
173 #define UPDATE_COUNTS() \
176 if(g_Master == Harness::CurrentTid()) g_MasterExecuted = true; \
177 else g_NonMasterExecuted = true; \
178 if( tbb::task::self().is_cancelled() ) ++g_TGCCancelled; \
182 } catch ( PropagatedException& e ) { \
183 g_ExecutedAtFirstCatch.compare_and_swap(g_CurExecuted,0); \
184 g_ExecutedAtLastCatch = g_CurExecuted; \
185 ASSERT( e.what(), "Empty what() string" ); \
186 ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(EXCEPTION_NAME(e), (g_SolitaryException ? typeid(solitary_test_exception) : typeid(test_exception)).name() ) == 0, "Unexpected original exception name"); \
187 ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR) == 0, "Unexpected original exception info"); \
188 g_ExceptionCaught = l_ExceptionCaughtAtCurrentLevel = true; \
189 ++g_NumExceptionsCaught; \
190 } catch ( tbb::tbb_exception& e ) { \
191 REPORT("Unexpected %s\n", e.name()); \
192 ASSERT (g_UnknownException && !g_UnknownException, "Unexpected tbb::tbb_exception" ); \
193 } catch ( std::exception& e ) { \
194 REPORT("Unexpected %s\n", typeid(e).name()); \
195 ASSERT (g_UnknownException && !g_UnknownException, "Unexpected std::exception" ); \
197 g_ExceptionCaught = l_ExceptionCaughtAtCurrentLevel = true; \
198 g_UnknownException = unknownException = true; \
200 if ( !g_SolitaryException ) \
201 REMARK_ONCE ("Multiple exceptions mode: %d throws", (intptr_t)g_ExceptionsThrown);
203 #define ASSERT_EXCEPTION() \
205 ASSERT (!g_ExceptionsThrown || g_ExceptionCaught, "throw without catch"); \
206 ASSERT (!g_ExceptionCaught || g_ExceptionsThrown, "catch without throw"); \
207 ASSERT (g_ExceptionCaught || (g_ExceptionInMaster && !g_MasterExecutedThrow) || (!g_ExceptionInMaster && !g_NonMasterExecutedThrow), "no exception occurred"); \
208 ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || !g_UnknownException, "unknown exception was caught"); \
211 #define CATCH_AND_ASSERT() \
215 #else /* !TBB_USE_EXCEPTIONS */
217 inline void ThrowTestException ( intptr_t ) {}
219 #endif /* !TBB_USE_EXCEPTIONS */
222 bool l_ExceptionCaughtAtCurrentLevel = false, unknownException = false; \
225 // "l_ExceptionCaughtAtCurrentLevel || unknownException" is used only to "touch" otherwise unused local variables
226 #define CATCH_AND_FAIL() } __TBB_CATCH(...) { \
227 ASSERT (false, "Cancelling tasks must not cause any exceptions"); \
228 (void)(l_ExceptionCaughtAtCurrentLevel && unknownException); \
231 const int c_Timeout = 1000000;
233 void WaitUntilConcurrencyPeaks ( int expected_peak ) {
238 while ( ++n < c_Timeout && (int)Harness::ConcurrencyTracker::PeakParallelism() < expected_peak )
240 #if USE_TASK_SCHEDULER_OBSERVER
241 ASSERT_WARNING( g_NumThreads == g_ActualMaxThreads, "Library did not provide sufficient threads");
243 ASSERT_WARNING(n < c_Timeout,g_Wakeup_Msg);
244 // Workaround in case a missed wakeup takes place
245 if ( n == c_Timeout ) {
246 tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task();
253 inline void WaitUntilConcurrencyPeaks () { WaitUntilConcurrencyPeaks(g_NumThreads); }
255 inline bool IsMaster() {
256 return Harness::CurrentTid() == g_Master;
259 inline bool IsThrowingThread() {
260 return g_ExceptionInMaster ^ IsMaster() ? true : false;
263 class CancellatorTask : public tbb::task {
264 static volatile bool s_Ready;
265 tbb::task_group_context &m_groupToCancel;
266 intptr_t m_cancellationThreshold;
268 tbb::task* execute () __TBB_override {
269 Harness::ConcurrencyTracker ct;
271 while ( g_CurExecuted < m_cancellationThreshold )
273 m_groupToCancel.cancel_group_execution();
274 g_ExecutedAtLastCatch = g_CurExecuted;
278 CancellatorTask ( tbb::task_group_context& ctx, intptr_t threshold )
279 : m_groupToCancel(ctx), m_cancellationThreshold(threshold)
284 static void Reset () { s_Ready = false; }
286 static bool WaitUntilReady () {
287 const intptr_t limit = 10000000;
291 } while( !s_Ready && ++n < limit );
292 // should yield once, then continue if Cancellator is ready.
293 ASSERT( s_Ready || n == limit, NULL );
298 volatile bool CancellatorTask::s_Ready = false;
300 template<class LauncherTaskT, class CancellatorTaskT>
301 void RunCancellationTest ( intptr_t threshold = 1 )
303 tbb::task_group_context ctx;
304 tbb::empty_task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
306 r.spawn( *new( r.allocate_child() ) CancellatorTaskT(ctx, threshold) );
308 r.spawn( *new( r.allocate_child() ) LauncherTaskT(ctx) );