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 "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: |
40 | TestFunc1 (Harness::SpinBarrier& barr) : my_barr(&barr) {} |
41 | 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 | |
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: |
82 | TestFunc2a (Harness::SpinBarrier& barr) : my_barr(&barr) {} |
83 | 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: |
96 | TestFunc2b (Harness::SpinBarrier& barr, TestTask2a& t) : my_barr(&barr), my_ward(t) {} |
97 | 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 | }; |
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 | |
120 | void TestKeyDtor() {} |
121 | |
122 | #else |
123 | |
124 | void *currSmall, *prevSmall, *currLarge, *prevLarge; |
125 | |
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 | |
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 { |
144 | TestThread(int ) {} |
145 | |
146 | 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 |
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 | |
171 | int TestMain () { |
172 | Test1(); // requires malloc initialization so should be first |
173 | Test2(); |
174 | TestKeyDtor(); |
175 | return Harness::Done; |
176 | } |
177 | |