Commit 53f443f9 authored by Titanium's avatar Titanium
Browse files

通过了TestBufferPoolManagerConcurrency

1. 在BufferPoolManager中引入了更细粒度的互斥量
2. 在page.h中将pin_count改为原子量
3. 在CMake配置中加入-fsanitizer=thread

TODO:
1. ThreadSanitizer仍然报pages_上有竞争 可以加入页面级别锁处理
2. flush_all_page时应阻塞所有竞争pages_的操作
Showing with 30 additions and 30 deletions
+30 -30
......@@ -6,6 +6,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 17)
# set(CMAKE_CXX_FLAGS "-Wall -O0 -g -ggdb3 -fsanitize=thread -fsanitize=undefined")
set(CMAKE_CXX_FLAGS "-Wall -O0 -g -ggdb3")
# set(CMAKE_CXX_FLAGS "-Wall -O3")
......
......@@ -14,6 +14,7 @@ See the Mulan PSL v2 for more details. */
* @description: 从free_list或replacer中得到可淘汰帧页的 *frame_id
* @return {bool} true: 可替换帧查找成功 , false: 可替换帧查找失败
* @param {frame_id_t*} frame_id 帧页id指针,返回成功找到的可替换帧id
* @note 涉及临界资源 {free_list_}
*/
bool BufferPoolManager::find_victim_page(frame_id_t* frame_id) {
// Todo:
......@@ -23,6 +24,8 @@ bool BufferPoolManager::find_victim_page(frame_id_t* frame_id) {
// std::scoped_lock lock{latch_};
// 判断是否有free frame,有则直接分配 free frame,并且无需淘汰页面
std::scoped_lock lock{free_list_latch_};
if (!free_list_.empty()) {
*frame_id = free_list_.front();
free_list_.pop_front();
......@@ -38,6 +41,7 @@ bool BufferPoolManager::find_victim_page(frame_id_t* frame_id) {
* @param {Page*} page 写回页指针
* @param {PageId} new_page_id 新的page_id
* @param {frame_id_t} new_frame_id 新的帧frame_id
* @note 涉及临界资源 {page_table_}
*/
void BufferPoolManager::update_page(Page *page, PageId new_page_id, frame_id_t new_frame_id) {
// Todo:
......@@ -47,6 +51,8 @@ void BufferPoolManager::update_page(Page *page, PageId new_page_id, frame_id_t n
// std::scoped_lock lock{latch_};
std::scoped_lock lock{page_table_latch_};
const auto &page_id = page->id_;
// 仅对脏页执行更新数据操作
......@@ -72,6 +78,7 @@ void BufferPoolManager::update_page(Page *page, PageId new_page_id, frame_id_t n
* 如果页表不存在page_id(说明该page在磁盘中),则找缓冲池victim page,将其替换为磁盘中读取的page,pin_count置1。
* @return {Page*} 若获得了需要的页则将其返回,否则返回nullptr
* @param {PageId} page_id 需要获取的页的PageId
* @note 涉及临界资源 {page_table_}
*/
Page* BufferPoolManager::fetch_page(PageId page_id) {
//Todo:
......@@ -86,6 +93,8 @@ Page* BufferPoolManager::fetch_page(PageId page_id) {
// std::scoped_lock lock{latch_};
std::scoped_lock lock{page_table_latch_};
// 尝试在 page table 中查找指定 PageId
const auto& page_table_record = page_table_.find(page_id);
// 如果目标页有被页表记录,则将其所在frame固定,并返回目标页
......@@ -136,6 +145,7 @@ Page* BufferPoolManager::fetch_page(PageId page_id) {
* @return {bool} 如果目标页的pin_count<=0则返回false,否则返回true
* @param {PageId} page_id 目标page的page_id
* @param {bool} is_dirty 若目标page应该被标记为dirty则为true,否则为false
* @note 涉及临界资源 {page_table_}
*/
bool BufferPoolManager::unpin_page(PageId page_id, bool is_dirty) {
// Todo:
......@@ -148,7 +158,9 @@ bool BufferPoolManager::unpin_page(PageId page_id, bool is_dirty) {
// 2.2.1 若自减后等于0,则调用replacer_的Unpin
// 3 根据参数is_dirty,更改P的is_dirty_
std::scoped_lock lock{latch_};
// std::scoped_lock lock{latch_};
std::scoped_lock lock{page_table_latch_};
// 在页表中寻找page_id对应的页P
auto target_page_record = page_table_.find(page_id);
......@@ -184,6 +196,7 @@ bool BufferPoolManager::unpin_page(PageId page_id, bool is_dirty) {
* @description: 将目标页写回磁盘,不考虑当前页面是否正在被使用
* @return {bool} 成功则返回true,否则返回false(只有page_table_中没有目标页时)
* @param {PageId} page_id 目标页的page_id,不能为INVALID_PAGE_ID
* @note 涉及临界资源 {page_table_}
*/
bool BufferPoolManager::flush_page(PageId page_id) {
// Todo:
......@@ -193,7 +206,9 @@ bool BufferPoolManager::flush_page(PageId page_id) {
// 2. 无论P是否为脏都将其写回磁盘。
// 3. 更新P的is_dirty_
std::scoped_lock lock{latch_};
// std::scoped_lock lock{latch_};
std::scoped_lock lock{page_table_latch_};
// 查找页表,尝试获取目标页P缓冲
const auto& target_page_record = page_table_.find(page_id);
......@@ -219,6 +234,7 @@ bool BufferPoolManager::flush_page(PageId page_id) {
* @description: 创建一个新的page,即从磁盘中移动一个新建的空page到缓冲池某个位置。
* @return {Page*} 返回新创建的page,若创建失败则返回nullptr
* @param {PageId*} page_id 当成功创建一个新的page时存储其page_id
* @note 涉及临界资源 {page_table_}
*/
Page* BufferPoolManager::new_page(PageId* page_id) {
// 1. 获得一个可用的frame,若无法获得则返回nullptr
......@@ -228,32 +244,7 @@ Page* BufferPoolManager::new_page(PageId* page_id) {
// 5. 返回获得的page
// std::scoped_lock lock{latch_};
// // 获得一个可用的frame,若无法获得则返回nullptr
// frame_id_t target_frame_id = 0;
// if (!find_victim_page(&target_frame_id)) {
// return nullptr;
// }
// // 在fd对应的文件分配一个新的page_id
// page_id->page_no = disk_manager_->allocate_page(page_id->fd);
// // 将可用 frame 的数据写回磁盘
// auto &victim_page = pages_[target_frame_id];
// auto &victim_page_id = victim_page.id_;
// if (victim_page_id.page_no != INVALID_PAGE_ID) {
// disk_manager_->write_page(victim_page_id.fd, victim_page_id.page_no, victim_page.data_, PAGE_SIZE);
// }
// // 固定 frame,更新 pincount
// replacer_->pin(target_frame_id);
// pages_[target_frame_id].pin_count_ = 1;
// // 加入页表
// page_table_.emplace(*page_id, target_frame_id);
// return &victim_page;
std::scoped_lock lock{page_table_latch_};
// 获得一个可用的frame,若无法获得则返回nullptr
frame_id_t target_frame_id = 0;
if (!find_victim_page(&target_frame_id)) {
......@@ -292,12 +283,14 @@ Page* BufferPoolManager::new_page(PageId* page_id) {
* @description: 从buffer_pool删除目标页
* @return {bool} 如果目标页不存在于buffer_pool或者成功被删除则返回true,若其存在于buffer_pool但无法删除则返回false
* @param {PageId} page_id 目标页
* @note 涉及临界资源 {page_table_, free_list_}
*/
bool BufferPoolManager::delete_page(PageId page_id) {
// 1. 在page_table_中查找目标页,若不存在返回true
// 2. 若目标页的pin_count不为0,则返回false
// 3. 将目标页数据写回磁盘,从页表中删除目标页,重置其元数据,将其加入free_list_,返回true
std::scoped_lock lock{page_table_latch_, free_list_latch_};
// 在页表中查找目标页 若不存在 返回 true
auto target_page_record = page_table_.find(page_id);
if (target_page_record == page_table_.end()) {
......@@ -328,8 +321,11 @@ bool BufferPoolManager::delete_page(PageId page_id) {
/**
* @description: 将buffer_pool中的所有页写回到磁盘
* @param {int} fd 文件句柄
* @note 涉及临界资源 {page_table_, pages_}
*/
void BufferPoolManager::flush_all_pages(int fd) {
// std::scoped_lock lock{latch_};
std::scoped_lock lock{page_table_latch_, pages_latch_};
for (auto &[page_id, frame_id] : page_table_) {
if (page_id.fd != fd)
continue;
......
......@@ -16,6 +16,7 @@ See the Mulan PSL v2 for more details. */
#include <list>
#include <unordered_map>
#include <vector>
#include <array>
#include "disk_manager.h"
#include "errors.h"
......@@ -31,7 +32,9 @@ class BufferPoolManager {
std::list<frame_id_t> free_list_; // 空闲帧编号的链表
DiskManager *disk_manager_;
Replacer *replacer_; // buffer_pool的置换策略,当前赛题中为LRU置换策略
std::mutex latch_; // 用于共享数据结构的并发控制
// std::mutex latch_; // 用于共享数据结构的并发控制
std::mutex page_table_latch_, free_list_latch_, pages_latch_;
// std::array<std::mutex, BUFFER_POOL_SIZE> pages_latch_i_;
public:
BufferPoolManager(size_t pool_size, DiskManager *disk_manager)
......
......@@ -86,5 +86,5 @@ class Page {
bool is_dirty_ = false;
/** The pin count of this page. */
int pin_count_ = 0;
std::atomic<int> pin_count_ = 0;
};
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment