2 Copyright (c) 2005-2017 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.
22 #include "tbb/tbb_exception.h"
23 #include "tbb/atomic.h"
24 #if USE_TASK_SCHEDULER_OBSERVER
25 #include "tbb/task_scheduler_observer.h"
28 #include "harness_concurrency_tracker.h"
31 Harness::tid_t g_Master = 0;
32 const char * g_Orig_Wakeup_Msg = "Missed wakeup or machine is overloaded?";
33 const char * g_Wakeup_Msg = g_Orig_Wakeup_Msg;
35 tbb::atomic<intptr_t> g_CurExecuted,
36 g_ExecutedAtLastCatch,
37 g_ExecutedAtFirstCatch,
39 g_MasterExecutedThrow, // number of times master entered exception code
40 g_NonMasterExecutedThrow, // number of times nonmaster entered exception code
42 volatile bool g_ExceptionCaught = false,
43 g_UnknownException = false;
45 #if USE_TASK_SCHEDULER_OBSERVER
46 tbb::atomic<intptr_t> g_ActualMaxThreads;
47 tbb::atomic<intptr_t> g_ActualCurrentThreads;
50 volatile bool g_ThrowException = true,
51 // g_Flog is true for nested construct tests with catches (exceptions are not allowed to
52 // propagate to the tbb construct itself.)
54 g_MasterExecuted = false,
55 g_NonMasterExecuted = false;
57 bool g_ExceptionInMaster = false;
58 bool g_SolitaryException = false;
59 bool g_NestedPipelines = false;
61 //! Number of exceptions propagated into the user code (i.e. intercepted by the tests)
62 tbb::atomic<intptr_t> g_NumExceptionsCaught;
64 //-----------------------------------------------------------
66 #if USE_TASK_SCHEDULER_OBSERVER
67 class eh_test_observer : public tbb::task_scheduler_observer {
69 void on_scheduler_entry(bool is_worker) __TBB_override {
70 if(is_worker) { // we've already counted the master
71 size_t p = ++g_ActualCurrentThreads;
72 size_t q = g_ActualMaxThreads;
74 q = g_ActualMaxThreads.compare_and_swap(p,q);
78 // size_t q = g_ActualMaxThreads;
81 void on_scheduler_exit(bool is_worker) __TBB_override {
83 --g_ActualCurrentThreads;
88 //-----------------------------------------------------------
90 inline void ResetEhGlobals ( bool throwException = true, bool flog = false ) {
91 Harness::ConcurrencyTracker::Reset();
92 g_CurExecuted = g_ExecutedAtLastCatch = g_ExecutedAtFirstCatch = 0;
93 g_ExceptionCaught = false;
94 g_UnknownException = false;
95 g_NestedPipelines = false;
96 g_ThrowException = throwException;
97 g_MasterExecutedThrow = 0;
98 g_NonMasterExecutedThrow = 0;
100 g_MasterExecuted = false;
101 g_NonMasterExecuted = false;
102 #if USE_TASK_SCHEDULER_OBSERVER
103 g_ActualMaxThreads = 1; // count master
104 g_ActualCurrentThreads = 1; // count master
106 g_ExceptionsThrown = g_NumExceptionsCaught = g_PipelinesStarted = 0;
109 #if TBB_USE_EXCEPTIONS
110 class test_exception : public std::exception {
111 const char* my_description;
113 test_exception ( const char* description ) : my_description(description) {}
115 const char* what() const throw() __TBB_override { return my_description; }
118 class solitary_test_exception : public test_exception {
120 solitary_test_exception ( const char* description ) : test_exception(description) {}
123 #if TBB_USE_CAPTURED_EXCEPTION
124 typedef tbb::captured_exception PropagatedException;
125 #define EXCEPTION_NAME(e) e.name()
127 typedef test_exception PropagatedException;
128 #define EXCEPTION_NAME(e) typeid(e).name()
131 #define EXCEPTION_DESCR "Test exception"
133 #if HARNESS_EH_SIMPLE_MODE
135 static void ThrowTestException () {
136 ++g_ExceptionsThrown;
137 throw test_exception(EXCEPTION_DESCR);
140 #else /* !HARNESS_EH_SIMPLE_MODE */
142 static void ThrowTestException ( intptr_t threshold ) {
143 bool inMaster = (Harness::CurrentTid() == g_Master);
144 if ( !g_ThrowException || // if we're not supposed to throw
145 (!g_Flog && // if we're not catching throw in bodies and
146 (g_ExceptionInMaster ^ inMaster)) ) { // we're the master and not expected to throw
147 // or are the master and the master is not the one to throw (??)
150 while ( Existed() < threshold )
152 if ( !g_SolitaryException ) {
153 ++g_ExceptionsThrown;
154 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
155 throw test_exception(EXCEPTION_DESCR);
157 // g_SolitaryException == true
158 if(g_NestedPipelines) {
159 // only throw exception if we have started at least two inner pipelines
161 if(g_PipelinesStarted >= 3) {
162 if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 ) {
163 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
164 throw solitary_test_exception(EXCEPTION_DESCR);
169 if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 ) {
170 if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
171 throw solitary_test_exception(EXCEPTION_DESCR);
175 #endif /* !HARNESS_EH_SIMPLE_MODE */
177 #define UPDATE_COUNTS() \
180 if(g_Master == Harness::CurrentTid()) g_MasterExecuted = true; \
181 else g_NonMasterExecuted = true; \
182 if( tbb::task::self().is_cancelled() ) ++g_TGCCancelled; \
186 } catch ( PropagatedException& e ) { \
187 g_ExecutedAtFirstCatch.compare_and_swap(g_CurExecuted,0); \
188 g_ExecutedAtLastCatch = g_CurExecuted; \
189 ASSERT( e.what(), "Empty what() string" ); \
190 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"); \
191 ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR) == 0, "Unexpected original exception info"); \
192 g_ExceptionCaught = l_ExceptionCaughtAtCurrentLevel = true; \
193 ++g_NumExceptionsCaught; \
194 } catch ( tbb::tbb_exception& e ) { \
195 REPORT("Unexpected %s\n", e.name()); \
196 ASSERT (g_UnknownException && !g_UnknownException, "Unexpected tbb::tbb_exception" ); \
197 } catch ( std::exception& e ) { \
198 REPORT("Unexpected %s\n", typeid(e).name()); \
199 ASSERT (g_UnknownException && !g_UnknownException, "Unexpected std::exception" ); \
201 g_ExceptionCaught = l_ExceptionCaughtAtCurrentLevel = true; \
202 g_UnknownException = unknownException = true; \
204 if ( !g_SolitaryException ) \
205 REMARK_ONCE ("Multiple exceptions mode: %d throws", (intptr_t)g_ExceptionsThrown);
207 #define ASSERT_EXCEPTION() \
209 ASSERT (!g_ExceptionsThrown || g_ExceptionCaught, "throw without catch"); \
210 ASSERT (!g_ExceptionCaught || g_ExceptionsThrown, "catch without throw"); \
211 ASSERT (g_ExceptionCaught || (g_ExceptionInMaster && !g_MasterExecutedThrow) || (!g_ExceptionInMaster && !g_NonMasterExecutedThrow), "no exception occurred"); \
212 ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || !g_UnknownException, "unknown exception was caught"); \
215 #define CATCH_AND_ASSERT() \
219 #else /* !TBB_USE_EXCEPTIONS */
221 inline void ThrowTestException ( intptr_t ) {}
223 #endif /* !TBB_USE_EXCEPTIONS */
226 bool l_ExceptionCaughtAtCurrentLevel = false, unknownException = false; \
229 // "l_ExceptionCaughtAtCurrentLevel || unknownException" is used only to "touch" otherwise unused local variables
230 #define CATCH_AND_FAIL() } __TBB_CATCH(...) { \
231 ASSERT (false, "Cancelling tasks must not cause any exceptions"); \
232 (void)(l_ExceptionCaughtAtCurrentLevel && unknownException); \
235 const int c_Timeout = 1000000;
237 void WaitUntilConcurrencyPeaks ( int expected_peak ) {
242 while ( ++n < c_Timeout && (int)Harness::ConcurrencyTracker::PeakParallelism() < expected_peak )
244 #if USE_TASK_SCHEDULER_OBSERVER
245 ASSERT_WARNING( g_NumThreads == g_ActualMaxThreads, "Library did not provide sufficient threads");
247 ASSERT_WARNING(n < c_Timeout,g_Wakeup_Msg);
248 // Workaround in case a missed wakeup takes place
249 if ( n == c_Timeout ) {
250 tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task();
257 inline void WaitUntilConcurrencyPeaks () { WaitUntilConcurrencyPeaks(g_NumThreads); }
259 inline bool IsMaster() {
260 return Harness::CurrentTid() == g_Master;
263 inline bool IsThrowingThread() {
264 return g_ExceptionInMaster ^ IsMaster() ? true : false;
267 class CancellatorTask : public tbb::task {
268 static volatile bool s_Ready;
269 tbb::task_group_context &m_groupToCancel;
270 intptr_t m_cancellationThreshold;
272 tbb::task* execute () __TBB_override {
273 Harness::ConcurrencyTracker ct;
275 while ( g_CurExecuted < m_cancellationThreshold )
277 m_groupToCancel.cancel_group_execution();
278 g_ExecutedAtLastCatch = g_CurExecuted;
282 CancellatorTask ( tbb::task_group_context& ctx, intptr_t threshold )
283 : m_groupToCancel(ctx), m_cancellationThreshold(threshold)
288 static void Reset () { s_Ready = false; }
290 static bool WaitUntilReady () {
291 const intptr_t limit = 10000000;
295 } while( !s_Ready && ++n < limit );
296 // should yield once, then continue if Cancellator is ready.
297 ASSERT( s_Ready || n == limit, NULL );
302 volatile bool CancellatorTask::s_Ready = false;
304 template<class LauncherTaskT, class CancellatorTaskT>
305 void RunCancellationTest ( intptr_t threshold = 1 )
307 tbb::task_group_context ctx;
308 tbb::empty_task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
310 r.spawn( *new( r.allocate_child() ) CancellatorTaskT(ctx, threshold) );
312 r.spawn( *new( r.allocate_child() ) LauncherTaskT(ctx) );