//===----------------------------------------------------------------------===//
//
// BusTub
//
// buffer_pool_manager.cpp
//
// Identification: src/buffer/buffer_pool_manager.cpp
//
// Copyright (c) 2015-2021, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//
#include "buffer/buffer_pool_manager.h"
#include "common/exception.h"
#include "common/macros.h"
#include "storage/disk/disk_scheduler.h"
#include "storage/page/page_guard.h"
namespace bustub {
BufferPoolManager::BufferPoolManager(size_t pool_size, DiskManager *disk_manager, size_t replacer_k,
LogManager *log_manager)
: pool_size_(pool_size), disk_scheduler_(std::make_unique<DiskScheduler>(disk_manager)), log_manager_(log_manager) {
// TODO(students): remove this line after you have implemented the buffer pool manager
// throw NotImplementedException(
// "BufferPoolManager is not implemented yet. If you have finished implementing BPM, please remove the throw "
// "exception line in `buffer_pool_manager.cpp`.");
// we allocate a consecutive memory space for the buffer pool
pages_ = new Page[pool_size_];
replacer_ = std::make_unique<LRUKReplacer>(pool_size, replacer_k);
// Initially, every page is in the free list.
for (size_t i = 0; i < pool_size_; ++i) {
free_list_.emplace_back(static_cast<int>(i));
}
}
BufferPoolManager::~BufferPoolManager() { delete[] pages_; }
auto BufferPoolManager::NewPage(page_id_t *page_id) -> Page * {
std::lock_guard<std::mutex> lock(latch_);
// std::cout << "NewPage" << std::endl;
if (!free_list_.empty()) { // first check free_list_
// std::cout << "Test 1" << std::endl;
frame_id_t frame_id = free_list_.front();
free_list_.pop_front();
*page_id = this->AllocatePage();
Page *new_page = &pages_[frame_id];
new_page->page_id_ = *page_id;
new_page->is_dirty_ = false;
new_page->pin_count_ = 1;
page_table_[*page_id] = frame_id;
replacer_->RecordAccess(frame_id);
replacer_->SetEvictable(frame_id, false);
// std::cout << "NewPage ends" << std::endl;
return new_page;
}
// std::cout << "Test 2" << std::endl;
frame_id_t frame_id;
if (!replacer_->Evict(&frame_id)) { // no page evictable
// std::cout << "NewPage ends" << std::endl;
return nullptr;
}
// std::cout << "Test 3" << std::endl;
Page *old_page = &pages_[frame_id];
if (old_page->IsDirty()) {
// std::cout << "FLUSH!!!" << std::endl;
this->FlushPage(old_page->GetPageId()); // deadlock!!!!!!!!!!
}
// std::cout << "Test 4" << std::endl;
*page_id = this->AllocatePage();
Page *new_page = &pages_[frame_id];
new_page->page_id_ = *page_id;
new_page->is_dirty_ = false;
new_page->pin_count_ = 1;
page_table_.erase(old_page->GetPageId()); // ???????
std::cout << "remove " << old_page->GetPageId() << std::endl;
page_table_[*page_id] = frame_id;
replacer_->RecordAccess(frame_id);
replacer_->SetEvictable(frame_id, false);
// std::cout << "NewPage ends" << std::endl;
return new_page;
}
auto BufferPoolManager::FetchPage(page_id_t page_id, [[maybe_unused]] AccessType access_type) -> Page * {
std::lock_guard<std::mutex> lock(latch_);
if (page_table_.find(page_id) != page_table_.end()) { // already in the buffer pool
std::cout << "already in the buffer pool" << std::endl;
frame_id_t frame_id = page_table_[page_id];
Page *page = &pages_[frame_id];
page->pin_count_++;
replacer_->RecordAccess(frame_id);
replacer_->SetEvictable(frame_id, false);
return page;
}
if (!free_list_.empty()) {
std::cout << "found free list in FetchPage" << std::endl;
// frame_id_t frame_id = free_list_.front();
free_list_.pop_front();
DiskRequest r;
r.is_write_ = false;
r.page_id_ = page_id;
auto promise1 = disk_scheduler_->CreatePromise();
// disk_scheduler_->Schedule(r);
}
std::cout << "cannot find free list in FetchPage" << std::endl;
frame_id_t frame_id;
if (!replacer_->Evict(&frame_id)) { // no page evictable
return nullptr;
}
return nullptr;
}
auto BufferPoolManager::UnpinPage(page_id_t page_id, bool is_dirty, [[maybe_unused]] AccessType access_type) -> bool {
std::lock_guard<std::mutex> lock(latch_);
if (page_table_.find(page_id) == page_table_.end()) {
return false;
}
frame_id_t frame_id = page_table_[page_id];
Page *page = &pages_[frame_id];
if (page->pin_count_ == 0) {
return false;
}
page->pin_count_--;
if (page->pin_count_ == 0) {
replacer_->SetEvictable(frame_id, true);
}
// set the dirty flag on the page to indicate if the page was modified???
page->is_dirty_ = is_dirty;
return true;
}
auto BufferPoolManager::FlushPage(page_id_t page_id) -> bool {
// std::lock_guard<std::mutex> lock(latch_); // ??????
if (page_table_.find(page_id) != page_table_.end()) {
frame_id_t frame_id = page_table_[page_id];
Page *page = &pages_[frame_id];
// Use the DiskManager::WritePage() method to flush a page to disk
disk_scheduler_->disk_manager_->WritePage(page_id, page->GetData());
page->is_dirty_ = false;
return true;
}
return false;
}
void BufferPoolManager::FlushAllPages() {
std::lock_guard<std::mutex> lock(latch_);
for (size_t i = 0; i < pool_size_; i++) {
Page *page = &pages_[i];
// this->FlushPage(page->GetPageId());
disk_scheduler_->disk_manager_->WritePage(page->GetPageId(), page->GetData());
page->is_dirty_ = false;
}
}
auto BufferPoolManager::DeletePage(page_id_t page_id) -> bool {
std::lock_guard<std::mutex> lock(latch_);
if (page_table_.find(page_id) == page_table_.end()) {
return true;
}
frame_id_t frame_id = page_table_[page_id];
Page *page = &pages_[frame_id];
if (page->pin_count_ > 0) {
return false;
}
page_table_.erase(page_id);
replacer_->Remove(frame_id);
free_list_.push_back(frame_id);
page->is_dirty_ = false;
page->pin_count_ = 0;
page->ResetMemory();
// Call DeallocatePage() ?????
DeallocatePage(page_id);
return true;
}
auto BufferPoolManager::AllocatePage() -> page_id_t { return next_page_id_++; }
auto BufferPoolManager::FetchPageBasic(page_id_t page_id) -> BasicPageGuard { return {this, nullptr}; }
auto BufferPoolManager::FetchPageRead(page_id_t page_id) -> ReadPageGuard { return {this, nullptr}; }
auto BufferPoolManager::FetchPageWrite(page_id_t page_id) -> WritePageGuard { return {this, nullptr}; }
auto BufferPoolManager::NewPageGuarded(page_id_t *page_id) -> BasicPageGuard { return {this, nullptr}; }
} // namespace bustub