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 #include "common/test.h"
18 #include "common/utils.h"
19 
20 #include "oneapi/tbb/task_arena.h"
21 #include "oneapi/tbb/task_scheduler_observer.h"
22 #include "oneapi/tbb/enumerable_thread_specific.h"
23 #include "oneapi/tbb/parallel_for.h"
24 
25 //! \file conformance_task_arena.cpp
26 //! \brief Test for [scheduler.task_arena scheduler.task_scheduler_observer] specification
27 
28 // This test requires TBB in an uninitialized state
29 //! Test for uninitilized arena
30 //! \brief \ref requirement \ref interface
31 TEST_CASE("Test current_thread_index") {
32     REQUIRE_MESSAGE((tbb::this_task_arena::current_thread_index() == tbb::task_arena::not_initialized), "TBB was initialized state");
33 }
34 
35 //! Test task arena interfaces
36 //! \brief \ref requirement \ref interface
37 TEST_CASE("Arena interfaces") {
38     //! Initialization interfaces
39     oneapi::tbb::task_arena a(1,1); a.initialize();
40     std::atomic<bool> done{ false };
41     //! Enqueue interface
42     a.enqueue([&done] {
43         CHECK(oneapi::tbb::this_task_arena::max_concurrency() == 2);
44         done = true;
45     });
46     //! Execute interface
47     a.execute([&] {
48         //! oneapi::tbb::this_task_arena interfaces
49         CHECK(oneapi::tbb::this_task_arena::current_thread_index() >= 0);
50         //! Attach interface
51         oneapi::tbb::task_arena attached_arena{oneapi::tbb::task_arena::attach()};
52         CHECK(attached_arena.is_active());
53     });
54     while (!done) {
55         utils::yield();
56     }
57     //! Terminate interface
58     a.terminate();
59 }
60 
61 //! Test tasks isolation for inner oneapi::tbb::parallel_for loop
62 //! \brief \ref requirement \ref interface
63 TEST_CASE("Task isolation") {
64     const int N1 = 1000, N2 = 1000;
65     oneapi::tbb::enumerable_thread_specific<int> ets;
66     oneapi::tbb::parallel_for(0, N1, [&](int i) {
67         // Set a thread specific value
68         ets.local() = i;
69         // Run the second parallel loop in an isolated region to prevent the current thread
70         // from taking tasks related to the outer parallel loop.
71         oneapi::tbb::this_task_arena::isolate([&]{
72             oneapi::tbb::parallel_for(0, N2, utils::DummyBody(10));
73         });
74         REQUIRE(ets.local() == i);
75     });
76 }
77 
78 class conformance_observer: public oneapi::tbb::task_scheduler_observer {
79 public:
80     std::atomic<bool> is_entry_called{false};
81     std::atomic<bool> is_exit_called{false};
82 
83     conformance_observer( oneapi::tbb::task_arena &a ) : oneapi::tbb::task_scheduler_observer(a) {
84         observe(true); // activate the observer
85     }
86 
87     void on_scheduler_entry(bool) override {
88         is_entry_called.store(true, std::memory_order_relaxed);
89     }
90 
91     void on_scheduler_exit(bool) override {
92         is_exit_called.store(true, std::memory_order_relaxed);
93     }
94 
95     bool is_callbacks_called() {
96         return is_entry_called.load(std::memory_order_relaxed)
97             && is_exit_called.load(std::memory_order_relaxed);
98     }
99 };
100 
101 //! Test task arena observer interfaces
102 //! \brief \ref requirement \ref interface
103 TEST_CASE("Task arena observer") {
104     oneapi::tbb::task_arena a; a.initialize();
105     conformance_observer observer(a);
106     a.execute([&] {
107         oneapi::tbb::parallel_for(0, 100, utils::DummyBody(10), oneapi::tbb::simple_partitioner());
108     });
109     REQUIRE(observer.is_callbacks_called());
110 }
111 
112 //! Test task arena copy constructor
113 //! \brief \ref interface \ref requirement
114 TEST_CASE("Task arena copy constructor") {
115     oneapi::tbb::task_arena arena(1);
116     oneapi::tbb::task_arena copy = arena;
117 
118     REQUIRE(arena.max_concurrency() == copy.max_concurrency());
119     REQUIRE(arena.is_active() == copy.is_active());
120 }
121