WebRTC源码分析-线程安全之Proxy,防止线程乱入

0 前言

在之前的文章WebRTC源码分析-呼叫建立过程之二(创建PeerConnectionFactory)分析PeerConnectionFactory对象的创建过程中,最后一步惊觉最终返给用户层的并非是PeerConnectionFactory对象,而是其代理对象PeerConnectionFactoryProxy。如果不深入看源码,应用层是无感知的,还以为是在直接操作PeerConnectionFactory实体对象呢,毕竟应用层的指针是PeerConnectionFactoryInterface,一般而言,第一观感肯定指向的是对应的Implement对象。

为什么如此设计呢?我们知道WebRTC是个多线程的程序,最直观的感受就是肯定会有三个基础线程signaling_thread(信令),worker_thread(工作者),network_thread(网络)。并且另外一个非常重要的点在于WebRTC内部很多对象的方法必须在指定的线程中执行,比如之前分析的PeerConnectionFactory所有成员方法必须在signaling_thread(信令)中执行,并且每个方法的开始位置都有一个断言,形如以下代码所示。如果方法没有在对应线程执行,程序走到此处必然嗝屁。

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/peerconnection_client示例工程那样,应用层根本就不会去创建上面三个基础线程,都是WebRTC内部创建的。

所以,为了使得用户使用WebRTC时可以无忧无虑,随处(不用考虑在哪个线程中)任意调用,Proxy代理层就合理的,巧妙的诞生了,据说Chrome也使用了相同的思想,相同的技术。Proxy代理层使得用户想调用某个对方的某个方法时(实际上操作的是对应的Proxy对象),将使得方法调用被代理到期待的对象的期待的方法上,并且是在恰当的线程上同步执行。

以下,我们来揭秘下Proxy代理层是如何实现的,是如何解决上述问题的

1 望而生畏的宏定义

WebRTC源码分析-呼叫建立过程之二(创建PeerConnectionFactory)文章中,提到PeerConnectionFactoryProxy是通过宏定义产生的,我们正好以PeerConnectionFactory代理的产生过程为入口点,一步步分析。
首先,在PeerConnectionFactory代理产生在api\peer_connection_factory_proxy.h文件中,该文件是专门为生成PeerConnectionFactory代理类而存在的。看上去很复杂,宏种类很多,简化来看,其实这里面的宏定义就4种而已:

  • BEGIN_SIGNALING_PROXY_MAP:用来产生代理类的名称,形如"class PeerConnectionFactoryProxy {";
  • PROXY_SIGNALING_THREAD_DESTRUCTOR:用来产生析新类的构函数;
  • PROXY_METHOD1~PROXY_METHOD4:用来产生新类的方法,数字代表入参个数;
  • END_PROXY_MAP():用来产生类声明的结尾,形如"};"
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<c##ProxyWithInternal> Create(                    \
      rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {                    \
    return new rtc::RefCountedObject<c##ProxyWithInternal>(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);   
  }

有没有觉得很神奇,代理类名称好像是PeerConnectionFactoryProxyWithInternal。我们继续

2.1.1 PROXY_MAP_BOILERPLATE

#define PROXY_MAP_BOILERPLATE(c)                          \
  template <class INTERNAL_CLASS>                         \
  class c##ProxyWithInternal;                             \
  typedef c##ProxyWithInternal<c##Interface> c##Proxy;    \
  template <class INTERNAL_CLASS>                         \
  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<c##ProxyWithInternal, void> call(      \
        this, &c##ProxyWithInternal::DestroyInternal); \
    call.Marshal(RTC_FROM_HERE, destructor_thread());  \
  }                                                    \
                                                       \
 private:                                              \
  void DestroyInternal() { c_ = nullptr; }             \
  rtc::scoped_refptr<INTERNAL_CLASS> 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<C, r, t1> 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 <class INTERNAL_CLASS> PeerConnectionFactoryProxyWithInternal模板类,该类继承于 PeerConnectionFactoryInterface;

3.2 模板类实例化 PeerConnectionFactoryProxy

同时,定义了PeerConnectionFactoryProxy为PeerConnectionFactoryProxyWithInternal<PeerConnectionFactoryInterface>; 是该模板的实例化,我们直接看这个模板实例化的类,并稍微整理下,将更加清晰,最终我们将看到一个这样的类(当然,由于需要代理的公有方法太多,因此只以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),以及目标线程对象signaling_thread_。
  • 执行PeerConnectionFactoryProxy的其他公有方法,比如SetOptions(),内部将会执行PeerConnectionFactory的SetOptions()方法,并且该方法是在目标线程signaling_thread_运行的,具体的原理见WebRTC源码分析-线程基础之跨线程同步MethodCall

如此,基本的原理就阐述完毕了。

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<c##ProxyWithInternal> Create(                     \
      rtc::Thread* signaling_thread, rtc::Thread* worker_thread,              \
      INTERNAL_CLASS* c) {                                                    \
    return new rtc::RefCountedObject<c##ProxyWithInternal>(signaling_thread,  \
                                                           worker_thread, c); \
  }
  • PROXY_WORKER_METHOD0 确保了方法在工作者线程上执行
#define PROXY_WORKER_METHOD0(r, method)                 \
  r method() override {                                 \
    MethodCall0<C, r> 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<c##ProxyWithInternal, void> 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<c##ProxyWithInternal, void> call(      \
        this, &c##ProxyWithInternal::DestroyInternal); \
    call.Marshal(RTC_FROM_HERE, destructor_thread());  \
  }                                                    \
                                                       \
 private:                                              \
  void DestroyInternal() { c_ = nullptr; }             \
  rtc::scoped_refptr<INTERNAL_CLASS> c_;

精彩之处在于内部成员c_的类别,和销毁方式:

  • owned方式,成员为INTERNAL_CLASS* c_,销毁的方式是delete c_;直接删除对象。
  • refcounted方式,成员为scoped_refptr<INTERNAL_CLASS> c_,销毁方式是c_ = nullptr;解除引用。

总结

  • 本文阐述了WebRTC中,为了防止线程乱入所做的工作:通过Proxy代理对象,将所有操作代理到对应的线程上执行对应的方法;
  • 详细的拆解了PeerConnectionFactory的公有方法均需要在信令线程上执行时,是如何被代理的;
  • 进一步地,如果有每个类的方法一部分需要在信令线程上执行,一部分需要在工作者线程上执行,这种情况如何被代理;
  • 未决问题:是否存在某些类的某些方法需要在网络线程上执行? 此时,WebRTC是如何做的呢?直接复用信令线程的实现嘛?
  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值