Untitled

 avatar
unknown
c_cpp
a year ago
6.7 kB
6
Indexable
//===----------------------------------------------------------------------===//
//
//                         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