本文仅用于作业提交
buffer_pool_manager
做到这里的时候,发现代码的封装性很强,很多的函数设置都在其它的文件中,在看懂源码和知晓源码所用函数的函数的基础上进行了修改。
buffer_pool_manager.h
/*
* buffer_pool_manager.h
*
* Functionality: The simplified Buffer Manager interface allows a client to
* new/delete pages on disk, to read a disk page into the buffer pool and pin
* it, also to unpin a page in the buffer pool.
*/
#pragma once
#include <list>
#include <mutex>
#include "buffer/lru_replacer.h"
#include "disk/disk_manager.h"
#include "hash/extendible_hash.h"
#include "logging/log_manager.h"
#include "page/page.h"
namespace scudb {
class BufferPoolManager {
public:
BufferPoolManager(size_t pool_size, DiskManager *disk_manager,
LogManager *log_manager = nullptr);
~BufferPoolManager();
Page *FetchPage(page_id_t page_id);
bool UnpinPage(page_id_t page_id, bool is_dirty);
bool FlushPage(page_id_t page_id);
Page *NewPage(page_id_t &page_id);
bool DeletePage(page_id_t page_id);
private:
size_t sizeofpool; // number of pages in buffer pool
Page *pages_; // array of pages
DiskManager *disk_manager_;
LogManager *log_manager_;
HashTable<page_id_t, Page *> *page_table_; // to keep track of pages
Replacer<Page *> *replacer_; // to find an unpinned page for replacement
std::list<Page *> *free_list_; // to find a free page for replacement
std::mutex latch_; // to protect shared data structure
Page *GetVictimPage();
};
} // namespace cmudb
函数接口:
寻找页面:
Page *FetchPage(page_id_t page_id);
修改pincount字段:
bool UnpinPage(page_id_t page_id, bool is_dirty);
更新页:
bool FlushPage(page_id_t page_id);
需要一个新页:
Page *NewPage(page_id_t &page_id);
删除页:
bool DeletePage(page_id_t page_id);
buffer_pool_manager.cpp
主要的函数如下:
获取页:
首先遍历,如果需要的页存在于页表,那么返回。否则找到需要替代的页,如果需要替代的页被修改,那么先执行写回。之后去除掉需要替代的页,并写入新页。最后修改新页的相关参数,便于后续使用。
//获取页
Page* BufferPoolManager::FetchPage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
//首先遍历,如果有记录则返回
if (page_table_->Find(p_id, tg)) { //1.1
tg->pin_count_++;
replacer_->Erase(tg);
return tg;
}
//1.2
//如果没有找到,那么就找到需要替代的页
tg = GetVictimPage();
if (tg == nullptr) return tg;
//2
//如果需要替换的页已被修改过,那么需要写回
if (tg->is_dirty_) {
disk_manager_->WritePage(tg->GetPageId(), tg->data_);
}
//3
//去除需要去掉的页,在页表中插入需要获取的页
page_table_->Remove(tg->GetPageId());
page_table_->Insert(p_id, tg);
//4
//读取页并设置页的参数,以供后续的算法需要
disk_manager_->ReadPage(p_id, tg->data_);
tg->is_dirty_ = false;
tg->page_id_ = p_id;
tg->pin_count_ = 1;
return tg;
}
修改pincount值:
如果未找到或者目标页的pincount<=0,那么返回0,否则把pincount减一,如果减一后为0,把它放入replacer中。
bool BufferPoolManager::UnpinPage(page_id_t p_id, bool ifdir) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
//如果未找到,返回false
if (tg == nullptr) {
return false;
}
tg->is_dirty_ = ifdir;
//如果pincount<=0,返回false
if (tg->GetPinCount() <= 0) {
return false;
}
//如果减一后,pincout为0,那么把它放入replacer里面
tg->pin_count_--;
if (tg->pin_count_ == 0) {
replacer_->Insert(tg);
}
return true;
}
更新页的isdirty值:
如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换。
//更新页的is_dirty_值,如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换
bool BufferPoolManager::FlushPage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
if (tg == nullptr || tg->page_id_ == INVALID_PAGE_ID) {
return false;
}
if (tg->is_dirty_) {
disk_manager_->WritePage(p_id, tg->GetData());
tg->is_dirty_ = false;
}
return true;
}
删除页:
//删除页
bool BufferPoolManager::DeletePage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
if (tg != nullptr) {
if (tg->GetPinCount() > 0) {//如果目标页的pincount>0,则不应该被删除
return false;
}
//否则删除页
page_table_->Remove(p_id);
replacer_->Erase(tg);
tg->is_dirty_ = false;
tg->ResetMemory();
free_list_->push_back(tg);
}
disk_manager_->DeallocatePage(p_id);
return true;
}
获取新页:
如果free_list中有,那么就使用free_list的空间。否则,采用页的替换算法。
Page* BufferPoolManager::GetVictimPage() {
Page* tg = nullptr;
if (!(free_list_->empty())) {
tg = free_list_->front();
free_list_->pop_front();
if (tg->GetPageId() != INVALID_PAGE_ID)
{
printf("the target id is INVALID_PAGE_ID");
exit(1);
}
}
else {
if (replacer_->Size() == 0) {
return nullptr;
}
replacer_->Victim(tg);
}
if (tg->GetPinCount() != 0)
{
printf("the PinCount is zero!\n");
exit(1);
}
return tg;
}
Page* BufferPoolManager::NewPage(page_id_t& p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
tg = GetVictimPage();
if (tg == nullptr) return tg;
p_id = disk_manager_->AllocatePage();
//2
//如果目标页被修改过
if (tg->is_dirty_) {
disk_manager_->WritePage(tg->GetPageId(), tg->data_);
}
//3
//删除目标页,插入新页
page_table_->Remove(tg->GetPageId());
page_table_->Insert(p_id, tg);
//4
//修改相关参数
tg->page_id_ = p_id;
tg->ResetMemory();
tg->pin_count_ = 1;
tg->is_dirty_ = false;
return tg;
}
完整代码如下:
#include "buffer/buffer_pool_manager.h"
namespace scudb {
/*
* BufferPoolManager Constructor
* When log_manager is nullptr, logging is disabled (for test purpose)
* WARNING: Do Not Edit This Function
*/
BufferPoolManager::BufferPoolManager(size_t pool_size,
DiskManager* disk_manager,
LogManager* log_manager)
: sizeofpool(pool_size), disk_manager_(disk_manager),
log_manager_(log_manager) {
// a consecutive memory space for buffer pool
pages_ = new Page[sizeofpool];
page_table_ = new ExtendibleHash<page_id_t, Page*>(BUCKET_SIZE);
replacer_ = new LRUReplacer<Page*>;
free_list_ = new std::list<Page*>;
// put all the pages into free list
for (size_t i = 0; i < sizeofpool; ++i) {
free_list_->push_back(&pages_[i]);
}
}
/*
* BufferPoolManager Deconstructor
* WARNING: Do Not Edit This Function
*/
BufferPoolManager::~BufferPoolManager() {
delete[] pages_;
delete page_table_;
delete replacer_;
delete free_list_;
}
/**
* 1. search hash table.
* 1.1 if exist, pin the page and return immediately
* 1.2 if no exist, find a replacement entry from either free list or lru
* replacer. (NOTE: always find from free list first)
* 2. If the entry chosen for replacement is dirty, write it back to disk.
* 3. Delete the entry for the old page from the hash table and insert an
* entry for the new page.
* 4. Update page metadata, read page content from disk file and return page
* pointer
*
* This function must mark the Page as pinned and remove its entry from LRUReplacer before it is returned to the caller.
*/
//获取页
Page* BufferPoolManager::FetchPage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
//首先遍历,如果有记录则返回
if (page_table_->Find(p_id, tg)) { //1.1
tg->pin_count_++;
replacer_->Erase(tg);
return tg;
}
//1.2
//如果没有找到,那么就找到需要替代的页
tg = GetVictimPage();
if (tg == nullptr) return tg;
//2
//如果需要替换的页已被修改过,那么需要写回
if (tg->is_dirty_) {
disk_manager_->WritePage(tg->GetPageId(), tg->data_);
}
//3
//去除需要去掉的页,在页表中插入需要获取的页
page_table_->Remove(tg->GetPageId());
page_table_->Insert(p_id, tg);
//4
//读取页并设置页的参数,以供后续的算法需要
disk_manager_->ReadPage(p_id, tg->data_);
tg->is_dirty_ = false;
tg->page_id_ = p_id;
tg->pin_count_ = 1;
return tg;
}
//Page *BufferPoolManager::find
/*
* Implementation of unpin page
* if pin_count>0, decrement it and if it becomes zero, put it back to
* replacer if pin_count<=0 before this call, return false. is_dirty: set the
* dirty flag of this page
*/
bool BufferPoolManager::UnpinPage(page_id_t p_id, bool ifdir) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
//如果未找到,返回false
if (tg == nullptr) {
return false;
}
tg->is_dirty_ = ifdir;
//如果pincount<=0,返回false
if (tg->GetPinCount() <= 0) {
return false;
}
//如果减一后,pincout为0,那么把它放入replacer里面
tg->pin_count_--;
if (tg->pin_count_ == 0) {
replacer_->Insert(tg);
}
return true;
}
/*
* Used to flush a particular page of the buffer pool to disk. Should call the
* write_page method of the disk manager
* if page is not found in page table, return false
* NOTE: make sure page_id != INVALID_PAGE_ID
*/
//更新页的is_dirty_值,如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换
bool BufferPoolManager::FlushPage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
if (tg == nullptr || tg->page_id_ == INVALID_PAGE_ID) {
return false;
}
if (tg->is_dirty_) {
disk_manager_->WritePage(p_id, tg->GetData());
tg->is_dirty_ = false;
}
return true;
}
/**
* User should call this method for deleting a page. This routine will call
* disk manager to deallocate the page. First, if page is found within page
* table, buffer pool manager should be reponsible for removing this entry out
* of page table, reseting page metadata and adding back to free list. Second,
* call disk manager's DeallocatePage() method to delete from disk file. If
* the page is found within page table, but pin_count != 0, return false
*/
//删除页
bool BufferPoolManager::DeletePage(page_id_t p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
page_table_->Find(p_id, tg);
if (tg != nullptr) {
if (tg->GetPinCount() > 0) {//如果目标页的pincount>0,则不应该被删除
return false;
}
//否则删除页
page_table_->Remove(p_id);
replacer_->Erase(tg);
tg->is_dirty_ = false;
tg->ResetMemory();
free_list_->push_back(tg);
}
disk_manager_->DeallocatePage(p_id);
return true;
}
Page* BufferPoolManager::GetVictimPage() {
Page* tg = nullptr;
if (!(free_list_->empty())) {
tg = free_list_->front();
free_list_->pop_front();
if (tg->GetPageId() != INVALID_PAGE_ID)
{
printf("the target id is INVALID_PAGE_ID");
exit(1);
}
}
else {
if (replacer_->Size() == 0) {
return nullptr;
}
replacer_->Victim(tg);
}
if (tg->GetPinCount() != 0)
{
printf("the PinCount is zero!\n");
exit(1);
}
return tg;
}
/**
* User should call this method if needs to create a new page. This routine
* will call disk manager to allocate a page.
* Buffer pool manager should be responsible to choose a victim page either
* from free list or lru replacer(NOTE: always choose from free list first),
* update new page's metadata, zero out memory and add corresponding entry
* into page table. return nullptr if all the pages in pool are pinned
*/
Page* BufferPoolManager::NewPage(page_id_t& p_id) {
//lock_guard<mutex> lck(latch_);
Page* tg = nullptr;
tg = GetVictimPage();
if (tg == nullptr) return tg;
p_id = disk_manager_->AllocatePage();
//2
//如果目标页被修改过
if (tg->is_dirty_) {
disk_manager_->WritePage(tg->GetPageId(), tg->data_);
}
//3
//删除目标页,插入新页
page_table_->Remove(tg->GetPageId());
page_table_->Insert(p_id, tg);
//4
//修改相关参数
tg->page_id_ = p_id;
tg->ResetMemory();
tg->pin_count_ = 1;
tg->is_dirty_ = false;
return tg;
}
} // namespace cmudb
我们完成了所有的代码,测试结果如下:
碰到的问题:
在参考答案中,很多的函数中,都在使用函数之前,进行了如下一句:
lock_guard<mutex> lock(latch);
但我自己写的时候,却并不知道为什么要这样,事实上在一些简单函数中也这样写的:
template <typename K, typename V>//返回全局深度
int ExtendibleHash<K, V>::GetGlobalDepth() const {
//lock_guard<mutex> lock(latch);
return globalDepth;
}
我自己做的时候进行了注释掉,但我不知道为什么标准代码会这样给出。