Synchronization and mutex of Android process threads (1)

Source: Internet
Author: User

1.1.1 synchronization and mutex in Android
The Android system also provides its own synchronization and mutex mechanism. However, the essence of any technology is similar. More is to apply these essential things to scenarios that meet your requirements. Currently, the synchronization mutex classes in Android package include:

· Mutex

The header file is in frameworks/native/include/utils/Mutex. h. Because the implementation is related to the specific platform, we only care about how to use it.

· Condition

The header file is in frameworks/native/include/utils/Condition. h. Similarly, we only apply it.

· Barrier

Frameworks/native/services/surfaceflinger/Barrier. h

This is a model based on Mutex and Condition. Currently, this model is only used in SurfaceFlinger.

1.1.1.1 Mutex
The Mutex class has an enum definition as follows:

Class Mutex {

Public:

Enum {

PRIVATE = 0,

SHARED = 1

};

If SHARED, it applies to cross-process sharing. For example, AudioTrack and AudioFlinger reside in two different processes, so their mutex is of this type:

/* Frameworks/av/media/libmedia/AudioTrack. cpp */

Audio_track_cblk_t: audio_track_cblk_t ()

: Lock (Mutex: SHARED), cv (Condition: SHARED), user (0), server (0 ),

UserBase (0), serverBase (0), buffers (NULL), frameCount (0 ),

LoopStart (UINT_MAX), loopEnd (UINT_MAX), loopCount (0), mVolumeLR (0x10001000 ),

MSendLevel (0), flags (0)

{

}

The Mutex class has three important member functions:

Status_t lock (); // obtain the resource lock

Void unlock (); // release the resource lock

Status_t tryLock ();/* if the current resource is available, it will be locked; otherwise, it will be returned directly. The return value 0 indicates success. We can see it and lock ()

The difference is that success or failure will return in time, rather than waiting */

Its constructor has three functions:

/* Frameworks/native/include/utils/Mutex. h */

Inline Mutex: Mutex (){

Pthread_mutex_init (& mMutex, NULL );

}

Inline Mutex: Mutex (const char * name ){

Pthread_mutex_init (& mMutex, NULL );

}

Inline Mutex: Mutex (int type, const char * name ){

If (type = SHARED ){

Pthread_mutexattr_tattr;

Pthread_mutexattr_init (& attr );

Pthread_mutexattr_setpshared (& attr, PTHREAD_PROCESS_SHARED );

Pthread_mutex_init (& mMutex, & attr );

Pthread_mutexattr_destroy (& attr );

} Else {

Pthread_mutex_init (& mMutex, NULL );

}

}

It can be seen that Mutex is actually a pthread-based encapsulation.

1.1.1.2 Condition
Condition means "Condition". In other words, its core is "whether the Condition is met". If the Condition is met, an operation is executed. If the Condition is not met, the system waits, wait until the condition is met and someone wakes it up.

Someone may ask, can Mutex be used to implement this situation? Theoretically, this is indeed true. For example, if two threads A and B share A global variable vari, their behavior is as follows:

Thread A: constantly modify vari. The value after each change is unknown.

Thread B: When vari is 0, it needs to do some action

That is to say, thread A wants to obtain vari access, while thread B waits for vari = 0. If Mutex is used, thread B can only judge whether the condition is met by constantly reading vari, which is similar to the following pseudo code:

While (1)

{

Acquire_mutex_lock (); // gets the Mutex lock on vari

If (0 = vari) // The condition is met

{

Release_mutex_lock (); // release the lock

Break;

}

Else

{

Release_mutex_lock (); // release the lock

Sleep (); // sleep for a period of time

}

}

For thread B, it is unknown when it meets the condition (vari = 0), which is very different from other threads that share vari (such as thread, therefore, the round-robin method is a great waste of CPU time.

Let's take another example in our daily life to deepen our understanding. For example, if there is a public toilet, it is assumed that it can only be used by one person at the same time. There are two types of people who want to use this resource: one is of course the person who uses the toilet normally, and the other is the person who replaces the toilet paper. What will happen if we treat them equally? That is, the staff also need to queue up. When he exists, he goes in and checks whether the toilet paper is used up. If yes, he will change it. Otherwise, he will quit without doing anything and wait in the queue. In other words, the employee's efficiency is quite low, because his time is wasted in the queue.

Therefore, we need to find another model to solve this special scenario. One solution is that the staff member does not need to wait in line, but is notified of the paper shortage in the toilet by others. This not only reduces the number of queuing staff, but also improves the efficiency of the staff.

Condition is the solution proposed for these scenarios.

Class Condition {

Public:

Enum {// Like Mutex, it supports cross-process Sharing

PRIVATE = 0,

SHARED = 1

};

...

Status_t wait (Mutex & mutex); // wait on a condition

Status_t waitRelative (Mutex & mutex, nsecs_treltime); // It is also waiting on a certain condition and adds the timeout exit function.

Void signal (); // notify the corresponding waiting person when the condition is met

Void broadcast (); // when conditions are met, all the waiting persons are notified.

Private:

# If defined (HAVE_PTHREADS)

Pthread_cond_t mCond;

# Else

Void * mState;

# Endif

};

From the several interface functions provided by Condition, we have the following questions:

· Since wait () is waiting for "conditions to be met", what are the conditions?

In the description of the entire Condition class, we cannot see variables or operations related to conditions. This is because Condition is actually a "semi-finished product" and does not provide specific "conditions"-The reason is very simple. In different situations, the "conditions" format required by users are different, condition wants to provide a "general solution" instead of designing for certain "Condition styles. For example, we can say that "satisfying the condition" means that A variable A is True, variable A reaches the value of 100, or variable A is equal to B, and so on. This is unpredictable by Condition, so what it can do is to provide a "black box", no matter what is in the box

· Why mutex?

I believe you have noticed that both the wait and waitRelative interfaces have a Mutex & mutex variable, which many people are puzzled about. Since there is a Condition Mutex, why does it involve a Mutex?

 

Due to the incompleteness of Condition itself, it is hard to understand it if we analyze it directly from the theoretical perspective. Therefore, we hope to answer the two questions mentioned above with the Barrier in the next section.

1.1.1.3 Barrier
Condition indicates "Condition", while Barrier indicates "fence and obstacle ". The latter is an application of the former. In other words, Barrier is filled with a Condition, which provides a good example for us to understand the Condition.

Barrier is defined in SurfaceFlinger and is not provided to the entire Android system as a common Utility like Condition. However, this does not affect our analysis.

/* Frameworks/native/services/surfaceflinger/Barrier. h */

Class Barrier

{

Public:

Inline Barrier (): state (CLOSED ){}

Inline ~ Barrier (){}

Void open (){

Mutex: Autolock_l (lock );

State = OPENED;

Cv. broadcast ();

}

Void close (){

Mutex: Autolock_l (lock );

State = CLOSED;

}

Void wait () const {

Mutex: Autolock_l (lock );

While (state = CLOSED ){

Cv. wait (lock );

}

}

Private:

Enum {OPENED, CLOSED };

Mutable Mutex lock;

Mutable Condition cv;

Volatile int state;

};

Barrier provides a total of three interface functions, namely, wait (), open (), and close (). We say it is a Condition instance. What is the Condition? The variable state = OPENED, and the other state is CLOSED, which is similar to the opening and closing of the car barrier. Before a car passes, it must first confirm that the fence is enabled, so wait () is called. If the conditions are not met, the car can only stop and wait. This function first obtains a Mutex lock and then calls the Condition object cv. Why? We know that Mutex is used to share Mutex resources between threads. This indicates that the following operations in wait () involve access to a Mutex resource. This resource is obviously the state variable. It can be imagined that if there is no lock for state access, will it cause problems when wait and open/close operate on it at the same time?

Take the following steps:

Step 1. wait () obtains the state value and finds it is CLOSED.

Step 2. open () to get the state value and change it to OPENED

Step 3. open () Wake up the waiting thread. Because wait () is not sleeping at this time, there is actually no thread to wake up

Step 4.wait () enters the wait state because state = CLOSED, but the fence is enabled at this time, which will cause the thread where the wait () caller is located to not be awakened.

This makes it clear that access to the state must be protected by a mutex lock.

Let's take a look at the implementation of Condition: wait:

Inline status_t Condition: wait (Mutex & mutex ){

Return-pthread_cond_wait (& mCond, & mutex. mMutex );

}

It is very simple. The method in pthread is called directly.

The logical semantics of pthread_cond_wait is as follows:

1. Release the lock mutex

2. Enter sleep waiting

3. Get the mutex lock after waking up.

Here we have gone through the steps to release the lock before obtaining it. Why?

Since wait is about to enter sleep wait, if it does not release the Mutex lock at this time, how can open ()/close () access the "Conditional Variable" state? This will undoubtedly cause the program to fall into a deadlock of mutual wait. So it needs to release the lock first and then go to sleep. After the open () operation is complete, the lock will be released, so that wait () has the opportunity to obtain this Mutex again.

At the same time, we noticed that the statement to determine whether the condition is met is a while loop:

While (state = CLOSED ){...

This is also reasonable. Let's assume that if we add a broadcast () or signal () at the end of close (), the wait () will also be awakened, but will the conditions be met? Obviously not, so wait () can only wait again until the condition is actually OPENED.

It is worth noting that the Mutex lock will be automatically released at the end of the wait () function (for the description of Autolock, see the next section), that is, when wait () returns, the program no longer has a lock on shared resources. I personally think that if the subsequent code is still dependent on operations on shared resources, the lock should be obtained again; otherwise, an error will still occur. For example, when wait () returns, we can think that the barrier is opened. But because the lock is released, it is very likely that someone will close it when the car starts. The consequence of this is that the car will directly hit the fence and cause an accident. Barrier is usually used to determine whether a thread is initialized. This scenario is irreversible-since it has been initialized, it is impossible to see "No initialization" in the future, therefore, even if wait () does not obtain the lock after it returns, it is considered safe.

Conditional Variable Condition is a resource protection method that is equally important to Mutex lock. Everyone must understand them clearly. Of course, we will learn more from the perspective of usage. As for how pthread_cond_wait is implemented, we do not need to go further into the specific hardware platform.

1.1.1.4 Autolock
There is also an Autolock nested class in the Mutex class. Literally, it should be used to automatically add and unlock operations. How can this be achieved?

In fact, it is very simple. You can see the constructor and destructor of this class:

Class Autolock {

Public:

InlineAutolock (Mutex & mutex): mLock (mutex) {mLock. lock ();}

Inline Autolock (Mutex * mutex): mLock (* mutex) {mLock. lock ();}

Inline ~ Autolock () {mLock. unlock ();}

Private:

Mutex & mLock;

};

That is to say, when the Autolock is constructed, the lock () method of the internal member variable mLock is actively called, while the opposite is true in the analysis. The unlock () method of the internal member variable is called to release the lock. In this case, if an Autolock object is a local variable, the resource lock will be automatically unlocked at the end of the lifecycle. The example in AudioTrack is as follows:

/* Frameworks/av/media/libmedia/AudioTrack. cpp */

Uint32_t audio_track_cblk_t: framesAvailable ()

{

Mutex: Autolock _ l (lock );

ReturnframesAvailable_l ();

}

Variable _ l is an Autolock object. It automatically calls the lock in audio_track_cblk_t during construction. When framesAvailable () ends, the lifecycle of _ l also ends, the lock corresponding to the lock will also be opened. This is an implementation tips. In some cases, it can effectively prevent developers from using lock/unlock.

 

 

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.