1 /*
2     Copyright (c) 2005-2021 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 #define DOCTEST_CONFIG_IMPLEMENT
18 #include "common/test.h"
19 
20 #include "common/utils.h"
21 #include "common/utils_report.h"
22 
23 #include "tbb/global_control.h"
24 #include "tbb/task_arena.h"
25 #include "tbb/scalable_allocator.h"
26 
27 
28 // Lets slow down the main thread on exit
29 static constexpr int MAX_DELAY = 5;
30 struct GlobalObject {
31     ~GlobalObject() {
32         utils::Sleep(rand( ) % MAX_DELAY);
33     }
34 } go;
35 
36 void allocatorRandomThrashing() {
37     const int ARRAY_SIZE = 1000;
38     const int MAX_ITER = 10000;
39     const int MAX_ALLOC = 10 * 1024 * 1024;
40 
41     void *arr[ARRAY_SIZE] = {0};
42     for (int i = 0; i < rand() % MAX_ITER; ++i) {
43         // Random allocation size for random arrays
44         for (int j = 0; j < rand() % ARRAY_SIZE; ++j) {
45             arr[j] = scalable_malloc(rand() % MAX_ALLOC);
46         }
47         // Deallocate everything
48         for (int j = 0; j < ARRAY_SIZE; ++j) {
49             scalable_free(arr[j]);
50             arr[j] = NULL;
51         }
52     }
53 }
54 
55 void hangOnExitReproducer() {
56     const int P = tbb::global_control::max_allowed_parallelism;
57     tbb::task_arena test_arena;
58     for (int i = 0; i < P-1; i++) {
59         test_arena.enqueue(allocatorRandomThrashing);
60     }
61 }
62 
63 #if (_WIN32 || _WIN64) && !__TBB_WIN8UI_SUPPORT
64 #include <process.h> // _spawnl
65 void processSpawn(const char* self) {
66     _spawnl(_P_WAIT, self, self, "1", NULL);
67 }
68 #elif __unix__ || __APPLE__
69 #include <unistd.h> // fork/exec
70 #include <sys/wait.h> // waitpid
71 void processSpawn(const char* self) {
72     pid_t pid = fork();
73     if (pid == -1) {
74         REPORT("ERROR: fork failed.\n");
75     } else if (pid == 0) { // child
76         execl(self, self, "1", NULL);
77         REPORT("ERROR: exec never returns\n");
78         exit(1);
79     } else { // parent
80         int status;
81         waitpid(pid, &status, 0);
82     }
83 }
84 #else
85 void processSpawn(const char* /*self*/) {
86     REPORT("Known issue: no support for process spawn on this platform.\n");
87     REPORT("done\n");
88     exit(0);
89 }
90 #endif
91 
92 #if _MSC_VER && !__INTEL_COMPILER
93 #pragma warning (push)
94 #pragma warning (disable: 4702)  /* Unreachable code */
95 #endif
96 
97 //! \brief \ref error_guessing
98 TEST_CASE("testing shutdown hang") {
99     hangOnExitReproducer();
100     CHECK(true); // just to notify that test has assertions
101 }
102 
103 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182
104 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4447)
105 int main(int argc, char* argv[]) {
106     // Executed from child processes
107     if (argc == 2 && strcmp(argv[1],"1") == 0) {
108         return doctest::Context(argc, argv).run();
109     }
110 
111     // The number of executions is a tradeoff
112     // between execution time and NBTS statistics
113     const int EXEC_TIMES = 100;
114     const char* self = argv[0];
115     for (int i = 0; i < EXEC_TIMES; i++) {
116         processSpawn(self);
117     }
118 
119 #if _MSC_VER && !__INTEL_COMPILER
120 #pragma warning (pop)
121 #endif
122 }
123 DOCTEST_MSVC_SUPPRESS_WARNING_POP
124