• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ExampleWorker(ExampleEmployer * employer,const net::CompletionCallback & callback)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 
~ExampleWorker()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 
DoWork()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 
DoCallback()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 
ExampleEmployer()90 ExampleEmployer::ExampleEmployer() {
91 }
92 
~ExampleEmployer()93 ExampleEmployer::~ExampleEmployer() {
94 }
95 
DoSomething(const net::CompletionCallback & callback)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 
TEST_F(TestCompletionCallbackTest,Simple)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