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