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:

committed by
Commit Bot

parent
491fa44ed9
commit
c88fe70a8d
@ -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;
|
||||||
|
@ -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
|
||||||
|
12
api/proxy.h
12
api/proxy.h
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user