|  | /* | 
|  | * Copyright (C) 2014 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "base/time_utils.h" | 
|  | #include "common_runtime_test.h" | 
|  | #include "task_processor.h" | 
|  | #include "thread_pool.h" | 
|  | #include "thread-inl.h" | 
|  |  | 
|  | namespace art { | 
|  | namespace gc { | 
|  |  | 
|  | class TaskProcessorTest : public CommonRuntimeTest { | 
|  | public: | 
|  | }; | 
|  |  | 
|  | class RecursiveTask : public HeapTask { | 
|  | public: | 
|  | RecursiveTask(TaskProcessor* task_processor, Atomic<size_t>* counter, size_t max_recursion) | 
|  | : HeapTask(NanoTime() + MsToNs(10)), task_processor_(task_processor), counter_(counter), | 
|  | max_recursion_(max_recursion) { | 
|  | } | 
|  | virtual void Run(Thread* self) OVERRIDE { | 
|  | if (max_recursion_ > 0) { | 
|  | task_processor_->AddTask(self, | 
|  | new RecursiveTask(task_processor_, counter_, max_recursion_ - 1)); | 
|  | counter_->FetchAndAddSequentiallyConsistent(1U); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | TaskProcessor* const task_processor_; | 
|  | Atomic<size_t>* const counter_; | 
|  | const size_t max_recursion_; | 
|  | }; | 
|  |  | 
|  | class WorkUntilDoneTask : public SelfDeletingTask { | 
|  | public: | 
|  | WorkUntilDoneTask(TaskProcessor* task_processor, Atomic<bool>* done_running) | 
|  | : task_processor_(task_processor), done_running_(done_running) { | 
|  | } | 
|  | virtual void Run(Thread* self) OVERRIDE { | 
|  | task_processor_->RunAllTasks(self); | 
|  | done_running_->StoreSequentiallyConsistent(true); | 
|  | } | 
|  |  | 
|  | private: | 
|  | TaskProcessor* const task_processor_; | 
|  | Atomic<bool>* done_running_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TaskProcessorTest, Interrupt) { | 
|  | ThreadPool thread_pool("task processor test", 1U); | 
|  | Thread* const self = Thread::Current(); | 
|  | TaskProcessor task_processor; | 
|  | static constexpr size_t kRecursion = 10; | 
|  | Atomic<bool> done_running(false); | 
|  | Atomic<size_t> counter(0); | 
|  | task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion)); | 
|  | task_processor.Start(self); | 
|  | // Add a task which will wait until interrupted to the thread pool. | 
|  | thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); | 
|  | thread_pool.StartWorkers(self); | 
|  | ASSERT_FALSE(done_running); | 
|  | // Wait until all the tasks are done, but since we didn't interrupt, done_running should be 0. | 
|  | while (counter.LoadSequentiallyConsistent() != kRecursion) { | 
|  | usleep(10); | 
|  | } | 
|  | ASSERT_FALSE(done_running); | 
|  | task_processor.Stop(self); | 
|  | thread_pool.Wait(self, true, false); | 
|  | // After the interrupt and wait, the WorkUntilInterruptedTasktask should have terminated and | 
|  | // set done_running_ to true. | 
|  | ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); | 
|  |  | 
|  | // Test that we finish remaining tasks before returning from RunTasksUntilInterrupted. | 
|  | counter.StoreSequentiallyConsistent(0); | 
|  | done_running.StoreSequentiallyConsistent(false); | 
|  | // Self interrupt before any of the other tasks run, but since we added them we should keep on | 
|  | // working until all the tasks are completed. | 
|  | task_processor.Stop(self); | 
|  | task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion)); | 
|  | thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); | 
|  | thread_pool.StartWorkers(self); | 
|  | thread_pool.Wait(self, true, false); | 
|  | ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); | 
|  | ASSERT_EQ(counter.LoadSequentiallyConsistent(), kRecursion); | 
|  | } | 
|  |  | 
|  | class TestOrderTask : public HeapTask { | 
|  | public: | 
|  | TestOrderTask(uint64_t expected_time, size_t expected_counter, size_t* counter) | 
|  | : HeapTask(expected_time), expected_counter_(expected_counter), counter_(counter) { | 
|  | } | 
|  | virtual void Run(Thread* thread ATTRIBUTE_UNUSED) OVERRIDE { | 
|  | ASSERT_EQ(*counter_, expected_counter_); | 
|  | ++*counter_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | const size_t expected_counter_; | 
|  | size_t* const counter_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TaskProcessorTest, Ordering) { | 
|  | static const size_t kNumTasks = 25; | 
|  | const uint64_t current_time = NanoTime(); | 
|  | Thread* const self = Thread::Current(); | 
|  | TaskProcessor task_processor; | 
|  | task_processor.Stop(self); | 
|  | size_t counter = 0; | 
|  | std::vector<std::pair<uint64_t, size_t>> orderings; | 
|  | for (size_t i = 0; i < kNumTasks; ++i) { | 
|  | orderings.push_back(std::make_pair(current_time + MsToNs(10U * i), i)); | 
|  | } | 
|  | for (size_t i = 0; i < kNumTasks; ++i) { | 
|  | std::swap(orderings[i], orderings[(i * 87654231 + 12345) % orderings.size()]); | 
|  | } | 
|  | for (const auto& pair : orderings) { | 
|  | auto* task = new TestOrderTask(pair.first, pair.second, &counter); | 
|  | task_processor.AddTask(self, task); | 
|  | } | 
|  | ThreadPool thread_pool("task processor test", 1U); | 
|  | Atomic<bool> done_running(false); | 
|  | // Add a task which will wait until interrupted to the thread pool. | 
|  | thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); | 
|  | ASSERT_FALSE(done_running.LoadSequentiallyConsistent()); | 
|  | thread_pool.StartWorkers(self); | 
|  | thread_pool.Wait(self, true, false); | 
|  | ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); | 
|  | ASSERT_EQ(counter, kNumTasks); | 
|  | } | 
|  |  | 
|  | }  // namespace gc | 
|  | }  // namespace art |