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// Testing automatic initialization of TBB task scheduler, so do not use task_scheduler_init anywhere.
18
19#include "tbb/task.h"
20
21#define HARNESS_NO_PARSE_COMMAND_LINE 1
22#include "harness.h"
23#include "tbb/atomic.h"
24
25static tbb::atomic<int> g_NumTestsExecuted;
26
27#define TEST_PROLOGUE() ++g_NumTestsExecuted
28
29// Global data used in testing use cases with cross-thread usage of TBB objects
30static tbb::task *g_Root1 = NULL,
31 *g_Root2 = NULL,
32 *g_Root3 = NULL,
33 *g_Task = NULL;
34
35#if __TBB_TASK_GROUP_CONTEXT
36static tbb::task_group_context* g_Ctx = NULL;
37#endif /* __TBB_TASK_GROUP_CONTEXT */
38
39
40void TestTaskSelf () {
41 TEST_PROLOGUE();
42 tbb::task& t = tbb::task::self();
43 ASSERT( !t.parent() && t.ref_count() == 1 && !t.affinity(), "Master's default task properties changed?" );
44}
45
46void TestRootAllocation () {
47 TEST_PROLOGUE();
48 tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
49 tbb::task::spawn_root_and_wait(r);
50}
51
52inline void ExecuteChildAndCleanup ( tbb::task &r, tbb::task &t ) {
53 r.set_ref_count(2);
54 r.spawn_and_wait_for_all(t);
55 r.destroy(r);
56}
57
58void TestChildAllocation () {
59 TEST_PROLOGUE();
60 tbb::task &t = *new( g_Root1->allocate_child() ) tbb::empty_task;
61 ExecuteChildAndCleanup( *g_Root1, t );
62}
63
64void TestAdditionalChildAllocation () {
65 TEST_PROLOGUE();
66 tbb::task &t = *new( tbb::task::allocate_additional_child_of(*g_Root2) ) tbb::empty_task;
67 ExecuteChildAndCleanup( *g_Root2, t );
68}
69
70#if __TBB_TASK_GROUP_CONTEXT
71void TestTaskGroupContextCreation () {
72 TEST_PROLOGUE();
73 tbb::task_group_context ctx;
74 tbb::task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
75 tbb::task::spawn_root_and_wait(r);
76}
77
78void TestRootAllocationWithContext () {
79 TEST_PROLOGUE();
80 tbb::task* root = new( tbb::task::allocate_root(*g_Ctx) ) tbb::empty_task;
81 tbb::task::spawn_root_and_wait(*root);
82}
83#endif /* __TBB_TASK_GROUP_CONTEXT */
84
85void TestSpawn () {
86 TEST_PROLOGUE();
87 tbb::task::spawn(*g_Task);
88}
89
90void TestWaitForAll () {
91 TEST_PROLOGUE();
92 g_Root3->wait_for_all();
93 tbb::task::destroy(*g_Root3);
94}
95
96typedef void (*TestFnPtr)();
97
98const TestFnPtr TestFuncsTable[] = {
99 TestTaskSelf, TestRootAllocation, TestChildAllocation, TestAdditionalChildAllocation,
100#if __TBB_TASK_GROUP_CONTEXT
101 TestTaskGroupContextCreation, TestRootAllocationWithContext,
102#endif /* __TBB_TASK_GROUP_CONTEXT */
103 TestSpawn, TestWaitForAll };
104
105const int NumTestFuncs = sizeof(TestFuncsTable) / sizeof(TestFnPtr);
106
107struct TestThreadBody : NoAssign, Harness::NoAfterlife {
108 // Each invocation of operator() happens in a fresh thread with zero-based ID
109 // id, and checks a specific auto-initialization scenario.
110 void operator() ( int id ) const {
111 ASSERT( id >= 0 && id < NumTestFuncs, "Test diver: NativeParallelFor is used incorrectly" );
112 TestFuncsTable[id]();
113 }
114};
115
116
117#include "../tbb/tls.h"
118
119void UseAFewNewTlsKeys () {
120 tbb::internal::tls<intptr_t> tls1, tls2, tls3, tls4;
121 tls1 = tls2 = tls3 = tls4 = -1;
122}
123
124using tbb::internal::spin_wait_until_eq;
125
126volatile bool FafStarted = false,
127 FafCanFinish = false,
128 FafCompleted = false;
129
130//! This task is supposed to be executed during termination of an auto-initialized master thread
131class FireAndForgetTask : public tbb::task {
132 tbb::task* execute () __TBB_override {
133 // Let another master thread proceed requesting new TLS keys
134 FafStarted = true;
135 UseAFewNewTlsKeys();
136 // Wait while another master thread dirtied its new TLS slots
137 spin_wait_until_eq( FafCanFinish, true );
138 FafCompleted = true;
139 return NULL;
140 }
141public: // to make gcc 3.2.3 happy
142 ~FireAndForgetTask() {
143 ASSERT(FafCompleted, "FireAndForgetTask got erroneously cancelled?");
144 }
145};
146
147#include "harness_barrier.h"
148Harness::SpinBarrier driver_barrier(2);
149
150struct DriverThreadBody : NoAssign, Harness::NoAfterlife {
151 void operator() ( int id ) const {
152 ASSERT( id < 2, "Only two test driver threads are expected" );
153 // a barrier is required to ensure both threads started; otherwise the test may deadlock:
154 // the first thread would execute FireAndForgetTask at shutdown and wait for FafCanFinish,
155 // while the second thread wouldn't even start waiting for the loader lock hold by the first one.
156 if ( id == 0 ) {
157 driver_barrier.wait();
158 // Prepare global data
159 g_Root1 = new( tbb::task::allocate_root() ) tbb::empty_task;
160 g_Root2 = new( tbb::task::allocate_root() ) tbb::empty_task;
161 g_Root3 = new( tbb::task::allocate_root() ) tbb::empty_task;
162 g_Task = new( g_Root3->allocate_child() ) tbb::empty_task;
163 g_Root3->set_ref_count(2);
164 // Run tests
165 NativeParallelFor( NumTestFuncs, TestThreadBody() );
166 ASSERT( g_NumTestsExecuted == NumTestFuncs, "Test driver: Wrong number of tests executed" );
167
168 // This test checks the validity of temporarily restoring the value of
169 // the last TLS slot for a given key during the termination of an
170 // auto-initialized master thread (in governor::auto_terminate).
171 // If anything goes wrong, generic_scheduler::cleanup_master() will assert.
172 // The context for this task must be valid till the task completion.
173#if __TBB_TASK_GROUP_CONTEXT
174 tbb::task &r = *new( tbb::task::allocate_root(*g_Ctx) ) FireAndForgetTask;
175#else
176 tbb::task &r = *new( tbb::task::allocate_root() ) FireAndForgetTask;
177#endif /* __TBB_TASK_GROUP_CONTEXT */
178 tbb::task::spawn(r);
179 }
180 else {
181#if __TBB_TASK_GROUP_CONTEXT
182 tbb::task_group_context ctx;
183 g_Ctx = &ctx;
184#endif /* __TBB_TASK_GROUP_CONTEXT */
185 driver_barrier.wait();
186 spin_wait_until_eq( FafStarted, true );
187 UseAFewNewTlsKeys();
188 FafCanFinish = true;
189 spin_wait_until_eq( FafCompleted, true );
190 }
191 }
192};
193
194int TestMain () {
195 // Do not use any TBB functionality in the main thread!
196 NativeParallelFor( 2, DriverThreadBody() );
197 return Harness::Done;
198}
199