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