Make Android/iOS local/remote description accessors thread safe.

Since the descriptions can be modified on the signaling thread,
ToString can only be safely called on that thread.

Bug: webrtc:11791
Change-Id: Icf6aada8aa66d00be94c6bda7b22e41b5d3bbc17
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180541
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Anders Carlsson <andersc@webrtc.org>
Commit-Queue: Taylor <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31862}
This commit is contained in:
Taylor Brandstetter
2020-08-03 16:36:16 -07:00
committed by Commit Bot
parent 491fa44ed9
commit c88fe70a8d
12 changed files with 87 additions and 34 deletions

View File

@ -909,6 +909,10 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
const std::string& label, const std::string& label,
const DataChannelInit* config) = 0; const DataChannelInit* config) = 0;
// NOTE: For the following 6 methods, it's only safe to dereference the
// SessionDescriptionInterface on signaling_thread() (for example, calling
// ToString).
// Returns the more recently applied description; "pending" if it exists, and // Returns the more recently applied description; "pending" if it exists, and
// otherwise "current". See below. // otherwise "current". See below.
virtual const SessionDescriptionInterface* local_description() const = 0; virtual const SessionDescriptionInterface* local_description() const = 0;
@ -1136,6 +1140,14 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
// thus the observer object can be safely destroyed. // thus the observer object can be safely destroyed.
virtual void Close() = 0; virtual void Close() = 0;
// The thread on which all PeerConnectionObserver callbacks will be invoked,
// as well as callbacks for other classes such as DataChannelObserver.
//
// Also the only thread on which it's safe to use SessionDescriptionInterface
// pointers.
// TODO(deadbeef): Make pure virtual when all subclasses implement it.
virtual rtc::Thread* signaling_thread() const { return nullptr; }
protected: protected:
// Dtor protected as objects shouldn't be deleted via this interface. // Dtor protected as objects shouldn't be deleted via this interface.
~PeerConnectionInterface() override = default; ~PeerConnectionInterface() override = default;

View File

@ -147,6 +147,7 @@ PROXY_METHOD2(bool,
PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr<RtcEventLogOutput>) PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr<RtcEventLogOutput>)
PROXY_METHOD0(void, StopRtcEventLog) PROXY_METHOD0(void, StopRtcEventLog)
PROXY_METHOD0(void, Close) PROXY_METHOD0(void, Close)
BYPASS_PROXY_CONSTMETHOD0(rtc::Thread*, signaling_thread)
END_PROXY_MAP() END_PROXY_MAP()
} // namespace webrtc } // namespace webrtc

View File

@ -400,11 +400,13 @@ class ConstMethodCall : public rtc::Message, public rtc::MessageHandler {
// For use when returning purely const state (set during construction). // For use when returning purely const state (set during construction).
// Use with caution. This method should only be used when the return value will // Use with caution. This method should only be used when the return value will
// always be the same. // always be the same.
#define BYPASS_PROXY_CONSTMETHOD0(r, method) \ #define BYPASS_PROXY_CONSTMETHOD0(r, method) \
r method() const override { \ r method() const override { \
static_assert(!std::is_pointer<r>::value, "Type is a pointer"); \ static_assert( \
static_assert(!std::is_reference<r>::value, "Type is a reference"); \ std::is_same<r, rtc::Thread*>::value || !std::is_pointer<r>::value, \
return c_->method(); \ "Type is a pointer"); \
static_assert(!std::is_reference<r>::value, "Type is a reference"); \
return c_->method(); \
} }
} // namespace webrtc } // namespace webrtc

View File

@ -237,7 +237,11 @@ class DummyPeerConnection : public PeerConnectionInterface {
void StopRtcEventLog() { FATAL() << "Not implemented"; } void StopRtcEventLog() { FATAL() << "Not implemented"; }
void Close() {} void Close() override {}
rtc::Thread* signaling_thread() const override {
return rtc::Thread::Current();
}
}; };
static_assert( static_assert(

View File

@ -270,6 +270,7 @@ rtc_library("peerconnection") {
"../rtc_base:weak_ptr", "../rtc_base:weak_ptr",
"../rtc_base/experiments:field_trial_parser", "../rtc_base/experiments:field_trial_parser",
"../rtc_base/synchronization:mutex", "../rtc_base/synchronization:mutex",
"../rtc_base/synchronization:sequence_checker",
"../rtc_base/system:file_wrapper", "../rtc_base/system:file_wrapper",
"../rtc_base/system:rtc_export", "../rtc_base/system:rtc_export",
"../rtc_base/third_party/base64", "../rtc_base/third_party/base64",

View File

@ -260,14 +260,15 @@ class PeerConnection : public PeerConnectionInternal,
void Close() override; void Close() override;
rtc::Thread* signaling_thread() const final {
return factory_->signaling_thread();
}
// PeerConnectionInternal implementation. // PeerConnectionInternal implementation.
rtc::Thread* network_thread() const final { rtc::Thread* network_thread() const final {
return factory_->network_thread(); return factory_->network_thread();
} }
rtc::Thread* worker_thread() const final { return factory_->worker_thread(); } rtc::Thread* worker_thread() const final { return factory_->worker_thread(); }
rtc::Thread* signaling_thread() const final {
return factory_->signaling_thread();
}
std::string session_id() const override { std::string session_id() const override {
RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK_RUN_ON(signaling_thread());

View File

@ -30,7 +30,6 @@ class PeerConnectionInternal : public PeerConnectionInterface {
public: public:
virtual rtc::Thread* network_thread() const = 0; virtual rtc::Thread* network_thread() const = 0;
virtual rtc::Thread* worker_thread() const = 0; virtual rtc::Thread* worker_thread() const = 0;
virtual rtc::Thread* signaling_thread() const = 0;
// The SDP session ID as defined by RFC 3264. // The SDP session ID as defined by RFC 3264.
virtual std::string session_id() const = 0; virtual std::string session_id() const = 0;

View File

@ -478,17 +478,39 @@ static jlong JNI_PeerConnection_GetNativePeerConnection(
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetLocalDescription( static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetLocalDescription(
JNIEnv* jni, JNIEnv* jni,
const JavaParamRef<jobject>& j_pc) { const JavaParamRef<jobject>& j_pc) {
const SessionDescriptionInterface* sdp = PeerConnectionInterface* pc = ExtractNativePC(jni, j_pc);
ExtractNativePC(jni, j_pc)->local_description(); // It's only safe to operate on SessionDescriptionInterface on the
return sdp ? NativeToJavaSessionDescription(jni, sdp) : nullptr; // signaling thread, but |jni| may only be used on the current thread, so we
// must do this odd dance.
std::string sdp;
std::string type;
pc->signaling_thread()->Invoke<void>(RTC_FROM_HERE, [pc, &sdp, &type] {
const SessionDescriptionInterface* desc = pc->local_description();
if (desc) {
RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
type = desc->type();
}
});
return sdp.empty() ? nullptr : NativeToJavaSessionDescription(jni, sdp, type);
} }
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetRemoteDescription( static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetRemoteDescription(
JNIEnv* jni, JNIEnv* jni,
const JavaParamRef<jobject>& j_pc) { const JavaParamRef<jobject>& j_pc) {
const SessionDescriptionInterface* sdp = PeerConnectionInterface* pc = ExtractNativePC(jni, j_pc);
ExtractNativePC(jni, j_pc)->remote_description(); // It's only safe to operate on SessionDescriptionInterface on the
return sdp ? NativeToJavaSessionDescription(jni, sdp) : nullptr; // signaling thread, but |jni| may only be used on the current thread, so we
// must do this odd dance.
std::string sdp;
std::string type;
pc->signaling_thread()->Invoke<void>(RTC_FROM_HERE, [pc, &sdp, &type] {
const SessionDescriptionInterface* desc = pc->remote_description();
if (desc) {
RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
type = desc->type();
}
});
return sdp.empty() ? nullptr : NativeToJavaSessionDescription(jni, sdp, type);
} }
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetCertificate( static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetCertificate(

View File

@ -31,8 +31,11 @@ CreateSdpObserverJni::~CreateSdpObserverJni() = default;
void CreateSdpObserverJni::OnSuccess(SessionDescriptionInterface* desc) { void CreateSdpObserverJni::OnSuccess(SessionDescriptionInterface* desc) {
JNIEnv* env = AttachCurrentThreadIfNeeded(); JNIEnv* env = AttachCurrentThreadIfNeeded();
Java_SdpObserver_onCreateSuccess(env, j_observer_global_, std::string sdp;
NativeToJavaSessionDescription(env, desc)); RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
Java_SdpObserver_onCreateSuccess(
env, j_observer_global_,
NativeToJavaSessionDescription(env, sdp, desc->type()));
// OnSuccess transfers ownership of the description (there's a TODO to make // OnSuccess transfers ownership of the description (there's a TODO to make
// it use unique_ptr...). // it use unique_ptr...).
delete desc; delete desc;

View File

@ -37,12 +37,10 @@ std::unique_ptr<SessionDescriptionInterface> JavaToNativeSessionDescription(
ScopedJavaLocalRef<jobject> NativeToJavaSessionDescription( ScopedJavaLocalRef<jobject> NativeToJavaSessionDescription(
JNIEnv* jni, JNIEnv* jni,
const SessionDescriptionInterface* desc) { const std::string& sdp,
std::string sdp; const std::string& type) {
RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
return Java_SessionDescription_Constructor( return Java_SessionDescription_Constructor(
jni, jni, Java_Type_fromCanonicalForm(jni, NativeToJavaString(jni, type)),
Java_Type_fromCanonicalForm(jni, NativeToJavaString(jni, desc->type())),
NativeToJavaString(jni, sdp)); NativeToJavaString(jni, sdp));
} }

View File

@ -13,6 +13,7 @@
#include <jni.h> #include <jni.h>
#include <memory> #include <memory>
#include <string>
#include "api/jsep.h" #include "api/jsep.h"
#include "sdk/android/native_api/jni/scoped_java_ref.h" #include "sdk/android/native_api/jni/scoped_java_ref.h"
@ -26,7 +27,8 @@ std::unique_ptr<SessionDescriptionInterface> JavaToNativeSessionDescription(
ScopedJavaLocalRef<jobject> NativeToJavaSessionDescription( ScopedJavaLocalRef<jobject> NativeToJavaSessionDescription(
JNIEnv* jni, JNIEnv* jni,
const SessionDescriptionInterface* desc); const std::string& sdp,
const std::string& type);
} // namespace jni } // namespace jni
} // namespace webrtc } // namespace webrtc

View File

@ -358,19 +358,27 @@ void PeerConnectionDelegateAdapter::OnRemoveTrack(
} }
- (RTC_OBJC_TYPE(RTCSessionDescription) *)localDescription { - (RTC_OBJC_TYPE(RTCSessionDescription) *)localDescription {
const webrtc::SessionDescriptionInterface *description = // It's only safe to operate on SessionDescriptionInterface on the signaling thread.
_peerConnection->local_description(); return _peerConnection->signaling_thread()->Invoke<RTC_OBJC_TYPE(RTCSessionDescription) *>(
return description ? RTC_FROM_HERE, [self] {
[[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : const webrtc::SessionDescriptionInterface *description =
nil; _peerConnection->local_description();
return description ?
[[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] :
nil;
});
} }
- (RTC_OBJC_TYPE(RTCSessionDescription) *)remoteDescription { - (RTC_OBJC_TYPE(RTCSessionDescription) *)remoteDescription {
const webrtc::SessionDescriptionInterface *description = // It's only safe to operate on SessionDescriptionInterface on the signaling thread.
_peerConnection->remote_description(); return _peerConnection->signaling_thread()->Invoke<RTC_OBJC_TYPE(RTCSessionDescription) *>(
return description ? RTC_FROM_HERE, [self] {
[[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : const webrtc::SessionDescriptionInterface *description =
nil; _peerConnection->remote_description();
return description ?
[[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] :
nil;
});
} }
- (RTCSignalingState)signalingState { - (RTCSignalingState)signalingState {