cancelable_callback.h 7.77 KB
Newer Older
gejun's avatar
gejun committed
1 2 3 4
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
5
// CancelableCallback is a wrapper around butil::Callback that allows
gejun's avatar
gejun committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// cancellation of a callback. CancelableCallback takes a reference on the
// wrapped callback until this object is destroyed or Reset()/Cancel() are
// called.
//
// NOTE:
//
// Calling CancelableCallback::Cancel() brings the object back to its natural,
// default-constructed state, i.e., CancelableCallback::callback() will return
// a null callback.
//
// THREAD-SAFETY:
//
// CancelableCallback objects must be created on, posted to, cancelled on, and
// destroyed on the same thread.
//
//
// EXAMPLE USAGE:
//
// In the following example, the test is verifying that RunIntensiveTest()
// Quit()s the message loop within 4 seconds. The cancelable callback is posted
// to the message loop, the intensive test runs, the message loop is run,
// then the callback is cancelled.
//
// void TimeoutCallback(const std::string& timeout_message) {
//   FAIL() << timeout_message;
//   MessageLoop::current()->QuitWhenIdle();
// }
//
34
// CancelableClosure timeout(butil::Bind(&TimeoutCallback, "Test timed out."));
gejun's avatar
gejun committed
35 36 37 38 39 40 41
// MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout.callback(),
//                                         4000)  // 4 seconds to run.
// RunIntensiveTest();
// MessageLoop::current()->Run();
// timeout.Cancel();  // Hopefully this is hit before the timeout callback runs.
//

42 43
#ifndef BUTIL_CANCELABLE_CALLBACK_H_
#define BUTIL_CANCELABLE_CALLBACK_H_
gejun's avatar
gejun committed
44

45 46 47 48 49 50 51
#include "butil/base_export.h"
#include "butil/bind.h"
#include "butil/callback.h"
#include "butil/callback_internal.h"
#include "butil/compiler_specific.h"
#include "butil/logging.h"
#include "butil/memory/weak_ptr.h"
gejun's avatar
gejun committed
52

53
namespace butil {
gejun's avatar
gejun committed
54 55 56 57 58 59 60 61 62 63

template <typename Sig>
class CancelableCallback;

template <>
class CancelableCallback<void(void)> {
 public:
  CancelableCallback() : weak_factory_(this) {}

  // |callback| must not be null.
64
  explicit CancelableCallback(const butil::Callback<void(void)>& callback)
gejun's avatar
gejun committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
      : weak_factory_(this),
        callback_(callback) {
    DCHECK(!callback.is_null());
    InitializeForwarder();
  }

  ~CancelableCallback() {}

  // Cancels and drops the reference to the wrapped callback.
  void Cancel() {
    weak_factory_.InvalidateWeakPtrs();
    forwarder_.Reset();
    callback_.Reset();
  }

  // Returns true if the wrapped callback has been cancelled.
  bool IsCancelled() const {
    return callback_.is_null();
  }

  // Sets |callback| as the closure that may be cancelled. |callback| may not
  // be null. Outstanding and any previously wrapped callbacks are cancelled.
87
  void Reset(const butil::Callback<void(void)>& callback) {
gejun's avatar
gejun committed
88 89 90 91 92 93 94 95 96 97 98 99
    DCHECK(!callback.is_null());

    // Outstanding tasks (e.g., posted to a message loop) must not be called.
    Cancel();

    // |forwarder_| is no longer valid after Cancel(), so re-bind.
    InitializeForwarder();

    callback_ = callback;
  }

  // Returns a callback that can be disabled by calling Cancel().
100
  const butil::Callback<void(void)>& callback() const {
gejun's avatar
gejun committed
101 102 103 104 105 106 107 108 109 110 111
    return forwarder_;
  }

 private:
  void Forward() {
    callback_.Run();
  }

  // Helper method to bind |forwarder_| using a weak pointer from
  // |weak_factory_|.
  void InitializeForwarder() {
112
    forwarder_ = butil::Bind(&CancelableCallback<void(void)>::Forward,
gejun's avatar
gejun committed
113 114 115 116
                            weak_factory_.GetWeakPtr());
  }

  // Used to ensure Forward() is not run when this object is destroyed.
117
  butil::WeakPtrFactory<CancelableCallback<void(void)> > weak_factory_;
gejun's avatar
gejun committed
118 119

  // The wrapper closure.
120
  butil::Callback<void(void)> forwarder_;
gejun's avatar
gejun committed
121 122

  // The stored closure that may be cancelled.
123
  butil::Callback<void(void)> callback_;
gejun's avatar
gejun committed
124 125 126 127 128 129 130 131 132 133

  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
};

template <typename A1>
class CancelableCallback<void(A1)> {
 public:
  CancelableCallback() : weak_factory_(this) {}

  // |callback| must not be null.
134
  explicit CancelableCallback(const butil::Callback<void(A1)>& callback)
gejun's avatar
gejun committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
      : weak_factory_(this),
        callback_(callback) {
    DCHECK(!callback.is_null());
    InitializeForwarder();
  }

  ~CancelableCallback() {}

  // Cancels and drops the reference to the wrapped callback.
  void Cancel() {
    weak_factory_.InvalidateWeakPtrs();
    forwarder_.Reset();
    callback_.Reset();
  }

  // Returns true if the wrapped callback has been cancelled.
  bool IsCancelled() const {
    return callback_.is_null();
  }

  // Sets |callback| as the closure that may be cancelled. |callback| may not
  // be null. Outstanding and any previously wrapped callbacks are cancelled.
157
  void Reset(const butil::Callback<void(A1)>& callback) {
gejun's avatar
gejun committed
158 159 160 161 162 163 164 165 166 167 168 169
    DCHECK(!callback.is_null());

    // Outstanding tasks (e.g., posted to a message loop) must not be called.
    Cancel();

    // |forwarder_| is no longer valid after Cancel(), so re-bind.
    InitializeForwarder();

    callback_ = callback;
  }

  // Returns a callback that can be disabled by calling Cancel().
170
  const butil::Callback<void(A1)>& callback() const {
gejun's avatar
gejun committed
171 172 173 174 175 176 177 178 179 180 181
    return forwarder_;
  }

 private:
  void Forward(A1 a1) const {
    callback_.Run(a1);
  }

  // Helper method to bind |forwarder_| using a weak pointer from
  // |weak_factory_|.
  void InitializeForwarder() {
182
    forwarder_ = butil::Bind(&CancelableCallback<void(A1)>::Forward,
gejun's avatar
gejun committed
183 184 185 186
                            weak_factory_.GetWeakPtr());
  }

  // Used to ensure Forward() is not run when this object is destroyed.
187
  butil::WeakPtrFactory<CancelableCallback<void(A1)> > weak_factory_;
gejun's avatar
gejun committed
188 189

  // The wrapper closure.
190
  butil::Callback<void(A1)> forwarder_;
gejun's avatar
gejun committed
191 192

  // The stored closure that may be cancelled.
193
  butil::Callback<void(A1)> callback_;
gejun's avatar
gejun committed
194 195 196 197 198 199 200 201 202 203

  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
};

template <typename A1, typename A2>
class CancelableCallback<void(A1, A2)> {
 public:
  CancelableCallback() : weak_factory_(this) {}

  // |callback| must not be null.
204
  explicit CancelableCallback(const butil::Callback<void(A1, A2)>& callback)
gejun's avatar
gejun committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
      : weak_factory_(this),
        callback_(callback) {
    DCHECK(!callback.is_null());
    InitializeForwarder();
  }

  ~CancelableCallback() {}

  // Cancels and drops the reference to the wrapped callback.
  void Cancel() {
    weak_factory_.InvalidateWeakPtrs();
    forwarder_.Reset();
    callback_.Reset();
  }

  // Returns true if the wrapped callback has been cancelled.
  bool IsCancelled() const {
    return callback_.is_null();
  }

  // Sets |callback| as the closure that may be cancelled. |callback| may not
  // be null. Outstanding and any previously wrapped callbacks are cancelled.
227
  void Reset(const butil::Callback<void(A1, A2)>& callback) {
gejun's avatar
gejun committed
228 229 230 231 232 233 234 235 236 237 238 239
    DCHECK(!callback.is_null());

    // Outstanding tasks (e.g., posted to a message loop) must not be called.
    Cancel();

    // |forwarder_| is no longer valid after Cancel(), so re-bind.
    InitializeForwarder();

    callback_ = callback;
  }

  // Returns a callback that can be disabled by calling Cancel().
240
  const butil::Callback<void(A1, A2)>& callback() const {
gejun's avatar
gejun committed
241 242 243 244 245 246 247 248 249 250 251
    return forwarder_;
  }

 private:
  void Forward(A1 a1, A2 a2) const {
    callback_.Run(a1, a2);
  }

  // Helper method to bind |forwarder_| using a weak pointer from
  // |weak_factory_|.
  void InitializeForwarder() {
252
    forwarder_ = butil::Bind(&CancelableCallback<void(A1, A2)>::Forward,
gejun's avatar
gejun committed
253 254 255 256
                            weak_factory_.GetWeakPtr());
  }

  // Used to ensure Forward() is not run when this object is destroyed.
257
  butil::WeakPtrFactory<CancelableCallback<void(A1, A2)> > weak_factory_;
gejun's avatar
gejun committed
258 259

  // The wrapper closure.
260
  butil::Callback<void(A1, A2)> forwarder_;
gejun's avatar
gejun committed
261 262

  // The stored closure that may be cancelled.
263
  butil::Callback<void(A1, A2)> callback_;
gejun's avatar
gejun committed
264 265 266 267 268 269

  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
};

typedef CancelableCallback<void(void)> CancelableClosure;

270
}  // namespace butil
gejun's avatar
gejun committed
271

272
#endif  // BUTIL_CANCELABLE_CALLBACK_H_