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
16typedef PlatformTest TestCompletionCallbackTest;
17
18const 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)
23class 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
41class 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
64void 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
79void 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
90ExampleEmployer::ExampleEmployer() {
91}
92
93ExampleEmployer::~ExampleEmployer() {
94}
95
96bool 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
114TEST_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