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