Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      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 
     18 #include <string>
     19 
     20 #include "atomic_integer.h"
     21 #include "common_test.h"
     22 #include "thread_pool.h"
     23 
     24 namespace art {
     25 
     26 class CountTask : public Task {
     27  public:
     28   explicit CountTask(AtomicInteger* count) : count_(count), verbose_(false) {}
     29 
     30   void Run(Thread* self) {
     31     if (verbose_) {
     32       LOG(INFO) << "Running: " << *self;
     33     }
     34     // Simulate doing some work.
     35     usleep(100);
     36     // Increment the counter which keeps track of work completed.
     37     ++*count_;
     38   }
     39 
     40   void Finalize() {
     41     if (verbose_) {
     42       LOG(INFO) << "Finalizing: " << *Thread::Current();
     43     }
     44     delete this;
     45   }
     46 
     47  private:
     48   AtomicInteger* const count_;
     49   const bool verbose_;
     50 };
     51 
     52 class ThreadPoolTest : public CommonTest {
     53  public:
     54   static int32_t num_threads;
     55 };
     56 
     57 int32_t ThreadPoolTest::num_threads = 4;
     58 
     59 // Check that the thread pool actually runs tasks that you assign it.
     60 TEST_F(ThreadPoolTest, CheckRun) {
     61   Thread* self = Thread::Current();
     62   ThreadPool thread_pool(num_threads);
     63   AtomicInteger count(0);
     64   static const int32_t num_tasks = num_threads * 4;
     65   for (int32_t i = 0; i < num_tasks; ++i) {
     66     thread_pool.AddTask(self, new CountTask(&count));
     67   }
     68   thread_pool.StartWorkers(self);
     69   // Wait for tasks to complete.
     70   thread_pool.Wait(self, true, false);
     71   // Make sure that we finished all the work.
     72   EXPECT_EQ(num_tasks, count);
     73 }
     74 
     75 TEST_F(ThreadPoolTest, StopStart) {
     76   Thread* self = Thread::Current();
     77   ThreadPool thread_pool(num_threads);
     78   AtomicInteger count(0);
     79   static const int32_t num_tasks = num_threads * 4;
     80   for (int32_t i = 0; i < num_tasks; ++i) {
     81     thread_pool.AddTask(self, new CountTask(&count));
     82   }
     83   usleep(200);
     84   // Check that no threads started prematurely.
     85   EXPECT_EQ(0, count);
     86   // Signal the threads to start processing tasks.
     87   thread_pool.StartWorkers(self);
     88   usleep(200);
     89   thread_pool.StopWorkers(self);
     90   AtomicInteger bad_count(0);
     91   thread_pool.AddTask(self, new CountTask(&bad_count));
     92   usleep(200);
     93   // Ensure that the task added after the workers were stopped doesn't get run.
     94   EXPECT_EQ(0, bad_count);
     95   // Allow tasks to finish up and delete themselves.
     96   thread_pool.StartWorkers(self);
     97   while (count.load() != num_tasks && bad_count.load() != 1) {
     98     usleep(200);
     99   }
    100   thread_pool.StopWorkers(self);
    101 }
    102 
    103 class TreeTask : public Task {
    104  public:
    105   TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth)
    106       : thread_pool_(thread_pool),
    107         count_(count),
    108         depth_(depth) {}
    109 
    110   void Run(Thread* self) {
    111     if (depth_ > 1) {
    112       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
    113       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
    114     }
    115     // Increment the counter which keeps track of work completed.
    116     ++*count_;
    117   }
    118 
    119   void Finalize() {
    120     delete this;
    121   }
    122 
    123  private:
    124   ThreadPool* const thread_pool_;
    125   AtomicInteger* const count_;
    126   const int depth_;
    127 };
    128 
    129 // Test that adding new tasks from within a task works.
    130 TEST_F(ThreadPoolTest, RecursiveTest) {
    131   Thread* self = Thread::Current();
    132   ThreadPool thread_pool(num_threads);
    133   AtomicInteger count(0);
    134   static const int depth = 8;
    135   thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth));
    136   thread_pool.StartWorkers(self);
    137   thread_pool.Wait(self, true, false);
    138   EXPECT_EQ((1 << depth) - 1, count);
    139 }
    140 
    141 }  // namespace art
    142