Committing Intel(R) TBB 2018 source code
[platform/upstream/tbb.git] / src / test / harness_eh.h
1 /*
2     Copyright (c) 2005-2017 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
18
19 */
20
21 #include <typeinfo>
22 #include "tbb/tbb_exception.h"
23 #include "tbb/atomic.h"
24 #if USE_TASK_SCHEDULER_OBSERVER
25 #include "tbb/task_scheduler_observer.h"
26 #endif
27 #include "harness.h"
28 #include "harness_concurrency_tracker.h"
29
30 int g_NumThreads = 0;
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;
34
35 tbb::atomic<intptr_t> g_CurExecuted,
36                       g_ExecutedAtLastCatch,
37                       g_ExecutedAtFirstCatch,
38                       g_ExceptionsThrown,
39                       g_MasterExecutedThrow,     // number of times master entered exception code
40                       g_NonMasterExecutedThrow,  // number of times nonmaster entered exception code
41                       g_PipelinesStarted;
42 volatile bool g_ExceptionCaught = false,
43               g_UnknownException = false;
44
45 #if USE_TASK_SCHEDULER_OBSERVER
46 tbb::atomic<intptr_t> g_ActualMaxThreads;
47 tbb::atomic<intptr_t> g_ActualCurrentThreads;
48 #endif
49
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.)
53               g_Flog = false,
54               g_MasterExecuted = false,
55               g_NonMasterExecuted = false;
56
57 bool    g_ExceptionInMaster = false;
58 bool    g_SolitaryException = false;
59 bool    g_NestedPipelines   = false;
60
61 //! Number of exceptions propagated into the user code (i.e. intercepted by the tests)
62 tbb::atomic<intptr_t> g_NumExceptionsCaught;
63
64 //-----------------------------------------------------------
65
66 #if USE_TASK_SCHEDULER_OBSERVER
67 class eh_test_observer : public tbb::task_scheduler_observer {
68 public:
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;
73             while(q < p) {
74                 q = g_ActualMaxThreads.compare_and_swap(p,q);
75             }
76         }
77         else {
78             // size_t q = g_ActualMaxThreads;
79         }
80     }
81     void on_scheduler_exit(bool is_worker) __TBB_override {
82         if(is_worker) {
83             --g_ActualCurrentThreads;
84         }
85     }
86 };
87 #endif
88 //-----------------------------------------------------------
89
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;
99     g_Flog = flog;
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
105 #endif
106     g_ExceptionsThrown = g_NumExceptionsCaught = g_PipelinesStarted = 0;
107 }
108
109 #if TBB_USE_EXCEPTIONS
110 class test_exception : public std::exception {
111     const char* my_description;
112 public:
113     test_exception ( const char* description ) : my_description(description) {}
114
115     const char* what() const throw() __TBB_override { return my_description; }
116 };
117
118 class solitary_test_exception : public test_exception {
119 public:
120     solitary_test_exception ( const char* description ) : test_exception(description) {}
121 };
122
123 #if TBB_USE_CAPTURED_EXCEPTION
124     typedef tbb::captured_exception PropagatedException;
125     #define EXCEPTION_NAME(e) e.name()
126 #else
127     typedef test_exception PropagatedException;
128     #define EXCEPTION_NAME(e) typeid(e).name()
129 #endif
130
131 #define EXCEPTION_DESCR "Test exception"
132
133 #if HARNESS_EH_SIMPLE_MODE
134
135 static void ThrowTestException () {
136     ++g_ExceptionsThrown;
137     throw test_exception(EXCEPTION_DESCR);
138 }
139
140 #else /* !HARNESS_EH_SIMPLE_MODE */
141
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 (??)
148         return;
149     }
150     while ( Existed() < threshold )
151         __TBB_Yield();
152     if ( !g_SolitaryException ) {
153         ++g_ExceptionsThrown;
154         if(inMaster) ++g_MasterExecutedThrow; else ++g_NonMasterExecutedThrow;
155         throw test_exception(EXCEPTION_DESCR);
156     }
157     // g_SolitaryException == true
158     if(g_NestedPipelines) {
159         // only throw exception if we have started at least two inner pipelines
160         // else return
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);
165             }
166         }
167     }
168     else {
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);
172         }
173     }
174 }
175 #endif /* !HARNESS_EH_SIMPLE_MODE */
176
177 #define UPDATE_COUNTS()     \
178     { \
179         ++g_CurExecuted; \
180         if(g_Master == Harness::CurrentTid()) g_MasterExecuted = true; \
181         else g_NonMasterExecuted = true; \
182         if( tbb::task::self().is_cancelled() ) ++g_TGCCancelled; \
183     }
184
185 #define CATCH()     \
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" ); \
200     } catch ( ... ) { \
201         g_ExceptionCaught = l_ExceptionCaughtAtCurrentLevel = true; \
202         g_UnknownException = unknownException = true; \
203     } \
204     if ( !g_SolitaryException ) \
205         REMARK_ONCE ("Multiple exceptions mode: %d throws", (intptr_t)g_ExceptionsThrown);
206
207 #define ASSERT_EXCEPTION() \
208     { \
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"); \
213     }
214
215 #define CATCH_AND_ASSERT() \
216     CATCH() \
217     ASSERT_EXCEPTION()
218
219 #else /* !TBB_USE_EXCEPTIONS */
220
221 inline void ThrowTestException ( intptr_t ) {}
222
223 #endif /* !TBB_USE_EXCEPTIONS */
224
225 #define TRY()   \
226     bool l_ExceptionCaughtAtCurrentLevel = false, unknownException = false;    \
227     __TBB_TRY {
228
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);                        \
233     }
234
235 const int c_Timeout = 1000000;
236
237 void WaitUntilConcurrencyPeaks ( int expected_peak ) {
238     if ( g_Flog )
239         return;
240     int n = 0;
241 retry:
242     while ( ++n < c_Timeout && (int)Harness::ConcurrencyTracker::PeakParallelism() < expected_peak )
243         __TBB_Yield();
244 #if USE_TASK_SCHEDULER_OBSERVER
245     ASSERT_WARNING( g_NumThreads == g_ActualMaxThreads, "Library did not provide sufficient threads");
246 #endif
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();
251         r.spawn(r);
252         n = 0;
253         goto retry;
254     }
255 }
256
257 inline void WaitUntilConcurrencyPeaks () { WaitUntilConcurrencyPeaks(g_NumThreads); }
258
259 inline bool IsMaster() {
260     return Harness::CurrentTid() == g_Master;
261 }
262
263 inline bool IsThrowingThread() {
264     return g_ExceptionInMaster ^ IsMaster() ? true : false;
265 }
266
267 class CancellatorTask : public tbb::task {
268     static volatile bool s_Ready;
269     tbb::task_group_context &m_groupToCancel;
270     intptr_t m_cancellationThreshold;
271
272     tbb::task* execute () __TBB_override {
273         Harness::ConcurrencyTracker ct;
274         s_Ready = true;
275         while ( g_CurExecuted < m_cancellationThreshold )
276             __TBB_Yield();
277         m_groupToCancel.cancel_group_execution();
278         g_ExecutedAtLastCatch = g_CurExecuted;
279         return NULL;
280     }
281 public:
282     CancellatorTask ( tbb::task_group_context& ctx, intptr_t threshold )
283         : m_groupToCancel(ctx), m_cancellationThreshold(threshold)
284     {
285         s_Ready = false;
286     }
287
288     static void Reset () { s_Ready = false; }
289
290     static bool WaitUntilReady () {
291         const intptr_t limit = 10000000;
292         intptr_t n = 0;
293         do {
294             __TBB_Yield();
295         } while( !s_Ready && ++n < limit );
296         // should yield once, then continue if Cancellator is ready.
297         ASSERT( s_Ready || n == limit, NULL );
298         return s_Ready;
299     }
300 };
301
302 volatile bool CancellatorTask::s_Ready = false;
303
304 template<class LauncherTaskT, class CancellatorTaskT>
305 void RunCancellationTest ( intptr_t threshold = 1 )
306 {
307     tbb::task_group_context  ctx;
308     tbb::empty_task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
309     r.set_ref_count(3);
310     r.spawn( *new( r.allocate_child() ) CancellatorTaskT(ctx, threshold) );
311     __TBB_Yield();
312     r.spawn( *new( r.allocate_child() ) LauncherTaskT(ctx) );
313     TRY();
314         r.wait_for_all();
315     CATCH_AND_FAIL();
316     r.destroy(r);
317 }