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 |