Threads in webrtc (1) rtc::Thread


The thread design in WebRtc is of great reference value. By learning their implementation, the understanding of threads can be improved to a higher level.
In WebRtc, it is not simply to encapsulate the API related to os thread, but to design the thread in combination with the requirements of function realization.
There are two kinds of thread objects in webrtc:

  1. rtc::Thread object

rtc::ThreadIt is the most important thread type in webrtc. It is deeply integrated with the implementation of the WebRtc library.

  1. rtc::ProcessThread object

rtc::ProcessThreadThe thread type is mainly used to perform some timing tasks.

This article will introduce rtc::Thread and its use in WebRtc .

rtc:Thread

threading model

rtc::ThreadThe threading model is as follows:

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-GZPu3OiZ-1678665809251)(https://note.youdao.com/yws/api/personal/file/WEBff381d87c74c6937c35f05c025579f26 ?method=download&shareKey=d2fd984d0ab7f17fd5395027c1aa3fab#clientId=ua4f980d8-c6d9-4&id=nodDw&originHeight=1190&originWidth=1574&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u1c3d1033-3e4f-4638-8ea8-2f6a65535ea&title=)]
如上图所示,rtc::ThreadNot only does it generate only one thread, it also contains a queue **(message list)**, it is more appropriate to call it an asynchronous task queue.

Its basic function is to execute tasks in the task queue in threads.

It is driven by events, that is to say, message listwhen there is no task ( message), it will block on the event waiting, and when an event wakes up, it will continue to execute tasks in the queue.

Main members and methods

Class Diagram

rtc::ThreadThe core classes and methods listed in the class diagram .
rtc_thread_1.png

ThreadThe class inherits from TaskQueueBase, and the semantics indicate that it is a task queue.

Core member variables:

  • MessageList messages_

It is the task queue, which is std::listdefined as follows:
typedef std::list<Message> MessageList, Messagethe class is the abstraction of the task.

  • PriorityQueue delayed_message_

The delayed task queue is a priority queue.

  • SocketServer* const ss_

Event driver, as you can see from the name, it can handle network IO events.

Core method:

Its core method is ProcessMessagethe sum Getmethod.

  • ProcessMessageIt is the execution body of the thread, and it is a while(true)loop, which Getcontinuously takes out tasks from it and then executes them. The method framework is as follows:
bool Thread::ProcessMessages(int cmsLoop) {
    
    
	while (true) {
    
    
        Message msg;
        //取出任务
        Get(&msg, cmsNext);
        ...
        //执行任务
        Dispatch(&msg);
    }
}
  • Getmethod, first take out the tasks in the queue, if there is no task, then process the event, if there is no event, then block to Waitthe method.
bool Thread::Get(Message* pmsg, int cmsWait, bool process_io) {
    
    
    //取出队列中的任务
	while(true) {
    
    
        ...
        if (messages_.empty()) {
    
    
          break;
        } else {
    
    
          *pmsg = messages_.front();
          messages_.pop_front();
        }
        ...
    	return true;
    }

    ...
    
	//如果没有任务则执行事件,如何也没有事件就阻塞在Wait方法上
    {
    
    
      // Wait and multiplex in the meantime
      if (!ss_->Wait(static_cast<int>(cmsNext), process_io))
        return false;
    }
}
control class method

bool Start()
void Stop()
void Run()
These three are typical thread control methods.

task class method

async method

It is an asynchronous queue with posta series of methods for delivering tasks to threads for execution. These postmethods are all asynchronous methods and return immediately.

  • Put in a QueuedTasktype of task. QueuedTaskMethods are encapsulated internally as Messagetypes.
void PostTask(std::unique_ptr<webrtc::QueuedTask> task) override;
  • Put in a QueuedTasktype of task to execute after delay ms.
void PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,
                       uint32_t milliseconds) override;
  • Post tasks to threads for execution.
virtual void Post(const Location& posted_from,
                    MessageHandler* phandler,
                    uint32_t id = 0,
                    MessageData* pdata = nullptr,
                    bool time_sensitive = false);
  • Post the task to the thread and execute it after delay ms.
virtual void PostDelayed(const Location& posted_from,
                           int delay_ms,
                           MessageHandler* phandler,
                           uint32_t id = 0,
                           MessageData* pdata = nullptr);
  • Post the task to the thread and execute it at the specified time point.
virtual void PostAt(const Location& posted_from,
                      int64_t run_at_ms,
                      MessageHandler* phandler,
                      uint32_t id = 0,
                      MessageData* pdata = nullptr);

The following two are Postpackages based on the above three methods, which are easier to use. Any type of callable object (function object, lambda, normal method) can be passed in.

template <class FunctorT>
  void PostTask(const Location& posted_from, FunctorT&& functor);
template <class FunctorT>
  void PostDelayedTask(const Location& posted_from,
                       FunctorT&& functor,
                       uint32_t milliseconds)

Using these **Post**methods, it is very convenient **rtc::Thread**to deliver tasks between them, that is to say, they can deliver tasks from one **rtc::Thread**to another .**rtc:Thread**

synchronization method

sendThe method posts a task to the thread queue, and returns after the task is executed.

virtual void Send(const Location& posted_from,
                    MessageHandler* phandler,
                    uint32_t id = 0,
                    MessageData* pdata = nullptr);

The following two Invokemethods are sendmethod encapsulations, which can be passed in callable objects. And the execution result of the task can be obtained.

template <
      class ReturnT,
      typename = typename std::enable_if<!std::is_void<ReturnT>::value>::type>
  ReturnT Invoke(const Location& posted_from, FunctionView<ReturnT()> functor);
template <
      class ReturnT,
      typename = typename std::enable_if<std::is_void<ReturnT>::value>::type>
  void Invoke(const Location& posted_from, FunctionView<void()> functor)

Constructor method

You can see from the above class rtc::Threadthat there are many ways to create an object, you can use its constructor directly, and they all require an event driver ( SocketServer). You can also use createsystem methods, which are simpler than constructors.

  • static std::unique_ptr<Thread> CreateWithSocketServer();

As mentioned earlier, the thread's task queue is driven by events. This method is to create a thread that can handle IO events.

  • static std::unique_ptr<Thread> Create();

This method is to create an event-driven thread that does not handle IO events.

  • static Thread* Current();

Wrap the current thread as an Threadobject. Note that if Threadthe object is generated by calling this method, the method cannot be called again Start. Because the thread is already running, there is no need to call it again. Its queue can be utilized by using methods like this:

Thread* th_main = Thread::Current();
th_main->ProcessMessages(kForever);

The purpose of rtc::Thread

**rtc::Thread**It serves three purposes:

  1. Used as an ordinary package class (requires overloading the Run() method).**Thread**
  2. Used as a message queue (the SocketServer object created at this time is NullSocketServer, and the role of NullSocketServer is to make the thread event-driven instead of poll-driven).
  3. It is used as a message queue with the function of managing IO (the SocketServer object created at this time is PhysicalSocketServer, which can handle I/O events).

Three major threads in WebRtc

The threading model in WebRtc is very clear. It is divided into three major threads according to their functions, each of which performs its own duties.

  • Signaling thread **signaling_thread**, the thread that calls the WebRtc API (that is, the client program thread).
  • Worker thread **worker_thread**, the thread for internal routine logic work.
  • Network thread **network_thread**, data sending and receiving thread.

They are all rtc::Threadobjects. When creating a PreeConnection object, it is required to pass in these three thread objects.

RTC_EXPORT rtc::scoped_refptr<PeerConnectionFactoryInterface>
CreatePeerConnectionFactory(
rtc::Thread* network_thread,
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
rtc::scoped_refptr<AudioDeviceModule> default_adm,
rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
rtc::scoped_refptr<AudioMixer> audio_mixer,
rtc::scoped_refptr<AudioProcessing> audio_processing);

Thread pointer, NULL can be passed in, created internally by WebRtc.

network_thread is a SocketServer
owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
owned_network_thread_->SetName("pc_network_thread", nullptr);

network_thread, as the name suggests, is used to send and receive packets. The following is the receiving stack of network_thread.
Network thread receiving flow process.png

network_queue packet sending stack
image.png

worker_thread is a message queue
owned_worker_thread_ = rtc::Thread::Create(); //创建的是NullSocketServer
owned_worker_thread_->SetName("pc_worker_thread", nullptr);
signaling_thread is also a message queue

Inside WebRtc, if signaling_threadit is NULL, the thread calling the API will be wrapped into one rtc::thread.

The post method used by the three major threads **rtc::Thread**delivers tasks to each other and performs thread switching, and the division of responsibilities is very clear.

rtc::Threadusage of

Create a without network IO functionrtc::Thread

  • Defines a task, which is a function object.
//一个任务
struct ThreadTask
{
    
    
    void operator()()
    {
    
    
        std::cout<<std::this_thread::get_id()<<std::endl;
    }
};
  • create thread
auto p1 = Thread::Create();
p1->Start();
  • p1send one toThreadTask
p1->PostTask(RTC_FROM_HERE,ThreadTest());
  • p1Post a deferred execution toThreadTask
//延迟1秒执行
p1->PostDelayedTask(RTC_FROM_HERE,ThreadTest(),1000);
Create an IO functionrtc::Thread
std::unique_ptr<Thread> th1 = Thread::CreateWithSocketServer()

rtc::Threadexample in unit test

To learn rtc::Threadmore about how to use it, you can take a look at its unit test code. The following is PostTaskan example of the interface, and you can PostTaskpass in various callable objects.

  • InvokesWitBindexample
TEST(ThreadPostTaskTest, InvokesWithBind) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    Event event;
    background_thread->PostTask(RTC_FROM_HERE, Bind(&Event::Set, &event));
    event.Wait(Event::kForever);
}
  • InvokesWithLambdaexample
TEST(ThreadPostTaskTest, InvokesWithLambda) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    Event event;
    background_thread->PostTask(RTC_FROM_HERE, [&event] {
    
     event.Set(); });
    event.Wait(Event::kForever);
}
  • InvokesWithCopiedFunctorexample
TEST(ThreadPostTaskTest, InvokesWithCopiedFunctor) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    //LifeCycleFunctor是有拷贝语义的对象
    LifeCycleFunctor functor(&stats, &event);
    background_thread->PostTask(RTC_FROM_HERE, functor);
    event.Wait(Event::kForever);

    EXPECT_EQ(1u, stats.copy_count);
    EXPECT_EQ(0u, stats.move_count);
}
  • InvokesWithMovedFunctorexample
TEST(ThreadPostTaskTest, InvokesWithMovedFunctor) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    LifeCycleFunctor functor(&stats, &event);
    //通过std::move产生右值引用
    background_thread->PostTask(RTC_FROM_HERE, std::move(functor));
    event.Wait(Event::kForever);

    EXPECT_EQ(0u, stats.copy_count);
    EXPECT_EQ(1u, stats.move_count);
}
  • InvokesWithReferencedFunctorShouldCopyexample
TEST(ThreadPostTaskTest, InvokesWithReferencedFunctorShouldCopy) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    LifeCycleFunctor functor(&stats, &event);
    //传入对象引用
    LifeCycleFunctor& functor_ref = functor;
    background_thread->PostTask(RTC_FROM_HERE, functor_ref);
    event.Wait(Event::kForever);

    EXPECT_EQ(1u, stats.copy_count);
    EXPECT_EQ(0u, stats.move_count);
}

Guess you like

Origin blog.csdn.net/mo4776/article/details/129329974