WebRTC 소스 분석 - 스레드 보안 Proxy로 스레드 무단 침입 방지
46764 단어 WebRTC 소스 분석
선언
앞의 글에서 WebRTC 원본 분석 - 호출 구축 과정의 2(PeerConnectionFactory 창설)가 PeerConnectionFactory 대상의 창설을 분석하는 과정에서 마지막으로 사용자층에 돌아가는 것은 PeerConnectionFactory 대상이 아니라 그 대리 대상인 PeerConnectionFactory Proxy라는 것을 깨달았다.만약에 원본 코드를 깊이 보지 않으면 응용층은 감지되지 않고 PeerConnectionFactory 실체 대상을 직접 조작하는 줄 알았다. 응용층의 지침은 PeerConnectionFactoryInterface이기 때문에 일반적으로 첫 번째 감각은 대응하는 Implement 대상을 가리킨다.
왜 이렇게 디자인했을까요?우리는 WebRTC가 다중 스레드 프로그램이라는 것을 알고 있다. 가장 직관적인 느낌은 틀림없이 세 개의 기초 스레드signaling이 있을 것이다thread, workerthread(일꾼),networkthread(네트워크).그리고 또 하나의 중요한 점은 WebRTC 내부의 많은 대상의 방법이 반드시 지정된 라인에서 실행되어야 한다는 것이다. 예를 들어 이전에 분석한 PeerConnectionFactory의 모든 구성원의 방법은signalingthread (명령) 에서 실행되고, 모든 방법의 시작 위치는 다음 코드와 같이 단언이 있습니다.만약 방법이 대응하는 라인에서 실행되지 않았다면, 프로그램이 여기까지 오면 반드시 트림을 할 것이다.rtc::scoped_refptr<AudioSourceInterface>
PeerConnectionFactory::CreateAudioSource(const cricket::AudioOptions& options) {
RTC_DCHECK(signaling_thread_->IsCurrent());
rtc::scoped_refptr<LocalAudioSource> source(
LocalAudioSource::Create(&options));
return source;
}
사용자의 입장에서 볼 때 만약에 직접적으로 이러한 인터페이스를 향한다면, 우리는 반드시 모든 인터페이스의 구체적인 실현을 이해하고, 이러한 방법을 사용할 때 어느 라인에서 실행하도록 해야 한다. 그렇지 않으면 어디를 가든지 붕괴될 것이다.이렇게 디자인된 라이브러리에 직면하여 프로그램이 붕괴되지 않아도 사용자는 붕괴될 것이다. 하물며 WebRTC의api층이 대외적으로 노출된 것은 모두 Interface인데 응용층이 실제 가지고 있는 것이 도대체 어느 Implement류의 대상인지 어떻게 알 수 있겠는가?더군다나 응용층은 모든 라인의 존재를 알지 못한다. 왜냐하면 example/peerconnectionclient 예시 프로젝트처럼 응용층은 위의 세 개의 기초 라인을 만들지 않고 모두 WebRTC 내부에서 만든 것이다.
따라서 사용자가 WebRTC를 사용할 때 근심 걱정 없이 어디서나 임의로 호출할 수 있도록 Proxy 에이전트층은 합리적이고 교묘하게 탄생했다. 크롬도 같은 사상, 같은 기술을 사용했다고 한다.Proxy 프록시층은 사용자가 상대방의 어떤 방법을 호출하고 싶을 때 (실제로는 대응하는 Proxy 대상을 조작함), 방법은 기대하는 대상에게 프록시된 기대를 호출하고 적절한 라인에서 동기화됩니다.
다음은 Proxy 에이전트가 어떻게 이루어졌는지, 상술한 문제를 어떻게 해결했는지 살펴보겠습니다.
1 두려운 매크로 정의
WebRTC 원본 분석 - 호출 구축 과정의 2(PeerConnectionFactory 창설) 글에서 PeerConnectionFactory Proxy는 매크로 정의를 통해 만들어진 것이라고 언급했는데 우리는 PeerConnectionFactory 에이전트의 생성 과정을 입구점으로 삼아 한 걸음 한 걸음 분석했다.우선, PeerConnectionFactory 에이전트에서api\peerconnection_factory_proxy.h 파일에서 이 파일은 PeerConnectionFactory 프록시 클래스를 생성하기 위해 존재합니다.보기에는 매우 복잡하고 매크로의 종류가 매우 많으며 간략하게 보면 사실 이 안의 매크로 정의는 4가지일 뿐이다.
rtc::scoped_refptr<AudioSourceInterface>
PeerConnectionFactory::CreateAudioSource(const cricket::AudioOptions& options) {
RTC_DCHECK(signaling_thread_->IsCurrent());
rtc::scoped_refptr<LocalAudioSource> source(
LocalAudioSource::Create(&options));
return source;
}
WebRTC 원본 분석 - 호출 구축 과정의 2(PeerConnectionFactory 창설) 글에서 PeerConnectionFactory Proxy는 매크로 정의를 통해 만들어진 것이라고 언급했는데 우리는 PeerConnectionFactory 에이전트의 생성 과정을 입구점으로 삼아 한 걸음 한 걸음 분석했다.우선, PeerConnectionFactory 에이전트에서api\peerconnection_factory_proxy.h 파일에서 이 파일은 PeerConnectionFactory 프록시 클래스를 생성하기 위해 존재합니다.보기에는 매우 복잡하고 매크로의 종류가 매우 많으며 간략하게 보면 사실 이 안의 매크로 정의는 4가지일 뿐이다.
namespace webrtc {
BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)
PROXY_SIGNALING_THREAD_DESTRUCTOR()
PROXY_METHOD1(void, SetOptions, const Options&)
PROXY_METHOD4(rtc::scoped_refptr<PeerConnectionInterface>,
CreatePeerConnection,
const PeerConnectionInterface::RTCConfiguration&,
std::unique_ptr<cricket::PortAllocator>,
std::unique_ptr<rtc::RTCCertificateGeneratorInterface>,
PeerConnectionObserver*);
PROXY_METHOD2(rtc::scoped_refptr<PeerConnectionInterface>,
CreatePeerConnection,
const PeerConnectionInterface::RTCConfiguration&,
PeerConnectionDependencies);
PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
GetRtpSenderCapabilities,
cricket::MediaType);
PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
GetRtpReceiverCapabilities,
cricket::MediaType);
PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,
CreateLocalMediaStream,
const std::string&)
PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,
CreateAudioSource,
const cricket::AudioOptions&)
PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,
CreateVideoTrack,
const std::string&,
VideoTrackSourceInterface*)
PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,
CreateAudioTrack,
const std::string&,
AudioSourceInterface*)
PROXY_METHOD2(bool, StartAecDump, rtc::PlatformFile, int64_t)
PROXY_METHOD0(void, StopAecDump)
END_PROXY_MAP()
} // namespace webrtc
위의 간략한 분석은 사실상 완전히 정확하지는 않지만 이해에 도움이 된다.상세한 상황을 이해하려면, 반드시 이러한 매크로 정의를 한층 더 전개해야만 끝까지 탐색할 수 있다.이 매크로 정의들은api/proxy에 있습니다.h의 파일에서
2 api/proxy.h
다음은 상술한 네 개의 매크로에 대한 비교적 번거로운 분할 분석으로 비교적 무미건조하다. 마치 Puzzle를 들고 천천히 맞붙어 최종적으로 전모를 엿보는 것과 같다.
2.1 BEGIN_SIGNALING_PROXY_MAP
#define BEGIN_SIGNALING_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
SIGNALING_PROXY_MAP_BOILERPLATE(c) \
REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
public: \
static rtc::scoped_refptr Create( \
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) { \
return new rtc::RefCountedObject(signaling_thread, \
c); \
}
이 매크로는 다른 3개의 매크로 + 하나의Create 방법으로 해석할 수 있으며, PeerConnectionFactory를 대입하면 public:
static rtc::scoped_refptr<PeerConnectionFactoryProxyWithInternal> Create(
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxyWithInternal>(signaling_thread, c);
}
프록시 이름이 PeerConnection Factory Proxy With Internal인 것 같아서 신기하지 않으세요?우리 계속
2.1.1 PROXY_MAP_BOILERPLATE
#define PROXY_MAP_BOILERPLATE(c) \
template \
class c##ProxyWithInternal; \
typedef c##ProxyWithInternal c##Proxy; \
template \
class c##ProxyWithInternal : public c##Interface { \
protected: \
typedef c##Interface C; \
\
public: \
const INTERNAL_CLASS* internal() const { return c_; } \
INTERNAL_CLASS* internal() { return c_; }
PeerConnectionFactory 대입: template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal;
typedef PeerConnectionFactoryProxyWithInternal<PeerConnectionFactoryInterface> PeerConnectionFactoryProxy;
template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal : public PeerConnectionFactoryInterface {
protected:
typedef PeerConnectionFactoryInterface C;
public:
const INTERNAL_CLASS* internal() const { return c_; }
INTERNAL_CLASS* internal() { return c_; }
2.1.2 SIGNALING_PROXY_MAP_BOILERPLATE
#define SIGNALING_PROXY_MAP_BOILERPLATE(c) \
protected: \
c##ProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c) \
: signaling_thread_(signaling_thread), c_(c) {} \
\
private: \
mutable rtc::Thread* signaling_thread_;
PeerConnectionFactory 대입: protected:
PeerConnectionFactoryProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c)
: signaling_thread_(signaling_thread), c_(c) {}
private:
mutable rtc::Thread* signaling_thread_;
2.1.3 REFCOUNTED_PROXY_MAP_BOILERPLATE
#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
protected: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { c_ = nullptr; } \
rtc::scoped_refptr c_;
PeerConnectionFactory 대입: protected:
~PeerConnectionFactoryProxyWithInternal() {
MethodCall0<PeerConnectionFactoryProxyWithInternal, void> call(
this, &PeerConnectionFactoryProxyWithInternal::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::scoped_refptr<INTERNAL_CLASS> c_;
2.2 PROXY_SIGNALING_THREAD_DESTRUCTOR
#define PROXY_SIGNALING_THREAD_DESTRUCTOR() \
private: \
rtc::Thread* destructor_thread() const { return signaling_thread_; } \
\
public: // NOLINTNEXTLINE
2.3 PROXY_METHOD
PROXY 정보METHOD 매크로 시리즈는 하나의 예시만 들 예정이며 다른 것은 기본적으로 같아서 하나하나 분석할 필요가 없다.#define PROXY_METHOD1(r, method, t1) \
r method(t1 a1) override { \
MethodCall1 call(c_, &C::method, std::move(a1)); \
return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
PeerConnectionFactory의 const Options & options () const {return options;예를 들어, 매크로 정의PROXY_METHOD1(void, SetOptions, const Options&)
다음으로 확장: void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , a1> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
2.4 END_PROXY_MAP()
#define END_PROXY_MAP() \
};
3 퍼즐
3.1 템플릿 클래스 PeerConnectionFactoryProxyWithInternal
위의 분할이 완료되었으므로 다음 단계에서는 위의 퍼즐의 각 부분을 조합하여 어떤 클래스가 생성되는지 살펴봅니다.template <class INTERNAL_CLASS> class PeerConnectionFactoryProxyWithInternal;
typedef PeerConnectionFactoryProxyWithInternal<PeerConnectionFactoryInterface> PeerConnectionFactoryProxy;
template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal : public PeerConnectionFactoryInterface {
protected:
typedef PeerConnectionFactoryInterface C;
public:
const INTERNAL_CLASS* internal() const { return c_; }
INTERNAL_CLASS* internal() { return c_; }
protected:
PeerConnectionFactoryProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c)
: signaling_thread_(signaling_thread), c_(c) {}
private:
mutable rtc::Thread* signaling_thread_;
protected:
~PeerConnectionFactoryProxyWithInternal() {
MethodCall0<PeerConnectionFactoryProxyWithInternal, void> call(
this, &PeerConnectionFactoryProxyWithInternal::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::scoped_refptr<INTERNAL_CLASS> c_;
public:
static rtc::scoped_refptr<PeerConnectionFactoryProxyWithInternal> Create(
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxyWithInternal>(signaling_thread, c);
}
private:
rtc::Thread* destructor_thread() const { return signaling_thread_; }
public: // NOLINTNEXTLINE
void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , const Options&> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
};
위에서 설명한 코드에서 알 수 있듯이template PeerConnectionFactoryProxyWithInternal 템플릿 클래스, 이 클래스는PeerConnectionFactoryInterface에 계승됩니다.
3.2 템플릿 클래스 인스턴스화 PeerConnectionFactoryProxy
또한 PeerConnectionFactoryProxy를 PeerConnectionFactoryProxyWithInternal로 정의했다.이 템플릿의 실례화입니다. 우리는 이 템플릿의 실례화된 클래스를 직접 보고 조금 정리하면 더욱 뚜렷해질 것입니다. 최종적으로 우리는 이런 클래스를 보게 될 것입니다. (물론 에이전트가 필요한 공유 방법이 너무 많기 때문에 SetOptions 방법만 예로 삼아 그의 방법을 생략합니다).class PeerConnectionFactoryProxy : public PeerConnectionFactoryInterface {
public:
static rtc::scoped_refptr<PeerConnectionFactoryProxy> Create(
rtc::Thread* signaling_thread, PeerConnectionFactoryInterface* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxy>(signaling_thread, c);
}
void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , const Options&> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
protected:
PeerConnectionFactoryProxy(rtc::Thread* signaling_thread, PeerConnectionFactoryInterface* c)
: signaling_thread_(signaling_thread), c_(c) {}
~PeerConnectionFactoryProxy() {
MethodCall0<PeerConnectionFactoryProxy, void> call(
this, &PeerConnectionFactoryProxy::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::Thread* destructor_thread() const { return signaling_thread_; }
private:
rtc::scoped_refptr<PeerConnectionFactoryInterface> c_;
mutable rtc::Thread* signaling_thread_;
};
이번에는 비교적 이해하기 쉽다.PeerConnectionFactoryProxy도 PeerConnectionFactoryInterface에 계승되어 가상을 만들 수 있다. 응용층에서 우리가 가지고 있는 것은 PeerConnectionFactory 실체 대상이지만 사실은 그렇지 않다. Create 방법은 PeerConnectionFactoryProxy 대상을 만들고 이 대상 내부에 PeerConnectionFactoryInterface 클래스를 가진 구성원(실체 대상 PeerConnectionFactory)과 목표 스레드 대상signalingthread_. PeerConnectionFactoryProxy를 실행하는 다른 공유 방법, 예를 들어 SetOptions()를 실행하면 내부적으로 PeerConnectionFactory의 SetOptions() 방법을 실행하고 이 방법은 목표 라인signalingthread_운행의 구체적인 원리는 WebRTC 원본 분석 - 스레드 기초의 크로스 스레드 동기화 Method Call을 보십시오.
이렇게 기본적인 원리는 논술이 끝났다.
4 더 나아가다
PeerConnectionFactory의 모든 방법은 명령 라인에서 실행해야 하기 때문에 상대적으로 간단하다.그러나 우리는 이런 상황을 만날 수 있다. 어떤 방법은 명령 라인에서 실행되어야 하고, 어떤 방법은 작업 라인에서 실행되어야 한다.이럴 땐 어떻게 해야 하나?
마찬가지로 api/proxy 클래스에서 다른 두 개의 매크로가 결합되어 이 기능을 제공합니다.
BEGIN_PROXY_MAP는 명령 스레드와 작업 스레드를 동시에 전달할 수 있음 #define BEGIN_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
WORKER_PROXY_MAP_BOILERPLATE(c) \
REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
public: \
static rtc::scoped_refptr Create( \
rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
INTERNAL_CLASS* c) { \
return new rtc::RefCountedObject(signaling_thread, \
worker_thread, c); \
}
PROXY_WORKER_METHOD0은 작업자 스레드에서 메소드를 실행하도록 보장 #define PROXY_WORKER_METHOD0(r, method) \
r method() override { \
MethodCall0 call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
끝났나요?아직 끝나지 않았어요. 그리고 또 다른 매크로도 있어요. 그다지 중요하지 않지만 WebRTC에서 보편적인 사실을 폭로했어요. owned라는 단어는 WebRTC에서 여러 번 등장하는 변수 이름에서 그 대상은 제가 가지고 있고 그 생명주기는 제가 컨트롤하고 있기 때문에 대상의 소각은 제가 할게요.
이 매크로는 다음과 같습니다.#define OWNED_PROXY_MAP_BOILERPLATE(c) \
public: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { delete c_; } \
INTERNAL_CLASS* c_;
이에 대응하는 것은:#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
protected: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { c_ = nullptr; } \
rtc::scoped_refptr c_;
흥미로운 점은 내부 멤버 c의 범주 및 제거 방법: owned 방식, 멤버는 INTERNALCLASS* c_,삭제 방법은 delete c;객체를 직접 삭제합니다. refcounted 방식, 구성원은scopedrefptr c_,제거 방법은 c =nullptr;인용을 해제하다.
총결산
본고는WebRTC에서 라인이 난입하는 것을 방지하기 위해 하는 일:Proxy 프록시 대상을 통해 모든 조작 프록시를 대응하는 라인에 대응하는 방법을 실행한다. PeerConnectionFactory의 공유 방법이 모두 신호 라인에서 실행되어야 할 때 어떻게 대리되는지 상세하게 설명했다. 한층 더 나아가 모든 종류의 방법이 일부분은 신호 라인에서 집행해야 하고 일부분은 작업자 라인에서 집행해야 한다. 이런 상황은 어떻게 대리되는지. 미결 문제: 네트워크 스레드에서 실행해야 하는 몇 가지 방법이 있습니까?이때 WebRTC는 어떻게 했습니까?명령 라인을 직접 복용하는 것이 실현됩니까?
#define BEGIN_SIGNALING_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
SIGNALING_PROXY_MAP_BOILERPLATE(c) \
REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
public: \
static rtc::scoped_refptr Create( \
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) { \
return new rtc::RefCountedObject(signaling_thread, \
c); \
}
public:
static rtc::scoped_refptr<PeerConnectionFactoryProxyWithInternal> Create(
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxyWithInternal>(signaling_thread, c);
}
#define PROXY_MAP_BOILERPLATE(c) \
template \
class c##ProxyWithInternal; \
typedef c##ProxyWithInternal c##Proxy; \
template \
class c##ProxyWithInternal : public c##Interface { \
protected: \
typedef c##Interface C; \
\
public: \
const INTERNAL_CLASS* internal() const { return c_; } \
INTERNAL_CLASS* internal() { return c_; }
template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal;
typedef PeerConnectionFactoryProxyWithInternal<PeerConnectionFactoryInterface> PeerConnectionFactoryProxy;
template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal : public PeerConnectionFactoryInterface {
protected:
typedef PeerConnectionFactoryInterface C;
public:
const INTERNAL_CLASS* internal() const { return c_; }
INTERNAL_CLASS* internal() { return c_; }
#define SIGNALING_PROXY_MAP_BOILERPLATE(c) \
protected: \
c##ProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c) \
: signaling_thread_(signaling_thread), c_(c) {} \
\
private: \
mutable rtc::Thread* signaling_thread_;
protected:
PeerConnectionFactoryProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c)
: signaling_thread_(signaling_thread), c_(c) {}
private:
mutable rtc::Thread* signaling_thread_;
#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
protected: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { c_ = nullptr; } \
rtc::scoped_refptr c_;
protected:
~PeerConnectionFactoryProxyWithInternal() {
MethodCall0<PeerConnectionFactoryProxyWithInternal, void> call(
this, &PeerConnectionFactoryProxyWithInternal::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::scoped_refptr<INTERNAL_CLASS> c_;
#define PROXY_SIGNALING_THREAD_DESTRUCTOR() \
private: \
rtc::Thread* destructor_thread() const { return signaling_thread_; } \
\
public: // NOLINTNEXTLINE
#define PROXY_METHOD1(r, method, t1) \
r method(t1 a1) override { \
MethodCall1 call(c_, &C::method, std::move(a1)); \
return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
PROXY_METHOD1(void, SetOptions, const Options&)
void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , a1> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
#define END_PROXY_MAP() \
};
template <class INTERNAL_CLASS> class PeerConnectionFactoryProxyWithInternal;
typedef PeerConnectionFactoryProxyWithInternal<PeerConnectionFactoryInterface> PeerConnectionFactoryProxy;
template <class INTERNAL_CLASS>
class PeerConnectionFactoryProxyWithInternal : public PeerConnectionFactoryInterface {
protected:
typedef PeerConnectionFactoryInterface C;
public:
const INTERNAL_CLASS* internal() const { return c_; }
INTERNAL_CLASS* internal() { return c_; }
protected:
PeerConnectionFactoryProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c)
: signaling_thread_(signaling_thread), c_(c) {}
private:
mutable rtc::Thread* signaling_thread_;
protected:
~PeerConnectionFactoryProxyWithInternal() {
MethodCall0<PeerConnectionFactoryProxyWithInternal, void> call(
this, &PeerConnectionFactoryProxyWithInternal::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::scoped_refptr<INTERNAL_CLASS> c_;
public:
static rtc::scoped_refptr<PeerConnectionFactoryProxyWithInternal> Create(
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxyWithInternal>(signaling_thread, c);
}
private:
rtc::Thread* destructor_thread() const { return signaling_thread_; }
public: // NOLINTNEXTLINE
void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , const Options&> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
};
3.2 템플릿 클래스 인스턴스화 PeerConnectionFactoryProxy
또한 PeerConnectionFactoryProxy를 PeerConnectionFactoryProxyWithInternal
class PeerConnectionFactoryProxy : public PeerConnectionFactoryInterface {
public:
static rtc::scoped_refptr<PeerConnectionFactoryProxy> Create(
rtc::Thread* signaling_thread, PeerConnectionFactoryInterface* c) {
return new rtc::RefCountedObject<PeerConnectionFactoryProxy>(signaling_thread, c);
}
void SetOptions(const Options& a1) override {
MethodCall1<PeerConnectionFactoryInterface, void , const Options&> call(c_, &PeerConnectionFactoryInterface::SetOptions, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
protected:
PeerConnectionFactoryProxy(rtc::Thread* signaling_thread, PeerConnectionFactoryInterface* c)
: signaling_thread_(signaling_thread), c_(c) {}
~PeerConnectionFactoryProxy() {
MethodCall0<PeerConnectionFactoryProxy, void> call(
this, &PeerConnectionFactoryProxy::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::Thread* destructor_thread() const { return signaling_thread_; }
private:
rtc::scoped_refptr<PeerConnectionFactoryInterface> c_;
mutable rtc::Thread* signaling_thread_;
};
이번에는 비교적 이해하기 쉽다.
이렇게 기본적인 원리는 논술이 끝났다.
4 더 나아가다
PeerConnectionFactory의 모든 방법은 명령 라인에서 실행해야 하기 때문에 상대적으로 간단하다.그러나 우리는 이런 상황을 만날 수 있다. 어떤 방법은 명령 라인에서 실행되어야 하고, 어떤 방법은 작업 라인에서 실행되어야 한다.이럴 땐 어떻게 해야 하나?
마찬가지로 api/proxy 클래스에서 다른 두 개의 매크로가 결합되어 이 기능을 제공합니다.
#define BEGIN_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
WORKER_PROXY_MAP_BOILERPLATE(c) \
REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
public: \
static rtc::scoped_refptr Create( \
rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
INTERNAL_CLASS* c) { \
return new rtc::RefCountedObject(signaling_thread, \
worker_thread, c); \
}
#define PROXY_WORKER_METHOD0(r, method) \
r method() override { \
MethodCall0 call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
끝났나요?아직 끝나지 않았어요. 그리고 또 다른 매크로도 있어요. 그다지 중요하지 않지만 WebRTC에서 보편적인 사실을 폭로했어요. owned라는 단어는 WebRTC에서 여러 번 등장하는 변수 이름에서 그 대상은 제가 가지고 있고 그 생명주기는 제가 컨트롤하고 있기 때문에 대상의 소각은 제가 할게요.
이 매크로는 다음과 같습니다.
#define OWNED_PROXY_MAP_BOILERPLATE(c) \
public: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { delete c_; } \
INTERNAL_CLASS* c_;
이에 대응하는 것은:
#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
protected: \
~c##ProxyWithInternal() { \
MethodCall0 call( \
this, &c##ProxyWithInternal::DestroyInternal); \
call.Marshal(RTC_FROM_HERE, destructor_thread()); \
} \
\
private: \
void DestroyInternal() { c_ = nullptr; } \
rtc::scoped_refptr c_;
흥미로운 점은 내부 멤버 c의 범주 및 제거 방법: