| /* | |
| * 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 <cstddef> | |
| #include "api/function_view.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/untyped_function.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; | |
| } else { | |
| // When send_in_progress_ we really only iterate the list and | |
| // mark elements with pending_removal_tag(). | |
| RTC_DCHECK(send_in_progress_); | |
| RTC_DCHECK_EQ(receivers_[first_todo].removal_tag, removal_tag); | |
| receivers_[first_todo++].removal_tag = pending_removal_tag(); | |
| } | |
| } | |
| if (!send_in_progress_) { | |
| // Discard the remove region. | |
| receivers_.resize(first_remove); | |
| } | |
| } | |
| void CallbackListReceivers::Foreach(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 |