webrtc/rtc_base/operations_chain.cc
Henrik Boström e574a31c50 [Perfect Negotiation] Fire onnegotiationneeded when chain is empty.
This CL generates "negotiationneeded" events if negotiation is needed
when the Operations Chain becomes empty. This is only implemented in
Unified Plan to avoid Plan B regressions (the event is pretty useless
in Plan B as it fires repeatedly).

In order to implement the spec-compliant behavior of only firing the
event when the chain is empty, this CL introduces
PeerConnectionObserver::OnNegotiationNeededEvent() and
PeerConnectionInterface::ShouldFireNegotiationNeededEvent() to allow
validating the event before firing it. This is needed because the event
must not be fired until a task has been posted and subsequently chained
operations could invalidate it in the meantime.

Test coverage is added for both legacy and modern "negotiationneeded"
events.

Bug: chromium:1060083
Change-Id: I1dbaa8f6ddb1c6e7c8abd8da3b92efcb64060383
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180620
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31989}
2020-08-25 09:56:39 +00:00

82 lines
2.6 KiB
C++

/*
* Copyright 2019 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/operations_chain.h"
#include "rtc_base/checks.h"
namespace rtc {
OperationsChain::CallbackHandle::CallbackHandle(
scoped_refptr<OperationsChain> operations_chain)
: operations_chain_(std::move(operations_chain)) {}
OperationsChain::CallbackHandle::~CallbackHandle() {
RTC_DCHECK(has_run_);
}
void OperationsChain::CallbackHandle::OnOperationComplete() {
RTC_DCHECK(!has_run_);
#ifdef RTC_DCHECK_IS_ON
has_run_ = true;
#endif // RTC_DCHECK_IS_ON
operations_chain_->OnOperationComplete();
// We have no reason to keep the |operations_chain_| alive through reference
// counting anymore.
operations_chain_ = nullptr;
}
// static
scoped_refptr<OperationsChain> OperationsChain::Create() {
return new OperationsChain();
}
OperationsChain::OperationsChain() : RefCountedObject() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
}
OperationsChain::~OperationsChain() {
// Operations keep the chain alive through reference counting so this should
// not be possible. The fact that the chain is empty makes it safe to
// destroy the OperationsChain on any sequence.
RTC_DCHECK(chained_operations_.empty());
}
void OperationsChain::SetOnChainEmptyCallback(
std::function<void()> on_chain_empty_callback) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
on_chain_empty_callback_ = std::move(on_chain_empty_callback);
}
bool OperationsChain::IsEmpty() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return chained_operations_.empty();
}
std::function<void()> OperationsChain::CreateOperationsChainCallback() {
return [handle = rtc::scoped_refptr<CallbackHandle>(
new CallbackHandle(this))]() { handle->OnOperationComplete(); };
}
void OperationsChain::OnOperationComplete() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// The front element is the operation that just completed, remove it.
RTC_DCHECK(!chained_operations_.empty());
chained_operations_.pop();
// If there are any other operations chained, execute the next one. Otherwise,
// invoke the "on chain empty" callback if it has been set.
if (!chained_operations_.empty()) {
chained_operations_.front()->Run();
} else if (on_chain_empty_callback_.has_value()) {
on_chain_empty_callback_.value()();
}
}
} // namespace rtc