151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #define DOCTEST_CONFIG_IMPLEMENT
1849e08aacStbbdev #include "common/test.h"
1951c0b2f7Stbbdev 
2051c0b2f7Stbbdev #include "common/utils.h"
2151c0b2f7Stbbdev #include "common/utils_report.h"
2251c0b2f7Stbbdev 
2351c0b2f7Stbbdev #include "tbb/global_control.h"
2451c0b2f7Stbbdev #include "tbb/task_arena.h"
2551c0b2f7Stbbdev #include "tbb/scalable_allocator.h"
2651c0b2f7Stbbdev 
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev // Lets slow down the main thread on exit
2951c0b2f7Stbbdev static constexpr int MAX_DELAY = 5;
3051c0b2f7Stbbdev struct GlobalObject {
~GlobalObjectGlobalObject3151c0b2f7Stbbdev     ~GlobalObject() {
3251c0b2f7Stbbdev         utils::Sleep(rand( ) % MAX_DELAY);
3351c0b2f7Stbbdev     }
3451c0b2f7Stbbdev } go;
3551c0b2f7Stbbdev 
allocatorRandomThrashing()3651c0b2f7Stbbdev void allocatorRandomThrashing() {
3751c0b2f7Stbbdev     const int ARRAY_SIZE = 1000;
3851c0b2f7Stbbdev     const int MAX_ITER = 10000;
3951c0b2f7Stbbdev     const int MAX_ALLOC = 10 * 1024 * 1024;
4051c0b2f7Stbbdev 
4157f524caSIlya Isaev     void *arr[ARRAY_SIZE] = {nullptr};
4251c0b2f7Stbbdev     for (int i = 0; i < rand() % MAX_ITER; ++i) {
4351c0b2f7Stbbdev         // Random allocation size for random arrays
4451c0b2f7Stbbdev         for (int j = 0; j < rand() % ARRAY_SIZE; ++j) {
4551c0b2f7Stbbdev             arr[j] = scalable_malloc(rand() % MAX_ALLOC);
4651c0b2f7Stbbdev         }
4751c0b2f7Stbbdev         // Deallocate everything
4851c0b2f7Stbbdev         for (int j = 0; j < ARRAY_SIZE; ++j) {
4951c0b2f7Stbbdev             scalable_free(arr[j]);
5057f524caSIlya Isaev             arr[j] = nullptr;
5151c0b2f7Stbbdev         }
5251c0b2f7Stbbdev     }
5351c0b2f7Stbbdev }
5451c0b2f7Stbbdev 
hangOnExitReproducer()5551c0b2f7Stbbdev void hangOnExitReproducer() {
5651c0b2f7Stbbdev     const int P = tbb::global_control::max_allowed_parallelism;
5751c0b2f7Stbbdev     tbb::task_arena test_arena;
5851c0b2f7Stbbdev     for (int i = 0; i < P-1; i++) {
5951c0b2f7Stbbdev         test_arena.enqueue(allocatorRandomThrashing);
6051c0b2f7Stbbdev     }
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev #if (_WIN32 || _WIN64) && !__TBB_WIN8UI_SUPPORT
6451c0b2f7Stbbdev #include <process.h> // _spawnl
processSpawn(const char * self)6551c0b2f7Stbbdev void processSpawn(const char* self) {
6657f524caSIlya Isaev     _spawnl(_P_WAIT, self, self, "1", nullptr);
6751c0b2f7Stbbdev }
68734f0bc0SPablo Romero #elif __unix__ || __APPLE__
6951c0b2f7Stbbdev #include <unistd.h> // fork/exec
7051c0b2f7Stbbdev #include <sys/wait.h> // waitpid
processSpawn(const char * self)7151c0b2f7Stbbdev void processSpawn(const char* self) {
7251c0b2f7Stbbdev     pid_t pid = fork();
7351c0b2f7Stbbdev     if (pid == -1) {
7451c0b2f7Stbbdev         REPORT("ERROR: fork failed.\n");
7551c0b2f7Stbbdev     } else if (pid == 0) { // child
7657f524caSIlya Isaev         execl(self, self, "1", nullptr);
7751c0b2f7Stbbdev         REPORT("ERROR: exec never returns\n");
7851c0b2f7Stbbdev         exit(1);
7951c0b2f7Stbbdev     } else { // parent
8051c0b2f7Stbbdev         int status;
8151c0b2f7Stbbdev         waitpid(pid, &status, 0);
8251c0b2f7Stbbdev     }
8351c0b2f7Stbbdev }
8451c0b2f7Stbbdev #else
processSpawn(const char *)8551c0b2f7Stbbdev void processSpawn(const char* /*self*/) {
8651c0b2f7Stbbdev     REPORT("Known issue: no support for process spawn on this platform.\n");
8751c0b2f7Stbbdev     REPORT("done\n");
8851c0b2f7Stbbdev     exit(0);
8951c0b2f7Stbbdev }
9051c0b2f7Stbbdev #endif
9151c0b2f7Stbbdev 
9251c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER
9351c0b2f7Stbbdev #pragma warning (push)
9451c0b2f7Stbbdev #pragma warning (disable: 4702)  /* Unreachable code */
9551c0b2f7Stbbdev #endif
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev //! \brief \ref error_guessing
9851c0b2f7Stbbdev TEST_CASE("testing shutdown hang") {
9951c0b2f7Stbbdev     hangOnExitReproducer();
10051c0b2f7Stbbdev     CHECK(true); // just to notify that test has assertions
10151c0b2f7Stbbdev }
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182
10451c0b2f7Stbbdev DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4447)
main(int argc,char * argv[])10551c0b2f7Stbbdev int main(int argc, char* argv[]) {
10651c0b2f7Stbbdev     // Executed from child processes
10751c0b2f7Stbbdev     if (argc == 2 && strcmp(argv[1],"1") == 0) {
10851c0b2f7Stbbdev         return doctest::Context(argc, argv).run();
10951c0b2f7Stbbdev     }
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     // The number of executions is a tradeoff
11251c0b2f7Stbbdev     // between execution time and NBTS statistics
11351c0b2f7Stbbdev     const int EXEC_TIMES = 100;
11451c0b2f7Stbbdev     const char* self = argv[0];
11551c0b2f7Stbbdev     for (int i = 0; i < EXEC_TIMES; i++) {
11651c0b2f7Stbbdev         processSpawn(self);
11751c0b2f7Stbbdev     }
11851c0b2f7Stbbdev 
11951c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER
12051c0b2f7Stbbdev #pragma warning (pop)
12151c0b2f7Stbbdev #endif
12251c0b2f7Stbbdev }
12351c0b2f7Stbbdev DOCTEST_MSVC_SUPPRESS_WARNING_POP
124