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