| /* | |
| * 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 "pc/connection_context.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "api/environment/environment.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/sequence_checker.h" | |
| #include "api/transport/sctp_transport_factory_interface.h" | |
| #include "media/base/media_engine.h" | |
| #include "media/sctp/sctp_transport_factory.h" | |
| #include "p2p/base/basic_packet_socket_factory.h" | |
| #include "pc/media_factory.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/internal/default_socket_server.h" | |
| #include "rtc_base/network.h" | |
| #include "rtc_base/socket_factory.h" | |
| #include "rtc_base/socket_server.h" | |
| #include "rtc_base/thread.h" | |
| namespace webrtc { | |
| namespace { | |
| Thread* MaybeStartNetworkThread( | |
| Thread* old_thread, | |
| std::unique_ptr<SocketFactory>& socket_factory_holder, | |
| std::unique_ptr<Thread>& thread_holder) { | |
| if (old_thread) { | |
| return old_thread; | |
| } | |
| std::unique_ptr<SocketServer> socket_server = CreateDefaultSocketServer(); | |
| thread_holder = std::make_unique<Thread>(socket_server.get()); | |
| socket_factory_holder = std::move(socket_server); | |
| thread_holder->SetName("pc_network_thread", nullptr); | |
| thread_holder->Start(); | |
| return thread_holder.get(); | |
| } | |
| Thread* MaybeWrapThread(Thread* signaling_thread, bool& wraps_current_thread) { | |
| wraps_current_thread = false; | |
| if (signaling_thread) { | |
| return signaling_thread; | |
| } | |
| auto this_thread = Thread::Current(); | |
| if (!this_thread) { | |
| // If this thread isn't already wrapped by an webrtc::Thread, create a | |
| // wrapper and own it in this class. | |
| this_thread = ThreadManager::Instance()->WrapCurrentThread(); | |
| wraps_current_thread = true; | |
| } | |
| return this_thread; | |
| } | |
| std::unique_ptr<SctpTransportFactoryInterface> MaybeCreateSctpFactory( | |
| std::unique_ptr<SctpTransportFactoryInterface> factory, | |
| Thread* network_thread) { | |
| if (factory) { | |
| return factory; | |
| } | |
| #ifdef WEBRTC_HAVE_SCTP | |
| return std::make_unique<SctpTransportFactory>(network_thread); | |
| #else | |
| return nullptr; | |
| #endif | |
| } | |
| } // namespace | |
| // Static | |
| scoped_refptr<ConnectionContext> ConnectionContext::Create( | |
| const Environment& env, | |
| PeerConnectionFactoryDependencies* dependencies) { | |
| return scoped_refptr<ConnectionContext>( | |
| new ConnectionContext(env, dependencies)); | |
| } | |
| // Access to the media engine operations is constrained to the worker thread. | |
| // This accessor via `MediaEngineReference` is provided to help ensure that a | |
| // reference is held and that the call is being issued on the worker thread. | |
| MediaEngineInterface* ConnectionContext::MediaEngineReference::media_engine() | |
| const { | |
| RTC_DCHECK_RUN_ON(c_->worker_thread()); | |
| RTC_DCHECK(c_->media_engine_w()); | |
| return c_->media_engine_w(); | |
| } | |
| ConnectionContext::ConnectionContext( | |
| const Environment& env, | |
| PeerConnectionFactoryDependencies* dependencies) | |
| : is_configured_for_media_(dependencies->media_factory != nullptr), | |
| network_thread_(MaybeStartNetworkThread(dependencies->network_thread, | |
| owned_socket_factory_, | |
| owned_network_thread_)), | |
| worker_thread_(dependencies->worker_thread, | |
| []() { | |
| auto thread_holder = Thread::Create(); | |
| thread_holder->SetName("pc_worker_thread", nullptr); | |
| thread_holder->Start(); | |
| return thread_holder; | |
| }), | |
| signaling_thread_(MaybeWrapThread(dependencies->signaling_thread, | |
| wraps_current_thread_)), | |
| media_engine_( | |
| is_configured_for_media_ | |
| ? dependencies->media_factory->CreateMediaEngine(env, | |
| *dependencies) | |
| : nullptr), | |
| network_monitor_factory_( | |
| std::move(dependencies->network_monitor_factory)), | |
| default_network_manager_(std::move(dependencies->network_manager)), | |
| call_factory_(std::move(dependencies->media_factory)), | |
| default_socket_factory_(std::move(dependencies->packet_socket_factory)), | |
| sctp_factory_( | |
| MaybeCreateSctpFactory(std::move(dependencies->sctp_factory), | |
| network_thread())), | |
| use_rtx_(true) { | |
| RTC_DCHECK_RUN_ON(signaling_thread_); | |
| RTC_DCHECK(!(default_network_manager_ && network_monitor_factory_)) | |
| << "You can't set both network_manager and network_monitor_factory."; | |
| signaling_thread_->AllowInvokesToThread(worker_thread()); | |
| signaling_thread_->AllowInvokesToThread(network_thread_); | |
| worker_thread_->AllowInvokesToThread(network_thread_); | |
| if (!network_thread_->IsCurrent()) { | |
| // network_thread_->IsCurrent() == true means signaling_thread_ is | |
| // network_thread_. In this case, no further action is required as | |
| // signaling_thread_ can already invoke network_thread_. | |
| network_thread_->PostTask( | |
| [thread = network_thread_, worker_thread = worker_thread_.get()] { | |
| thread->DisallowBlockingCalls(); | |
| thread->DisallowAllInvokes(); | |
| if (worker_thread == thread) { | |
| // In this case, worker_thread_ == network_thread_ | |
| thread->AllowInvokesToThread(thread); | |
| } | |
| }); | |
| } | |
| SocketFactory* socket_factory = dependencies->socket_factory; | |
| if (socket_factory == nullptr) { | |
| if (owned_socket_factory_) { | |
| socket_factory = owned_socket_factory_.get(); | |
| } else { | |
| // TODO(bugs.webrtc.org/13145): This case should be deleted. Either | |
| // require that a PacketSocketFactory and NetworkManager always are | |
| // injected (with no need to construct these default objects), or require | |
| // that if a network_thread is injected, an approprite | |
| // webrtc::SocketServer should be injected too. | |
| socket_factory = network_thread()->socketserver(); | |
| } | |
| } | |
| if (!default_network_manager_) { | |
| // If network_monitor_factory_ is non-null, it will be used to create a | |
| // network monitor while on the network thread. | |
| default_network_manager_ = std::make_unique<BasicNetworkManager>( | |
| env, socket_factory, network_monitor_factory_.get()); | |
| } | |
| if (!default_socket_factory_) { | |
| default_socket_factory_ = | |
| std::make_unique<BasicPacketSocketFactory>(socket_factory); | |
| } | |
| // Set warning levels on the threads, to give warnings when response | |
| // may be slower than is expected of the thread. | |
| // Since some of the threads may be the same, start with the least | |
| // restrictive limits and end with the least permissive ones. | |
| // This will give warnings for all cases. | |
| signaling_thread_->SetDispatchWarningMs(100); | |
| worker_thread_->SetDispatchWarningMs(30); | |
| network_thread_->SetDispatchWarningMs(10); | |
| blocking_media_engine_destruction_ = | |
| env.field_trials().IsEnabled("WebRTC-SynchronousDestructors"); | |
| } | |
| ConnectionContext::~ConnectionContext() { | |
| RTC_DCHECK_RUN_ON(signaling_thread_); | |
| // Now that the `media_engine_reference_count_` will be 0 when we get here, | |
| // the blocking terminate operation that previously ran as part of the | |
| // destructor of the media engine, has already run and there's not | |
| // a need any longer to do the blocking call behind the | |
| // `blocking_media_engine_destruction_` flag. | |
| RTC_DCHECK_EQ(media_engine_reference_count_, 0); | |
| // `media_engine_` requires destruction to happen on the worker thread. | |
| if (blocking_media_engine_destruction_) { | |
| // The media engine shares its Environment with objects that may outlive | |
| // the ConnectionContext if this call is not blocking. If Environment is | |
| // destroyed when ConnectionContext's destruction completes, this may | |
| // cause Use-After-Free. | |
| // | |
| // The plan is to address the problem with a new Terminate(callback) method, | |
| // which is referenced in webrtc:443588673, but pending this one can | |
| // control this with field trial `WebRTC-SynchronousDestructors`. | |
| worker_thread_->BlockingCall([&] { media_engine_ = nullptr; }); | |
| } else { | |
| worker_thread_->PostTask([media_engine = std::move(media_engine_)] {}); | |
| } | |
| // Make sure `worker_thread()` and `signaling_thread()` outlive | |
| // `default_socket_factory_` and `default_network_manager_`. | |
| default_socket_factory_ = nullptr; | |
| default_network_manager_ = nullptr; | |
| if (wraps_current_thread_) | |
| ThreadManager::Instance()->UnwrapCurrentThread(); | |
| } | |
| MediaEngineInterface* ConnectionContext::media_engine_w() { | |
| RTC_DCHECK_RUN_ON(worker_thread()); | |
| return media_engine_.get(); | |
| } | |
| void ConnectionContext::AddRefMediaEngine() { | |
| RTC_DCHECK_RUN_ON(worker_thread()); | |
| RTC_DCHECK_GE(media_engine_reference_count_, 0); | |
| RTC_DCHECK(media_engine_); | |
| ++media_engine_reference_count_; | |
| if (media_engine_reference_count_ == 1) { | |
| media_engine_->Init(); | |
| } | |
| } | |
| void ConnectionContext::ReleaseMediaEngine() { | |
| RTC_DCHECK_RUN_ON(worker_thread()); | |
| RTC_DCHECK_GT(media_engine_reference_count_, 0); | |
| RTC_DCHECK(media_engine_); | |
| --media_engine_reference_count_; | |
| if (media_engine_reference_count_ == 0) { | |
| media_engine_->Terminate(); | |
| } | |
| } | |
| } // namespace webrtc |