Libjingle Development Series 3: Message Mechanism

Source: Internet
Author: User

I admit that I am lazy. I wanted to write it. I found someone already wrote it. Forget it. Repost an article:

Original article link

1. The Thread class is the core class in libjingle. I divided his functions into three parts:

1. Thread-related functions: the uniform interface packaging for threads in different operating systems. It includes thread control functions such as start (), stop (), join (), priority control functions, and locating functions. The function of locating and searching is mainly controlled by threadmanager, which manages threads. Of course, there will be a global threadmanager object to register, locate, and search all the thread objects. The execution function in thread is prerun (). In the prerun () function, the threadmanager: setcurrent () function is used to set the current thread.

2. Message Mechanism: this function is mainly obtained by inheriting messagequeue.

The core function in thread is processmessages ():

Bool thread: processmessages (INT cmsloop ){
Uint32 msend;
If (cmsloop! = Kforever)
Msend = getmillisecondcount () + cmsloop;
Int cmsnext = cmsloop;
While (true ){
Message MSG;
If (! Get (& MSG, cmsnext ))
Return false;
Dispatch (& MSG );
If (cmsloop! = Kforever ){
Uint32 mscur = getmillisecondcount ();
If (mscur> = msend)
Return true;
Cmsnext = msend-mscur;
}
}
}

This function is used to obtain messages cyclically and dispatch messages. Of course, there is a timeout mechanism. Messagequeue: Get () and dispatch () functions are used here,

This topic will be highlighted later.

3. Asynchronous Network event listening. It inherits the functions obtained by socketserver. (Note that messagequeue inherits socketserver. PS: I personally think it is more comfortable to separate these two functional interfaces .) This will also be highlighted.

Ii. messagequeue: (a friend who understands the message mechanism can skip this step ...)

Messagequeue mainly implements a message queue for post, get, and peek messages. Is it useful to decouple processing logic and content, which is often used in programming.

1. Message format:

Struct message {
Message (){
Memset (this, 0, sizeof (* This); // Initialization
}
Messagehandler * phandler; // The callback function of the message, used to process the operation to get the message.
Uint32 message_id; // Message ID
Messagedata * pdata; // message data is a flag Interface
Uint32 ts_sensitive ;//?
};

2. Message Queue type:

There are two main types of message queues, one is the general first-in-first-out message queue, and the other is the priority queue that can set the delayed time according to the delayed time.

2. Send a message:

We use the post function to send messages.

Void messagequeue: Post (messagehandler * phandler, uint32 ID,
Messagedata * pdata, bool time_sensitive ){
If (fstop _)
Return;
// Keep thread safe
// Add the message to the end of the queue
// Signal for the multiplexer to return
Critscope CS (& crit _);
Ensureactive ();
Message MSG;
MSG. phandler = phandler;
MSG. message_id = ID;
MSG. pdata = pdata;
If (time_sensitive ){
MSG. ts_sensitive = Time () + kmaxmsglatency;
}
Msgq _. Push (MSG );
SS _-> wakeup ();
}

Here, a message is formed and placed at the end of the message queue. This operation is thread-safe. In addition, the wakeup () method of the socketserver multiplexing is called to wake up the multiplexing waiting for a certain period of time.

Similarly, postdelayed sends messages to a priority queue.

3. Retrieve the message:

We use get () to obtain the message.

Bool messagequeue: Get (Message * PMSG, int cmswait ){
// Return and clear peek if present
// Always return the peek if it exists so there is PEEK/get verify ry
If (fpeekkeep _){
* PMSG = msgpeek _;
Fpeekkeep _ = false;
Return true;
}
// Get w/Wait + timer scan/Dispatch + socket/event multiplexer dispatch
Int cmstotal = cmswait;
Int cmselapsed = 0;
Uint32 msstart = Time ();
Uint32 mscurrent = msstart;
While (true ){
// Check for sent messages
Receivesends ();
// Check queues
Int cmsdelaynext = kforever;
{
Critscope CS (& crit _);
// Check for delayed messages that have been triggered
// Calc the next trigger too
While (! Dmsgq _. Empty ()){
If (mscurrent <dmsgq _. Top (). mstrigger _){
Cmsdelaynext = dmsgq _. Top (). mstrigger _-mscurrent;
Break;
}
Msgq _. Push (dmsgq _. Top (). MSG _);
Dmsgq _. Pop ();
}
// Check for posted events
While (! Msgq _. Empty ()){
* PMSG = msgq _. Front ();
If (PMSG-> ts_sensitive ){
Long Delay = timediff (mscurrent, PMSG-> ts_sensitive );
If (delay> 0 ){
Log_f (ls_warning) <"ID:" <PMSG-> message_id <"Delay :"
<(Delay + kmaxmsglatency) <"Ms ";
}
}
Msgq _. Pop ();
If (mqid_dispose = PMSG-> message_id ){
Assert (null = PMSG-> phandler );
Delete PMSG-> pdata;
Continue;
}
Return true;
}
}
If (fstop _)
Break;
// Which is shorter, the delay wait or the asked wait?
Int cmsnext;
If (cmswait = kforever ){
Cmsnext = cmsdelaynext;
} Else {
Cmsnext = cmstotal-cmselapsed;
If (cmsnext <0)
Cmsnext = 0;
If (cmsdelaynext! = Kforever) & (cmsdelaynext <cmsnext ))
Cmsnext = cmsdelaynext;
}
// Wait and multiplex in the meantime
SS _-> wait (cmsnext, true );
// If the specified Timeout expired, return
Mscurrent = Time ();
Cmselapsed = mscurrent-msstart;
If (cmswait! = Kforever ){
If (cmselapsed> = cmswait)
Return false;
}
}
Return false;
}

As mentioned in the annotations, The get method mainly performs the following tasks.

1. If a message has already been peek, the message is taken out and OK is returned directly, and the peekkeep flag is set to false.

2. First fetch the message from the delayed queue to see if there is any message at that time. If yes, put it in the master message queue.

3. Retrieve the message from the primary message queue. If yes, OK is returned directly. If no, continue with the following steps. There is another message to delete the message. If you receive the message just now, you need to execute the dispose operation.

4. Check the stop flag. If the stop flag is not completed, use the remaining time to listen to the multiplexing to listen for Asynchronous Network events. When you see this article, you will understand why the wakeup multiline multiplexing is required when the post message is sent. It is king to quickly respond to the message!

Peek message:

The peek message is actually a view of the message. You can also get the message when getting it. At first, I thought the message would not pop out from the queue, but here he took a similar approach. Set a message variable to save the msg of the peeked, and a flag of peekkeep _ to indicate whether the message is saved to the variable by peek. Therefore, when peekkeep _ is true, get will retrieve the message in the variable and set the flag to false.

The implementation of other functions is also relatively basic and simple. Here we will not talk about the boring thing of message queue.

Socketserver

Next, let's take a look at the socketserver that I'm most interested in.

Libjingle is the Gtalk client. It is used to communicate with the underlying socket of the server.

1. Socket:

Socketserver is a socketfactory first, so it can create a socket to create a blocking and non-blocking socket.

The created socket is a high-level abstraction of the underlying socket of different operating systems.

Mainly include BIND (), connect (), send (), Recv () and so on.

Asyncsocket has some more signal events to capture and process the outside world.

Class asyncsocket: Public socket, public sigslot: has_slots <> {
Public:
Virtual ~ Asyncsocket (){}
Sigslot: signal1 <asyncsocket *> signalreadevent; // ready to read
Sigslot: signal1 <asyncsocket *> signalwriteevent; // ready to write
Sigslot: signal1 <asyncsocket *> signalconnectevent; // connected
Sigslot: signal2 <asyncsocket *, int> signalcloseevent; // closed
// Todo: Error
};

The signal/slot mechanism is used here. This mechanism is great. If you have used QT before, you will certainly understand it. It is similar to the signal/Slot Mechanism of QT, I haven't had time to analyze the signal/slot library in libjingle, But it's short and concise. It should be good. I will have the opportunity to analyze it later. However, the most important thing is to know that this is something similar to the observer mode. The most important thing is that when you send a message, there is a signal and some slots are bound with the connect function at the beginning, signal is the observer, and slot is the observer. IF signal is triggered, the slot can be connected.

Socketserver is a high-level abstraction of listening for network events. It has two main functions: Wait () and wakeup (). Wait () is used for listening, and waeup () can be used to wake up.

Here we mainly use Asynchronous clients. Select multiplexing is used to listen for Asynchronous Network events.

Of course, the advantage of abstraction is that there can be different implementations, or even virtual mock implementations.

Physicalsocketserver is used by default. This class encapsulates the differences in Network Libraries between different operating systems.

I will not introduce the encapsulation of common socket functions here. I will mainly introduce the working mechanisms in wait () and wakeup () functions. Here we use Linux as an example.

1. DISPATCHER:

Class dispatcher {
Public:
Virtual uint32 getrequestedevents () = 0; // return the event of interest
Virtual void onpreevent (uint32 ff) = 0; // before the event is executed
Virtual void onevent (uint32 ff, int ERR) = 0; // when the event is executed

Virtual int getdescriptor () = 0; // returns the description of dispather.
};

Here we introduce the concept of dispatcher. dispatcher is used to dispatch events. Therefore, we can introduce an approach for the main loop of the multier: that is, when we receive the event, we use different dispatcher for dispatching. The event here can be a network event or another event that can be selected by the multiplexing.

Let's first look at the network event dispatcher socketdispatcher.

It inherits physicalsocket and dispatcher, and then performs Asynchronous Network Event Notification processing in the onevent () distribution function. The signal is interested in network connection, read, write, and close events, in addition, you can update network events of interest to remove events that have already been performed.

Virtual void onevent (uint32 ff, int ERR ){
Int cache_id = ID _;
If (FF & kfread )! = 0 ){
Enabled_events _ & = ~ Kfread;
Signalreadevent (this );
}
If (FF & kfwrite )! = 0) & (ID _ = cache_id )){
Enabled_events _ & = ~ Kfwrite;
Signalwriteevent (this );
}
If (FF & kfconnect )! = 0) & (ID _ = cache_id )){
If (FF! = Kfconnect)
Log (ls_verbose) <"signalled with kfconnect:" <ff;
Enabled_events _ & = ~ Kfconnect;
Signalconnectevent (this );
}
If (FF & kfclose )! = 0) & (ID _ = cache_id )){
// Log (Info) <"Sock [" <static_cast <int> (S _) <"] onclose () error:" <err;
Signal_close _ = true;
Signal_err _ = err;
}
}

Another type is the event dispatcher. in Linux, eventdispatcher uses pipe for simulation (in windows, the event mechanism is available ). This can also be selected by the multiplexing.

Signal is written from one end to the pipeline, and read from the other end during the preonevent.

An instance here is a class Signaler. This class inherits eventdispatcher to exit wait and reach wakeup.

2. Wait () function details:

Here I have posted a Linux implementation code for wait (), excluding the timeout mechanism.

// Zero all fd_sets. Don't need to do this inside the loop since
// Select () zeros the descriptors not signaled
Fd_set fdsread;
Fd_zero (& fdsread );
Fd_set fdswrite;
Fd_zero (& fdswrite );
Fwait _ = true;
While (fwait _){
Int fdmax =-1;
{
Critscope Cr (& crit _);
For (unsigned I = 0; I <dispatchers _. Size (); I ++ ){
// Query dispatchers for read and write wait state
Dispatcher * pdispatcher = dispatchers _ [I];
Assert (pdispatcher );
If (! Process_io & (pdispatcher! = Signal_wakeup _))
Continue;
Int FD = pdispatcher-> getdescriptor ();
If (FD> fdmax)
Fdmax = FD;
Uint32 FF = pdispatcher-> getrequestedevents ();
If (FF & kfread)
Fd_set (FD, & fdsread );
If (FF & (kfwrite | kfconnect ))
Fd_set (FD, & fdswrite );
}
}
// Wait then call handlers as appropriate
// <0 means Error
// 0 means timeout
//> 0 means count of descriptors ready
Int n = select (fdmax + 1, & fdsread, & fdswrite, null, ptvwait );
// If error, Return Error
// Todo: Do something intelligent
If (n <0)
Return false;
// If timeout, return success
If (n = 0)
Return true;
// We have signaled Descriptors
{
Critscope Cr (& crit _);
For (unsigned I = 0; I <dispatchers _. Size (); I ++ ){
Dispatcher * pdispatcher = dispatchers _ [I];
Int FD = pdispatcher-> getdescriptor ();
Uint32 FF = 0;
If (fd_isset (FD, & fdsread )){
Fd_clr (FD, & fdsread );
FF | = kfread;
}
If (fd_isset (FD, & fdswrite )){
Fd_clr (FD, & fdswrite );
If (pdispatcher-> getrequestedevents () & kfconnect ){
FF | = kfconnect;
} Else {
FF | = kfwrite;
}
}
If (FF! = 0 ){
Pdispatcher-> onpreevent (ff );
Pdispatcher-> onevent (FF, 0 );
}
}
}

1. Create and initialize fd_set.

2. Add events of interest to fd of Each dispatcher.

3. Select ().

4. Update the events that interest Each dispatcher to ff, and then execute onpreevent and onevent.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.