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