Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Illustrates how to use worker threads that issue completion callbacks
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/threading/worker_pool.h"
     11 #include "net/base/completion_callback.h"
     12 #include "net/base/test_completion_callback.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "testing/platform_test.h"
     15 
     16 typedef PlatformTest TestCompletionCallbackTest;
     17 
     18 const int kMagicResult = 8888;
     19 
     20 // ExampleEmployer is a toy version of HostResolver
     21 // TODO: restore damage done in extracting example from real code
     22 // (e.g. bring back real destructor, bring back comments)
     23 class ExampleEmployer {
     24  public:
     25   ExampleEmployer();
     26   ~ExampleEmployer();
     27 
     28   // Do some imaginary work on a worker thread;
     29   // when done, worker posts callback on the original thread.
     30   // Returns true on success
     31   bool DoSomething(const net::CompletionCallback& callback);
     32 
     33  private:
     34   class ExampleWorker;
     35   friend class ExampleWorker;
     36   scoped_refptr<ExampleWorker> request_;
     37   DISALLOW_COPY_AND_ASSIGN(ExampleEmployer);
     38 };
     39 
     40 // Helper class; this is how ExampleEmployer puts work on a different thread
     41 class ExampleEmployer::ExampleWorker
     42     : public base::RefCountedThreadSafe<ExampleWorker> {
     43  public:
     44   ExampleWorker(ExampleEmployer* employer,
     45                 const net::CompletionCallback& callback)
     46       : employer_(employer),
     47         callback_(callback),
     48         origin_loop_(base::MessageLoop::current()) {}
     49   void DoWork();
     50   void DoCallback();
     51  private:
     52   friend class base::RefCountedThreadSafe<ExampleWorker>;
     53 
     54   ~ExampleWorker() {}
     55 
     56   // Only used on the origin thread (where DoSomething was called).
     57   ExampleEmployer* employer_;
     58   net::CompletionCallback callback_;
     59   // Used to post ourselves onto the origin thread.
     60   base::Lock origin_loop_lock_;
     61   base::MessageLoop* origin_loop_;
     62 };
     63 
     64 void ExampleEmployer::ExampleWorker::DoWork() {
     65   // Running on the worker thread
     66   // In a real worker thread, some work would be done here.
     67   // Pretend it is, and send the completion callback.
     68 
     69   // The origin loop could go away while we are trying to post to it, so we
     70   // need to call its PostTask method inside a lock.  See ~ExampleEmployer.
     71   {
     72     base::AutoLock locked(origin_loop_lock_);
     73     if (origin_loop_)
     74       origin_loop_->PostTask(FROM_HERE,
     75                              base::Bind(&ExampleWorker::DoCallback, this));
     76   }
     77 }
     78 
     79 void ExampleEmployer::ExampleWorker::DoCallback() {
     80   // Running on the origin thread.
     81 
     82   // Drop the employer_'s reference to us.  Do this before running the
     83   // callback since the callback might result in the employer being
     84   // destroyed.
     85   employer_->request_ = NULL;
     86 
     87   callback_.Run(kMagicResult);
     88 }
     89 
     90 ExampleEmployer::ExampleEmployer() {
     91 }
     92 
     93 ExampleEmployer::~ExampleEmployer() {
     94 }
     95 
     96 bool ExampleEmployer::DoSomething(const net::CompletionCallback& callback) {
     97   DCHECK(!request_.get()) << "already in use";
     98 
     99   request_ = new ExampleWorker(this, callback);
    100 
    101   // Dispatch to worker thread...
    102   if (!base::WorkerPool::PostTask(
    103           FROM_HERE,
    104           base::Bind(&ExampleWorker::DoWork, request_.get()),
    105           true)) {
    106     NOTREACHED();
    107     request_ = NULL;
    108     return false;
    109   }
    110 
    111   return true;
    112 }
    113 
    114 TEST_F(TestCompletionCallbackTest, Simple) {
    115   ExampleEmployer boss;
    116   net::TestCompletionCallback callback;
    117   bool queued = boss.DoSomething(callback.callback());
    118   EXPECT_EQ(queued, true);
    119   int result = callback.WaitForResult();
    120   EXPECT_EQ(result, kMagicResult);
    121 }
    122 
    123 // TODO: test deleting ExampleEmployer while work outstanding
    124