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 16typedef PlatformTest TestCompletionCallbackTest; 17 18using net::CompletionCallback; 19 20const 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) 25class 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 43class 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 65void 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 85void 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 96ExampleEmployer::ExampleEmployer() { 97} 98 99ExampleEmployer::~ExampleEmployer() { 100} 101 102bool 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 118TEST_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