This test simulates one of the current use patterns of the task scheduler. There are a number of groups. The groups are allocated to stages. All groups in a stage execute concurrently. When all groups in that stage finish the next stage is started.
| 102 | // concurrently. When all groups in that stage finish the next |
| 103 | // stage is started. |
| 104 | TEST(TaskScheduler, Stress) { |
| 105 | #ifndef ARROW_ENABLE_THREADING |
| 106 | GTEST_SKIP() << "Test requires threading support"; |
| 107 | #endif |
| 108 | constexpr int kNumThreads = 8; |
| 109 | constexpr int kNumGroups = 8; |
| 110 | constexpr int kGroupsPerStage = 3; |
| 111 | constexpr int kTasksPerGroup = 32; |
| 112 | constexpr int kNumStages = (kNumGroups % kGroupsPerStage == 0) |
| 113 | ? (kNumGroups / kGroupsPerStage) |
| 114 | : (kNumGroups / kGroupsPerStage) + 1; |
| 115 | constexpr int kTrailingGroups = (kNumGroups % kGroupsPerStage == 0) |
| 116 | ? kGroupsPerStage |
| 117 | : kNumGroups % kGroupsPerStage; |
| 118 | |
| 119 | ThreadIndexer thread_indexer; |
| 120 | int num_threads = std::min(static_cast<int>(thread_indexer.Capacity()), kNumThreads); |
| 121 | ASSERT_OK_AND_ASSIGN(std::shared_ptr<ThreadPool> thread_pool, |
| 122 | ThreadPool::Make(num_threads)); |
| 123 | |
| 124 | std::array<std::atomic<int>, kNumStages - 1> stage_counters; |
| 125 | for (int i = 0; i < kNumStages - 1; i++) { |
| 126 | stage_counters[i].store(kGroupsPerStage); |
| 127 | } |
| 128 | std::atomic<int> final_counter(kTrailingGroups); |
| 129 | std::mutex mutex; |
| 130 | std::condition_variable finish_cv; |
| 131 | |
| 132 | std::vector<int> group_ids; |
| 133 | auto scheduler = TaskScheduler::Make(); |
| 134 | |
| 135 | std::function<void(std::size_t, int)> start_next_stage = [&](std::size_t thread_id, |
| 136 | int stage_index) { |
| 137 | int start = stage_index * kGroupsPerStage; |
| 138 | int end = std::min(kNumGroups, start + kGroupsPerStage); |
| 139 | for (int i = start; i < end; i++) { |
| 140 | ASSERT_OK(thread_pool->Spawn([&, i] { |
| 141 | std::size_t my_thread_id = thread_indexer(); |
| 142 | SleepABit(); |
| 143 | ASSERT_OK(scheduler->StartTaskGroup(my_thread_id, group_ids[i], kTasksPerGroup)); |
| 144 | })); |
| 145 | } |
| 146 | }; |
| 147 | |
| 148 | for (auto i = 0; i < kNumGroups; i++) { |
| 149 | int next_stage = (i / kGroupsPerStage) + 1; |
| 150 | TaskScheduler::TaskGroupContinuationImpl finish = |
| 151 | MakeFinalContinuation(&final_counter, &mutex, &finish_cv); |
| 152 | if (next_stage < kNumStages) { |
| 153 | finish = |
| 154 | MakeContinuation(&stage_counters[next_stage - 1], start_next_stage, next_stage); |
| 155 | } |
| 156 | group_ids.push_back(scheduler->RegisterTaskGroup(SlowTaskImpl, finish)); |
| 157 | } |
| 158 | scheduler->RegisterEnd(); |
| 159 | |
| 160 | TaskScheduler::AbortContinuationImpl abort = [] { FAIL() << "Unexpected abort"; }; |
| 161 | TaskScheduler::ScheduleImpl schedule = |
nothing calls this directly
no test coverage detected