summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/rtc_base/callback_list.cc
blob: c452c79b3843f5447b47ebd2149f5ede72ddb78d (plain)
1
2
3
4
5
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/callback_list.h"

#include "rtc_base/checks.h"

namespace webrtc {
namespace callback_list_impl {

CallbackListReceivers::CallbackListReceivers() = default;

CallbackListReceivers::~CallbackListReceivers() {
  RTC_CHECK(!send_in_progress_);
}

void CallbackListReceivers::RemoveReceivers(const void* removal_tag) {
  RTC_DCHECK(removal_tag);

  // We divide the receivers_ vector into three regions: from right to left, the
  // "keep" region, the "todo" region, and the "remove" region. The "todo"
  // region initially covers the whole vector.
  size_t first_todo = 0;                    // First element of the "todo"
                                            // region.
  size_t first_remove = receivers_.size();  // First element of the "remove"
                                            // region.

  // Loop until the "todo" region is empty.
  while (first_todo != first_remove) {
    if (receivers_[first_todo].removal_tag != removal_tag) {
      // The first element of the "todo" region should be kept. Move the
      // "keep"/"todo" boundary.
      ++first_todo;
    } else if (receivers_[first_remove - 1].removal_tag == removal_tag) {
      // The last element of the "todo" region should be removed. Move the
      // "todo"/"remove" boundary.
      if (send_in_progress_) {
        // Tag this receiver for removal, which will be done when `ForEach`
        // has completed.
        receivers_[first_remove - 1].removal_tag = pending_removal_tag();
      }
      --first_remove;
    } else if (!send_in_progress_) {
      // The first element of the "todo" region should be removed, and the last
      // element of the "todo" region should be kept. Swap them, and then shrink
      // the "todo" region from both ends.
      RTC_DCHECK_NE(first_todo, first_remove - 1);
      using std::swap;
      swap(receivers_[first_todo], receivers_[first_remove - 1]);
      RTC_DCHECK_NE(receivers_[first_todo].removal_tag, removal_tag);
      ++first_todo;
      RTC_DCHECK_EQ(receivers_[first_remove - 1].removal_tag, removal_tag);
      --first_remove;
    }
  }

  if (!send_in_progress_) {
    // Discard the remove region.
    receivers_.resize(first_remove);
  }
}

void CallbackListReceivers::Foreach(
    rtc::FunctionView<void(UntypedFunction&)> fv) {
  RTC_CHECK(!send_in_progress_);
  bool removals_detected = false;
  send_in_progress_ = true;
  for (auto& r : receivers_) {
    RTC_DCHECK_NE(r.removal_tag, pending_removal_tag());
    fv(r.function);
    if (r.removal_tag == pending_removal_tag()) {
      removals_detected = true;
    }
  }
  send_in_progress_ = false;
  if (removals_detected) {
    RemoveReceivers(pending_removal_tag());
  }
}

template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<1>);
template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<2>);
template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<3>);
template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<4>);
template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::NontrivialUntypedFunctionArgs);
template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::FunctionPointerUntypedFunctionArgs);

template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<1>);
template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<2>);
template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<3>);
template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<4>);
template void CallbackListReceivers::AddReceiver(
    UntypedFunction::NontrivialUntypedFunctionArgs);
template void CallbackListReceivers::AddReceiver(
    UntypedFunction::FunctionPointerUntypedFunctionArgs);

}  // namespace callback_list_impl
}  // namespace webrtc