1 /*
2     Copyright (c) 2005-2020 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 "tbb/scalable_allocator.h"
18 #include "tbb/atomic.h"
19 #include "tbb/aligned_space.h"
20 
21 #define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1
22 #include "harness.h"
23 #include "harness_barrier.h"
24 #if !__TBB_SOURCE_DIRECTLY_INCLUDED
25 #include "harness_tbb_independence.h"
26 #endif
27 
28 tbb::atomic<int> FinishedTasks;
29 const int MaxTasks = 16;
30 
31 /*--------------------------------------------------------------------*/
32 // The regression test against a bug triggered when malloc initialization
33 // and thread shutdown were called simultaneously, in which case
34 // Windows dynamic loader lock and allocator initialization/termination lock
35 // were taken in different order.
36 
37 class TestFunc1 {
38     Harness::SpinBarrier* my_barr;
39 public:
TestFunc1(Harness::SpinBarrier & barr)40     TestFunc1 (Harness::SpinBarrier& barr) : my_barr(&barr) {}
operator ()(bool do_malloc) const41     void operator() (bool do_malloc) const {
42         my_barr->wait();
43         if (do_malloc) scalable_malloc(10);
44         ++FinishedTasks;
45     }
46 };
47 
48 typedef NativeParallelForTask<bool,TestFunc1> TestTask1;
49 
Test1()50 void Test1 () {
51     int NTasks = min(MaxTasks, max(2, MaxThread));
52     Harness::SpinBarrier barr(NTasks);
53     TestFunc1 tf(barr);
54     FinishedTasks = 0;
55     tbb::aligned_space<TestTask1,MaxTasks> tasks;
56 
57     for(int i=0; i<NTasks; ++i) {
58         TestTask1* t = tasks.begin()+i;
59         new(t) TestTask1(i%2==0, tf);
60         t->start();
61     }
62 
63     Harness::Sleep(1000); // wait a second :)
64     ASSERT( FinishedTasks==NTasks, "Some threads appear to deadlock" );
65 
66     for(int i=0; i<NTasks; ++i) {
67         TestTask1* t = tasks.begin()+i;
68         t->wait_to_finish();
69         t->~TestTask1();
70     }
71 }
72 
73 /*--------------------------------------------------------------------*/
74 // The regression test against a bug when cross-thread deallocation
75 // caused livelock at thread shutdown.
76 
77 void* gPtr = NULL;
78 
79 class TestFunc2a {
80     Harness::SpinBarrier* my_barr;
81 public:
TestFunc2a(Harness::SpinBarrier & barr)82     TestFunc2a (Harness::SpinBarrier& barr) : my_barr(&barr) {}
operator ()(int) const83     void operator() (int) const {
84         gPtr = scalable_malloc(8);
85         my_barr->wait();
86         ++FinishedTasks;
87     }
88 };
89 
90 typedef NativeParallelForTask<int,TestFunc2a> TestTask2a;
91 
92 class TestFunc2b: NoAssign {
93     Harness::SpinBarrier* my_barr;
94     TestTask2a& my_ward;
95 public:
TestFunc2b(Harness::SpinBarrier & barr,TestTask2a & t)96     TestFunc2b (Harness::SpinBarrier& barr, TestTask2a& t) : my_barr(&barr), my_ward(t) {}
operator ()(int) const97     void operator() (int) const {
98         tbb::internal::spin_wait_while_eq(gPtr, (void*)NULL);
99         scalable_free(gPtr);
100         my_barr->wait();
101         my_ward.wait_to_finish();
102         ++FinishedTasks;
103     }
104 };
Test2()105 void Test2() {
106     Harness::SpinBarrier barr(2);
107     TestFunc2a func2a(barr);
108     TestTask2a t2a(0, func2a);
109     TestFunc2b func2b(barr, t2a);
110     NativeParallelForTask<int,TestFunc2b> t2b(1, func2b);
111     FinishedTasks = 0;
112     t2a.start(); t2b.start();
113     Harness::Sleep(1000); // wait a second :)
114     ASSERT( FinishedTasks==2, "Threads appear to deadlock" );
115     t2b.wait_to_finish(); // t2a is monitored by t2b
116 }
117 
118 #if _WIN32||_WIN64
119 
TestKeyDtor()120 void TestKeyDtor() {}
121 
122 #else
123 
124 void *currSmall, *prevSmall, *currLarge, *prevLarge;
125 
threadDtor(void *)126 extern "C" void threadDtor(void*) {
127     // First, release memory that was allocated before;
128     // it will not re-initialize the thread-local data if already deleted
129     prevSmall = currSmall;
130     scalable_free(currSmall);
131     prevLarge = currLarge;
132     scalable_free(currLarge);
133     // Then, allocate more memory.
134     // It will re-initialize the allocator data in the thread.
135     scalable_free(scalable_malloc(8));
136 }
137 
intersectingObjects(const void * p1,const void * p2,size_t n)138 inline bool intersectingObjects(const void *p1, const void *p2, size_t n)
139 {
140     return p1>p2 ? ((uintptr_t)p1-(uintptr_t)p2)<n : ((uintptr_t)p2-(uintptr_t)p1)<n;
141 }
142 
143 struct TestThread: NoAssign {
TestThreadTestThread144     TestThread(int ) {}
145 
operator ()TestThread146     void operator()( int /*id*/ ) const {
147         pthread_key_t key;
148 
149         currSmall = scalable_malloc(8);
150         ASSERT(!prevSmall || currSmall==prevSmall, "Possible memory leak");
151         currLarge = scalable_malloc(32*1024);
152         // intersectingObjects takes into account object shuffle
153         ASSERT(!prevLarge || intersectingObjects(currLarge, prevLarge, 32*1024), "Possible memory leak");
154         pthread_key_create( &key, &threadDtor );
155         pthread_setspecific(key, (const void*)42);
156     }
157 };
158 
159 // test releasing memory from pthread key destructor
TestKeyDtor()160 void TestKeyDtor() {
161     // Allocate region for large objects to prevent whole region release
162     // on scalable_free(currLarge) call, which result in wrong assert inside intersectingObjects check
163     void* preventLargeRelease = scalable_malloc(32*1024);
164     for (int i=0; i<4; i++)
165         NativeParallelFor( 1, TestThread(1) );
166     scalable_free(preventLargeRelease);
167 }
168 
169 #endif // _WIN32||_WIN64
170 
TestMain()171 int TestMain () {
172     Test1(); // requires malloc initialization so should be first
173     Test2();
174     TestKeyDtor();
175     return Harness::Done;
176 }
177