| OLD | NEW |
| (Empty) |
| 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 namespace { | |
| 17 | |
| 18 const int kMagicResult = 8888; | |
| 19 | |
| 20 void CallClosureAfterCheckingResult(const base::Closure& closure, | |
| 21 bool* did_check_result, | |
| 22 int result) { | |
| 23 DCHECK_EQ(result, kMagicResult); | |
| 24 *did_check_result = true; | |
| 25 closure.Run(); | |
| 26 } | |
| 27 | |
| 28 // ExampleEmployer is a toy version of HostResolver | |
| 29 // TODO: restore damage done in extracting example from real code | |
| 30 // (e.g. bring back real destructor, bring back comments) | |
| 31 class ExampleEmployer { | |
| 32 public: | |
| 33 ExampleEmployer(); | |
| 34 ~ExampleEmployer(); | |
| 35 | |
| 36 // Do some imaginary work on a worker thread; | |
| 37 // when done, worker posts callback on the original thread. | |
| 38 // Returns true on success | |
| 39 bool DoSomething(const net::CompletionCallback& callback); | |
| 40 | |
| 41 private: | |
| 42 class ExampleWorker; | |
| 43 friend class ExampleWorker; | |
| 44 scoped_refptr<ExampleWorker> request_; | |
| 45 DISALLOW_COPY_AND_ASSIGN(ExampleEmployer); | |
| 46 }; | |
| 47 | |
| 48 // Helper class; this is how ExampleEmployer puts work on a different thread | |
| 49 class ExampleEmployer::ExampleWorker | |
| 50 : public base::RefCountedThreadSafe<ExampleWorker> { | |
| 51 public: | |
| 52 ExampleWorker(ExampleEmployer* employer, | |
| 53 const net::CompletionCallback& callback) | |
| 54 : employer_(employer), | |
| 55 callback_(callback), | |
| 56 origin_loop_(base::MessageLoop::current()) {} | |
| 57 void DoWork(); | |
| 58 void DoCallback(); | |
| 59 private: | |
| 60 friend class base::RefCountedThreadSafe<ExampleWorker>; | |
| 61 | |
| 62 ~ExampleWorker() {} | |
| 63 | |
| 64 // Only used on the origin thread (where DoSomething was called). | |
| 65 ExampleEmployer* employer_; | |
| 66 net::CompletionCallback callback_; | |
| 67 // Used to post ourselves onto the origin thread. | |
| 68 base::Lock origin_loop_lock_; | |
| 69 base::MessageLoop* origin_loop_; | |
| 70 }; | |
| 71 | |
| 72 void ExampleEmployer::ExampleWorker::DoWork() { | |
| 73 // Running on the worker thread | |
| 74 // In a real worker thread, some work would be done here. | |
| 75 // Pretend it is, and send the completion callback. | |
| 76 | |
| 77 // The origin loop could go away while we are trying to post to it, so we | |
| 78 // need to call its PostTask method inside a lock. See ~ExampleEmployer. | |
| 79 { | |
| 80 base::AutoLock locked(origin_loop_lock_); | |
| 81 if (origin_loop_) | |
| 82 origin_loop_->PostTask(FROM_HERE, | |
| 83 base::Bind(&ExampleWorker::DoCallback, this)); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 void ExampleEmployer::ExampleWorker::DoCallback() { | |
| 88 // Running on the origin thread. | |
| 89 | |
| 90 // Drop the employer_'s reference to us. Do this before running the | |
| 91 // callback since the callback might result in the employer being | |
| 92 // destroyed. | |
| 93 employer_->request_ = NULL; | |
| 94 | |
| 95 callback_.Run(kMagicResult); | |
| 96 } | |
| 97 | |
| 98 ExampleEmployer::ExampleEmployer() { | |
| 99 } | |
| 100 | |
| 101 ExampleEmployer::~ExampleEmployer() { | |
| 102 } | |
| 103 | |
| 104 bool ExampleEmployer::DoSomething(const net::CompletionCallback& callback) { | |
| 105 DCHECK(!request_.get()) << "already in use"; | |
| 106 | |
| 107 request_ = new ExampleWorker(this, callback); | |
| 108 | |
| 109 // Dispatch to worker thread... | |
| 110 if (!base::WorkerPool::PostTask( | |
| 111 FROM_HERE, | |
| 112 base::Bind(&ExampleWorker::DoWork, request_.get()), | |
| 113 true)) { | |
| 114 NOTREACHED(); | |
| 115 request_ = NULL; | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 return true; | |
| 120 } | |
| 121 | |
| 122 } // namespace | |
| 123 | |
| 124 typedef PlatformTest TestCompletionCallbackTest; | |
| 125 | |
| 126 TEST_F(TestCompletionCallbackTest, Simple) { | |
| 127 ExampleEmployer boss; | |
| 128 net::TestCompletionCallback callback; | |
| 129 bool queued = boss.DoSomething(callback.callback()); | |
| 130 EXPECT_TRUE(queued); | |
| 131 int result = callback.WaitForResult(); | |
| 132 EXPECT_EQ(result, kMagicResult); | |
| 133 } | |
| 134 | |
| 135 TEST_F(TestCompletionCallbackTest, Closure) { | |
| 136 ExampleEmployer boss; | |
| 137 net::TestClosure closure; | |
| 138 bool did_check_result = false; | |
| 139 net::CompletionCallback completion_callback = | |
| 140 base::Bind(&CallClosureAfterCheckingResult, | |
| 141 closure.closure(), base::Unretained(&did_check_result)); | |
| 142 bool queued = boss.DoSomething(completion_callback); | |
| 143 EXPECT_TRUE(queued); | |
| 144 | |
| 145 EXPECT_FALSE(did_check_result); | |
| 146 closure.WaitForResult(); | |
| 147 EXPECT_TRUE(did_check_result); | |
| 148 } | |
| 149 | |
| 150 // TODO: test deleting ExampleEmployer while work outstanding | |
| OLD | NEW |