xref: /oneTBB/test/tbb/test_eh_thread.cpp (revision 5d8ddb3f)
1 /*
2     Copyright (c) 2020-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 #include "common/config.h"
18 
19 #include "tbb/parallel_for.h"
20 #include "tbb/global_control.h"
21 
22 #include "common/test.h"
23 #include "common/utils.h"
24 #include "common/utils_concurrency_limit.h"
25 
26 #include <atomic>
27 #include <condition_variable>
28 #include <thread>
29 #include <vector>
30 
31 //! \file test_eh_thread.cpp
32 //! \brief Test for [internal] functionality
33 
34 // On Windows there is no real thread number limit beside available memory.
35 // Therefore, the test for thread limit is unreasonable.
36 // TODO: enable limitThreads with sanitizer under docker
37 #if TBB_USE_EXCEPTIONS && !_WIN32 && !__ANDROID__
38 
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 
43 void limitThreads(size_t limit)
44 {
45     rlimit rlim;
46 
47     int ret = getrlimit(RLIMIT_NPROC, &rlim);
48     CHECK_MESSAGE(0 == ret, "getrlimit has returned an error");
49 
50     rlim.rlim_cur = (rlim.rlim_max == (rlim_t)RLIM_INFINITY) ? limit : utils::min(limit, rlim.rlim_max);
51 
52     ret = setrlimit(RLIMIT_NPROC, &rlim);
53     CHECK_MESSAGE(0 == ret, "setrlimit has returned an error");
54 }
55 
56 static bool g_exception_caught = false;
57 static std::mutex m;
58 static std::condition_variable cv;
59 static std::atomic<bool> stop{ false };
60 
61 static void* thread_routine(void*)
62 {
63     std::unique_lock<std::mutex> lock(m);
64     cv.wait(lock, [] { return stop == true; });
65     return 0;
66 }
67 
68 class Thread {
69     pthread_t mHandle{};
70     bool mValid{};
71 public:
72     Thread() {
73         mValid = false;
74         pthread_attr_t attr;
75         // Limit the stack size not to consume all virtual memory on 32 bit platforms.
76         if (pthread_attr_init(&attr) == 0 && pthread_attr_setstacksize(&attr, 100*1024) == 0) {
77             mValid = pthread_create(&mHandle, &attr, thread_routine, /* arg = */ nullptr) == 0;
78         }
79     }
80     bool isValid() const { return mValid; }
81     void join() {
82         pthread_join(mHandle, nullptr);
83     }
84 };
85 
86 //! Test for exception when too many threads
87 //! \brief \ref resource_usage
88 TEST_CASE("Too many threads") {
89     if (utils::get_platform_max_threads() < 2) {
90         // The test expects that the scheduler will try to create at least one thread.
91         return;
92     }
93 
94     // Some systems set really big limit (e.g. >45К) for the number of processes/threads
95     limitThreads(1024);
96 
97     std::thread /* isolate test */ ([] {
98         std::vector<Thread> threads;
99         stop = false;
100         auto finalize = [&] {
101             stop = true;
102             cv.notify_all();
103             for (auto& t : threads) {
104                 t.join();
105             }
106         };
107 
108         for (int i = 0;; ++i) {
109             Thread thread;
110             if (!thread.isValid()) {
111                 break;
112             }
113             threads.push_back(thread);
114             if (i == 1024) {
115                 WARN_MESSAGE(false, "setrlimit seems having no effect");
116                 finalize();
117                 return;
118             }
119         }
120         g_exception_caught = false;
121         try {
122             // Initialize the library to create worker threads
123             tbb::parallel_for(0, 2, [](int) {});
124         } catch (const std::exception & e) {
125             g_exception_caught = true;
126             // Do not CHECK to avoid memory allocation (we can be out of memory)
127             if (e.what()== nullptr) {
128                 FAIL("Exception does not have description");
129             }
130         }
131         // Do not CHECK to avoid memory allocation (we can be out of memory)
132         if (!g_exception_caught) {
133             FAIL("No exception was caught");
134         }
135         finalize();
136     }).join();
137 }
138 #endif
139