1// Copyright 2013 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#ifndef MEDIA_BASE_CALLBACK_HOLDER_H_
6#define MEDIA_BASE_CALLBACK_HOLDER_H_
7
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/callback_helpers.h"
11#include "media/base/bind_to_current_loop.h"
12
13namespace media {
14
15// A helper class that can hold a callback from being fired.
16template <typename CB> class CallbackHolder {
17 public:
18  CallbackHolder() : hold_(false) {}
19
20  ~CallbackHolder() {
21    // Make sure all callbacks are satisfied!
22    DCHECK(!hold_);
23    DCHECK(original_cb_.is_null());
24    DCHECK(held_cb_.is_null());
25  }
26
27  // Sets the callback to be potentially held.
28  void SetCallback(const CB& cb) {
29    DCHECK(original_cb_.is_null());
30    DCHECK(held_cb_.is_null());
31    original_cb_ = cb;
32  }
33
34  bool IsNull() const {
35    return original_cb_.is_null() && held_cb_.is_null();
36  }
37
38  // Holds the callback when Run() is called.
39  void HoldCallback() { hold_ = true; }
40
41  // Runs or holds the callback as specified by |hold_|.
42  // This method has overloaded versions to support different types of CB.
43  void RunOrHold() {
44    DCHECK(held_cb_.is_null());
45    if (hold_)
46      held_cb_ = base::ResetAndReturn(&original_cb_);
47    else
48      base::ResetAndReturn(&original_cb_).Run();
49  }
50
51  template <typename A1> void RunOrHold(A1 a1) {
52    DCHECK(held_cb_.is_null());
53    if (hold_) {
54      held_cb_ = base::Bind(base::ResetAndReturn(&original_cb_),
55                            internal::TrampolineForward(a1));
56    } else {
57      base::ResetAndReturn(&original_cb_).Run(a1);
58    }
59  }
60
61  template <typename A1, typename A2> void RunOrHold(A1 a1, A2 a2) {
62    DCHECK(held_cb_.is_null());
63    if (hold_) {
64      held_cb_ = base::Bind(base::ResetAndReturn(&original_cb_),
65                            internal::TrampolineForward(a1),
66                            internal::TrampolineForward(a2));
67    } else {
68      base::ResetAndReturn(&original_cb_).Run(a1, a2);
69    }
70  }
71
72  // Releases and runs the held callback.
73  void RunHeldCallback() {
74    DCHECK(hold_);
75    DCHECK(!held_cb_.is_null());
76    hold_ = false;
77    base::ResetAndReturn(&held_cb_).Run();
78  }
79
80 private:
81  bool hold_;
82  CB original_cb_;
83  base::Closure held_cb_;
84};
85
86}  // namespace media
87
88#endif  // MEDIA_BASE_CALLBACK_HOLDER_H_
89