函数buf_page_get_gen

时间:2023-12-23 23:42:55
/********************************************************************//**
This is the general function used to get access to a database page.
@return    pointer to the block or NULL */
UNIV_INTERN
buf_block_t*
buf_page_get_gen(
/*=============*/
    ulint        space,    /*!< in: space id */
    ulint        zip_size,/*!< in: compressed page size in bytes
                or 0 for uncompressed pages */
    ulint        offset,    /*!< in: page number */
    ulint        rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */
    buf_block_t*    guess,    /*!< in: guessed block or NULL */
    ulint        mode,    /*!< in: BUF_GET, BUF_GET_IF_IN_POOL,
                BUF_PEEK_IF_IN_POOL, BUF_GET_NO_LATCH, or
                BUF_GET_IF_IN_POOL_OR_WATCH */
    const char*    file,    /*!< in: file name */
    ulint        line,    /*!< in: line where called */
    mtr_t*        mtr)    /*!< in: mini-transaction */
{
    buf_block_t*    block;
    ulint        fold;
    unsigned    access_time;
    ulint        fix_type;
    ibool        must_read;
    ulint        retries = ;
    buf_pool_t*    buf_pool = buf_pool_get(space, offset);

    ut_ad(mtr);
    ut_ad(mtr->state == MTR_ACTIVE);
    ut_ad((rw_latch == RW_S_LATCH)
          || (rw_latch == RW_X_LATCH)
          || (rw_latch == RW_NO_LATCH));

    ut_ad(zip_size == fil_space_get_zip_size(space));
    ut_ad(ut_is_2pow(zip_size));

    buf_pool->stat.n_page_gets++;
    fold = buf_page_address_fold(space, offset);
loop:
    block = guess; //guess 为 NULL
    buf_pool_mutex_enter(buf_pool);

    if (block) {        //这里不进来,因为block为空
        /* If the guess is a compressed page descriptor that
        has been allocated by buf_page_alloc_descriptor(),
        it may have been freed by buf_relocate(). */

        if (!buf_block_is_uncompressed(buf_pool, block)
            || offset != block->page.offset
            || space != block->page.space
            || buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) {

            block = guess = NULL;
        } else {
            ut_ad(!block->page.in_zip_hash);
            ut_ad(block->page.in_page_hash);
        }
    }

    if (block == NULL) {              /**        *详见        *从buf_pool->page_hash中找到相应page        *当找到的page->offset == offset时,返回page        *        */
        block = (buf_block_t*) buf_page_hash_get_low(buf_pool, space, offset, fold);
    }

    if (block && buf_pool_watch_is_sentinel(buf_pool, &block->page)) {
        block = NULL;
    }

    if (block == NULL) {
        /* Page not in buf_pool: needs to be read from file */
        //如果hash表中没有找到block,就读取硬盘文件
        if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) {
            block = (buf_block_t*) buf_pool_watch_set(space, offset, fold);

            if (UNIV_LIKELY_NULL(block)) {

                goto got_block;
            }
        }

        buf_pool_mutex_exit(buf_pool);

        if (mode == BUF_GET_IF_IN_POOL
            || mode == BUF_PEEK_IF_IN_POOL
            || mode == BUF_GET_IF_IN_POOL_OR_WATCH) {

            return(NULL);
        }

        if (buf_read_page(space, zip_size, offset)) {//从硬盘中读取数据,并开始预读,详见
            buf_read_ahead_random(space, zip_size, offset,ibuf_inside(mtr));

            retries = ;
        } else if (retries < BUF_PAGE_READ_MAX_RETRIES) {
            ++retries;
            DBUG_EXECUTE_IF(
                "innodb_page_corruption_retries",
                retries = BUF_PAGE_READ_MAX_RETRIES;
            );
        } else {
            fprintf(stderr, "InnoDB: Error: Unable"
                " to read tablespace %lu page no"
                " %lu into the buffer pool after"
                " %lu attempts\n"
                "InnoDB: The most probable cause"
                " of this error may be that the"
                " table has been corrupted.\n"
                "InnoDB: You can try to fix this"
                " problem by using"
                " innodb_force_recovery.\n"
                "InnoDB: Please see reference manual"
                " for more details.\n"
                "InnoDB: Aborting...\n",
                space, offset,
                BUF_PAGE_READ_MAX_RETRIES);

            ut_error;
        }

#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
        ut_a(++buf_dbg_counter %  || buf_validate());
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
        goto loop;
    }

got_block:
    ut_ad(page_zip_get_size(&block->page.zip) == zip_size);

    must_read = buf_block_get_io_fix(block) == BUF_IO_READ;

    if (must_read && (mode == BUF_GET_IF_IN_POOL
              || mode == BUF_PEEK_IF_IN_POOL)) {

        /* The page is being read to buffer pool,
        but we cannot wait around for the read to
        complete. */
null_exit:
        buf_pool_mutex_exit(buf_pool);

        return(NULL);
    }

    switch (buf_block_get_state(block)) {
        buf_page_t*    bpage;
        ibool        success;

    case BUF_BLOCK_FILE_PAGE:
        break;

    case BUF_BLOCK_ZIP_PAGE:
    case BUF_BLOCK_ZIP_DIRTY:
        if (mode == BUF_PEEK_IF_IN_POOL) {
            /* This mode is only used for dropping an
            adaptive hash index.  There cannot be an
            adaptive hash index for a compressed-only
            page, so do not bother decompressing the page. */
            goto null_exit;
        }

        bpage = &block->page;
        /* Protect bpage->buf_fix_count. */
        mutex_enter(&buf_pool->zip_mutex);

        if (bpage->buf_fix_count
            || buf_page_get_io_fix(bpage) != BUF_IO_NONE) {
            /* This condition often occurs when the buffer
            is not buffer-fixed, but I/O-fixed by
            buf_page_init_for_read(). */
            mutex_exit(&buf_pool->zip_mutex);
wait_until_unfixed:
            /* The block is buffer-fixed or I/O-fixed.
            Try again later. */
            buf_pool_mutex_exit(buf_pool);
            os_thread_sleep(WAIT_FOR_READ);

            goto loop;
        }

        /* Buffer-fix the block so that it cannot be evicted
        or relocated while we are attempting to allocate an
        uncompressed page. */
        bpage->buf_fix_count++;

        /* Allocate an uncompressed page. */
        buf_pool_mutex_exit(buf_pool);
        mutex_exit(&buf_pool->zip_mutex);

        block = buf_LRU_get_free_block(buf_pool);
        ut_a(block);

        buf_pool_mutex_enter(buf_pool);
        mutex_enter(&block->mutex);
        mutex_enter(&buf_pool->zip_mutex);
        /* Buffer-fixing prevents the page_hash from changing. */
        ut_ad(bpage == buf_page_hash_get_low(buf_pool,
                             space, offset, fold));

        if (--bpage->buf_fix_count
            || buf_page_get_io_fix(bpage) != BUF_IO_NONE) {

            mutex_exit(&buf_pool->zip_mutex);
            /* The block was buffer-fixed or I/O-fixed while
            buf_pool->mutex was not held by this thread.
            Free the block that was allocated and retry.
            This should be extremely unlikely, for example,
            if buf_page_get_zip() was invoked. */

            buf_LRU_block_free_non_file_page(block);
            mutex_exit(&block->mutex);

            goto wait_until_unfixed;
        }

        /* Move the compressed page from bpage to block,
        and uncompress it. */

        buf_relocate(bpage, &block->page);
        buf_block_init_low(block);
        block->lock_hash_val = lock_rec_hash(space, offset);

        UNIV_MEM_DESC(&block->page.zip.data,
                  page_zip_get_size(&block->page.zip), block);

        if (buf_page_get_state(&block->page)
            == BUF_BLOCK_ZIP_PAGE) {
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
            UT_LIST_REMOVE(list, buf_pool->zip_clean,
                       &block->page);
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
            ut_ad(!block->page.in_flush_list);
        } else {
            /* Relocate buf_pool->flush_list. */
            buf_flush_relocate_on_flush_list(bpage,
                             &block->page);
        }

        /* Buffer-fix, I/O-fix, and X-latch the block
        for the duration of the decompression.
        Also add the block to the unzip_LRU list. */
        block->page.state = BUF_BLOCK_FILE_PAGE;

        /* Insert at the front of unzip_LRU list */
        buf_unzip_LRU_add_block(block, FALSE);

        block->page.buf_fix_count = ;
        buf_block_set_io_fix(block, BUF_IO_READ);
        rw_lock_x_lock_inline(&block->, file, line);

        UNIV_MEM_INVALID(bpage, sizeof *bpage);

        buf_pool->n_pend_unzip++;
        mutex_exit(&buf_pool->zip_mutex);
        buf_pool_mutex_exit(buf_pool);

        access_time = buf_page_is_accessed(&block->page);
        mutex_exit(&block->mutex);

        buf_page_free_descriptor(bpage);

        /* Decompress the page while not holding
        buf_pool->mutex or block->mutex. */
        success = buf_zip_decompress(block, srv_use_checksums);
        ut_a(success);

        if (UNIV_LIKELY(!recv_no_ibuf_operations)) {
            if (access_time) {
#ifdef UNIV_IBUF_COUNT_DEBUG
                ut_a(ibuf_count_get(space, offset) == );
#endif /* UNIV_IBUF_COUNT_DEBUG */
            } else {
                ibuf_merge_or_delete_for_page(
                    block, space, offset, zip_size, TRUE);
            }
        }

        /* Unfix and unlatch the block. */
        buf_pool_mutex_enter(buf_pool);
        mutex_enter(&block->mutex);
        block->page.buf_fix_count--;
        buf_block_set_io_fix(block, BUF_IO_NONE);
        mutex_exit(&block->mutex);
        buf_pool->n_pend_unzip--;
        rw_lock_x_unlock(&block->lock);

        break;

    case BUF_BLOCK_ZIP_FREE:
    case BUF_BLOCK_NOT_USED:
    case BUF_BLOCK_READY_FOR_USE:
    case BUF_BLOCK_MEMORY:
    case BUF_BLOCK_REMOVE_HASH:
        ut_error;
        break;
    }

    ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE);

    mutex_enter(&block->mutex);
#if UNIV_WORD_SIZE == 4
    /* On 32-bit systems, there is no padding in buf_page_t.  On
    other systems, Valgrind could complain about uninitialized pad
    bytes. */
    UNIV_MEM_ASSERT_RW(&block->page, sizeof block->page);
#endif
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
    if ((mode == BUF_GET_IF_IN_POOL || mode == BUF_GET_IF_IN_POOL_OR_WATCH)
        && ibuf_debug) {
        /* Try to evict the block from the buffer pool, to use the
        insert buffer (change buffer) as much as possible. */

        if (buf_LRU_free_block(&block->page, TRUE)) {
            mutex_exit(&block->mutex);
            if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) {
                /* Set the watch, as it would have
                been set if the page were not in the
                buffer pool in the first place. */
                block = (buf_block_t*) buf_pool_watch_set(
                    space, offset, fold);

                if (UNIV_LIKELY_NULL(block)) {

                    /* The page entered the buffer
                    pool for some reason. Try to
                    evict it again. */
                    goto got_block;
                }
            }
            buf_pool_mutex_exit(buf_pool);
            fprintf(stderr,
                "innodb_change_buffering_debug evict %u %u\n",
                (unsigned) space, (unsigned) offset);
            return(NULL);
        } else if (buf_flush_page_try(buf_pool, block)) {
            fprintf(stderr,
                "innodb_change_buffering_debug flush %u %u\n",
                (unsigned) space, (unsigned) offset);
            guess = block;
            goto loop;
        }

        /* Failed to evict the page; change it directly */
    }
#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */

    buf_block_buf_fix_inc(block, file, line);
#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
    ut_a(mode == BUF_GET_POSSIBLY_FREED
         || !block->page.file_page_was_freed);
#endif
    buf_pool_mutex_exit(buf_pool);

    /* Check if this is the first access to the page */
    access_time = buf_page_is_accessed(&block->page);

    buf_page_set_accessed(&block->page);

    mutex_exit(&block->mutex);

    if (mode != BUF_PEEK_IF_IN_POOL) {
        buf_page_make_young_if_needed(&block->page);
    }

#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
    ut_a(++buf_dbg_counter %  || buf_validate());
    ut_a(block->page.buf_fix_count > );
    ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE);
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */

    switch (rw_latch) {
    case RW_NO_LATCH:
        if (must_read) {
            /* Let us wait until the read operation
            completes */

            for (;;) {
                enum buf_io_fix    io_fix;

                mutex_enter(&block->mutex);
                io_fix = buf_block_get_io_fix(block);
                mutex_exit(&block->mutex);

                if (io_fix == BUF_IO_READ) {
                    /* wait by temporaly s-latch */
                    rw_lock_s_lock(&(block->lock));
                    rw_lock_s_unlock(&(block->lock));
                } else {
                    break;
                }
            }
        }

        fix_type = MTR_MEMO_BUF_FIX;
        break;

    case RW_S_LATCH:
        rw_lock_s_lock_inline(&(block->, file, line);

        fix_type = MTR_MEMO_PAGE_S_FIX;
        break;

    default:
        ut_ad(rw_latch == RW_X_LATCH);
        rw_lock_x_lock_inline(&(block->, file, line);

        fix_type = MTR_MEMO_PAGE_X_FIX;
        break;
    }

    mtr_memo_push(mtr, block, fix_type);

    if (mode != BUF_PEEK_IF_IN_POOL && !access_time) {
        /* In the case of a first access, try to apply linear
        read-ahead */

        buf_read_ahead_linear(space, zip_size, offset,
                      ibuf_inside(mtr));
    }

#ifdef UNIV_IBUF_COUNT_DEBUG
    ut_a(ibuf_count_get(buf_block_get_space(block),
                buf_block_get_page_no(block)) == );
#endif
    return(block);
}