An overview of the

Recently, I am interested in innoDB kernel. I will update some kernel related articles in the future. This article mainly describes how to do the synchronization mechanism of the kernel. The implementation is similar to the JDK. Through spin + queue blocking + signal wake up. Learn from mysql-5.0.15.

Specific details

The mutex and rw_lock

Innodb implements mutex and RW_lock. Rw_lock is based on mutex (read/write locks), so the underlying principles are almost the same, except that rw_lock does some logic on top of mutex.

Rw_lock is primarily designed to further improve concurrency performance while avoiding race conditions.

There are two types of RW_locks: X-latch and S-LATCH

S-latch X-latch
S-latch Compatible with Are not compatible
X-latch Are not compatible Are not compatible

The X-Latch is reentrant. The S-Latch cannot be reentrant because the re-entrant of the S-Latch conflicts with another thread that has applied for the X-Latch lock for the resource, resulting in a deadlock.

Gets the mutex lock logic

Through the mutex_enter_func method. If the wait array fails, the thread is added to the wait array and blocked.

UNIV_INLINE
void mutex_enter_func(
                mutex_t *mutex,                          /* in: pointer to mutex */
                const char *file_name, /* in: file name where locked */
                ulint line)                                              /* in: line where locked */
{
        ut_ad(mutex_validate(mutex));
        /* Note that we do not peek at the value of lock_word before trying the atomic test_and_set; we could peek, and possibly save time. */
#ifndef UNIV_HOTBACKUP
        mutex->count_using++;
#endif /* UNIV_HOTBACKUP */
        if(! mutex_test_and_set(mutex)) {return; /* Succeeded! * /
        }
        mutex_spin_wait(mutex, file_name, line);
}
Copy the code

Try first with mutex_test_and_set and call mutex_spin_wait if it fails. The details are spins a number of times (20) and then joins the wait Array after failure.

Wait array

Innodb has a global Wait Array that initially allocates 1000 memory. Every time the spin fails, it joins this queue. Whenever the thread holding the latch is released, the waiting thread is awakened by calling the sync_array_singnal_object method.

void sync_array_signal_object(
                sync_array_t *arr, /* in: wait array */
                void *object)                    /* in: wait object */
{
        sync_cell_t *cell;
        ulint count;
        ulint i;
        sync_array_enter(arr);
        arr->sg_count++;
        i = 0;
        count = 0;
        while (count < arr->n_reserved)
        {
                cell = sync_array_get_nth_cell(arr, i);
                if(cell->wait_object ! =NULL)
                {
                        count++;
                        if (cell->wait_object == object)
                        {
                                sync_cell_event_set(cell);
                        }
                }
                i++;
        }
        sync_array_exit(arr);
}
Copy the code

This method iterates through the global array. If a thread is waiting to acquire the lock on the object, it sets the corresponding cell signal and event event to release the lock. The blocking thread will wake up and try again to acquire the lock.

Obtain the S-Latch logic

Run the rw_LOCK_s_lock_LOW command to check the writer field of the RW_lock. If the writer field is RW_LOCK_NOT_LOCKED, it succeeds. Otherwise, call rw_lock_s_lock_spin to get it.

UNIV_INLINE
void
rw_lock_s_lock_func(
        rw_lock_t*      lock,   /* in: pointer to rw-lock */
        ulint           pass,   /* in: pass value; ! = 0, if the lock will be passed to another thread to unlock */
        const char*     file_name,/* in: file name where lock requested */
        ulint           line)   /* in: line where requested */
{
        mutex_enter(rw_lock_get_mutex(lock));


        if (UNIV_LIKELY(rw_lock_s_lock_low(lock, pass, file_name, line))) {
                mutex_exit(rw_lock_get_mutex(lock));

                return; /* Success */
        } else {
                /* Did not succeed, try spin wait */
                mutex_exit(rw_lock_get_mutex(lock));
                rw_lock_s_lock_spin(lock, pass, file_name, line);
                return; }}Copy the code

Rw_lock_s_lock_spin is basically a spin attempt thrown into a queue.

The wake up operation is similar.

The deadlock problem

Deadlocks are avoided through latch-level.

As described above, as long as the sequence to lock, there will be no deadlock phenomenon. In debug mode, deadlocks can be detected. Through the depth-first algorithm to detect whether there is a loop.

conclusion

Innodb has a more traditional synchronization mechanism through TAS➕ spin ➕wait array. Innodb will continue to be updated in the future. Stay tuned