disk_buffer_pool.cpp 19.9 KB
Newer Older
羽飞's avatar
羽飞 已提交
1 2 3 4 5 6 7 8 9 10 11
/* Copyright (c) 2021 Xie Meiyi(xiemeiyi@hust.edu.cn) and OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
12
// Created by Meiyi & Longda on 2021/4/13.
羽飞's avatar
羽飞 已提交
13 14 15 16 17
//
#include "disk_buffer_pool.h"
#include <errno.h>
#include <string.h>

L
Longda 已提交
18
#include "common/lang/mutex.h"
羽飞's avatar
羽飞 已提交
19
#include "common/log/log.h"
L
Longda 已提交
20
#include "common/os/os.h"
羽飞's avatar
羽飞 已提交
21 22 23

using namespace common;

L
Longda 已提交
24 25
int DiskBufferPool::POOL_NUM = MAX_OPEN_FILE / 4;

羽飞's avatar
羽飞 已提交
26 27 28 29 30 31 32
unsigned long current_time()
{
  struct timespec tp;
  clock_gettime(CLOCK_MONOTONIC, &tp);
  return tp.tv_sec * 1000 * 1000 * 1000UL + tp.tv_nsec;
}

L
Longda 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
BPFileHandle::BPFileHandle()
{
  memset((void *)this, 0, sizeof(*this));
}

BPFileHandle::~BPFileHandle()
{
  if (file_name != nullptr) {
    ::free((void *)file_name);
    file_name = nullptr;
  }
}

BPManager::BPManager(const char *name) : MemPoolSimple<Frame>(name)
{}

static bool match_purge(void *item, void *arg)
{
  Frame *frame = (Frame *)item;
  return frame->can_purge();
}

Frame *BPManager::begin_purge()
{
  return MemPoolSimple<Frame>::find(match_purge, nullptr);
}

struct MatchFilePage {
  MatchFilePage(int file_desc, PageNum page_num)
  {
    this->file_desc = file_desc;
    this->page_num = page_num;
  }
  int file_desc;
  PageNum page_num;
};

static bool match_file_page(void *item, void *arg)
{
  Frame *frame = (Frame *)item;
  MatchFilePage *file_page = (MatchFilePage *)arg;

  if (frame->file_desc == file_page->file_desc && frame->page.page_num == file_page->page_num)
    return true;

  return false;
}

Frame *BPManager::get(int file_desc, PageNum page_num)
{
  MatchFilePage file_page(file_desc, page_num);
  return MemPoolSimple<Frame>::find(match_file_page, &file_page);
}

static bool match_file(void *item, void *arg)
{
  Frame *frame = (Frame *)item;
  int *file_desc = (int *)arg;

  if (frame->file_desc == *file_desc)
    return true;

  return false;
}

std::list<Frame *> BPManager::find_list(int file_desc)
{
  return find_all(match_file, &file_desc);
}

羽飞's avatar
羽飞 已提交
103 104
DiskBufferPool *theGlobalDiskBufferPool()
{
L
Longda 已提交
105
  static DiskBufferPool *instance = DiskBufferPool::mk_instance();
羽飞's avatar
羽飞 已提交
106 107 108 109

  return instance;
}

L
Longda 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
DiskBufferPool::DiskBufferPool() : bp_manager_("BPManager")
{
  bp_manager_.init(false, DiskBufferPool::POOL_NUM, BP_BUFFER_SIZE);
};

DiskBufferPool::~DiskBufferPool()
{
  for (int i = 0; i < MAX_OPEN_FILE; i++) {
    BPFileHandle *file_handle = open_list_[i];
    if (file_handle == nullptr) {
      continue;
    }

    close_file(i);
    open_list_[i] = nullptr;
  }

  bp_manager_.cleanup();
  LOG_INFO("Exit");
}

羽飞's avatar
羽飞 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
RC DiskBufferPool::create_file(const char *file_name)
{
  int fd = open(file_name, O_RDWR | O_CREAT | O_EXCL, S_IREAD | S_IWRITE);
  if (fd < 0) {
    LOG_ERROR("Failed to create %s, due to %s.", file_name, strerror(errno));
    return RC::SCHEMA_DB_EXIST;
  }

  close(fd);

  /**
   * Here don't care about the failure
   */
  fd = open(file_name, O_RDWR);
  if (fd < 0) {
    LOG_ERROR("Failed to open for readwrite %s, due to %s.", file_name, strerror(errno));
    return RC::IOERR_ACCESS;
  }

  Page page;
  memset(&page, 0, sizeof(Page));

  BPFileSubHeader *fileSubHeader;
  fileSubHeader = (BPFileSubHeader *)page.data;
  fileSubHeader->allocated_pages = 1;
  fileSubHeader->page_count = 1;

  char *bitmap = page.data + (int)BP_FILE_SUB_HDR_SIZE;
  bitmap[0] |= 0x01;
  if (lseek(fd, 0, SEEK_SET) == -1) {
    LOG_ERROR("Failed to seek file %s to position 0, due to %s .", file_name, strerror(errno));
    close(fd);
    return RC::IOERR_SEEK;
  }

  if (write(fd, (char *)&page, sizeof(Page)) != sizeof(Page)) {
    LOG_ERROR("Failed to write header to file %s, due to %s.", file_name, strerror(errno));
    close(fd);
    return RC::IOERR_WRITE;
  }

  close(fd);
  LOG_INFO("Successfully create %s.", file_name);
  return RC::SUCCESS;
}

RC DiskBufferPool::open_file(const char *file_name, int *file_id)
{
L
Longda 已提交
179
  int fd, i, size = 0, empty_id = -1;
羽飞's avatar
羽飞 已提交
180 181 182
  // This part isn't gentle, the better method is using LRU queue.
  for (i = 0; i < MAX_OPEN_FILE; i++) {
    if (open_list_[i]) {
L
Longda 已提交
183
      size++;
羽飞's avatar
羽飞 已提交
184 185 186 187 188
      if (!strcmp(open_list_[i]->file_name, file_name)) {
        *file_id = i;
        LOG_INFO("%s has already been opened.", file_name);
        return RC::SUCCESS;
      }
L
Longda 已提交
189 190
    } else if (empty_id == -1) {
      empty_id = i;
羽飞's avatar
羽飞 已提交
191 192
    }
  }
L
Longda 已提交
193 194

  i = size;
羽飞's avatar
羽飞 已提交
195
  if (i >= MAX_OPEN_FILE && open_list_[i - 1]) {
L
Longda 已提交
196
    LOG_ERROR("Failed to open file %s, because too much files have been opened.", file_name);
羽飞's avatar
羽飞 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    return RC::BUFFERPOOL_OPEN_TOO_MANY_FILES;
  }

  if ((fd = open(file_name, O_RDWR)) < 0) {
    LOG_ERROR("Failed to open file %s, because %s.", file_name, strerror(errno));
    return RC::IOERR_ACCESS;
  }
  LOG_INFO("Successfully open file %s.", file_name);

  BPFileHandle *file_handle = new (std::nothrow) BPFileHandle();
  if (file_handle == nullptr) {
    LOG_ERROR("Failed to alloc memory of BPFileHandle for %s.", file_name);
    close(fd);
    return RC::NOMEM;
  }

L
Longda 已提交
213
  RC tmp = RC::SUCCESS;
羽飞's avatar
羽飞 已提交
214
  file_handle->bopen = true;
L
Longda 已提交
215
  file_handle->file_name = strdup(file_name);
羽飞's avatar
羽飞 已提交
216
  file_handle->file_desc = fd;
L
Longda 已提交
217
  if ((tmp = allocate_page(&file_handle->hdr_frame)) != RC::SUCCESS) {
羽飞's avatar
羽飞 已提交
218 219 220 221 222 223 224 225
    LOG_ERROR("Failed to allocate block for %s's BPFileHandle.", file_name);
    delete file_handle;
    close(fd);
    return tmp;
  }
  file_handle->hdr_frame->dirty = false;
  file_handle->hdr_frame->file_desc = fd;
  file_handle->hdr_frame->pin_count = 1;
L
Longda 已提交
226
  file_handle->hdr_frame->acc_time = current_time();
羽飞's avatar
羽飞 已提交
227
  if ((tmp = load_page(0, file_handle, file_handle->hdr_frame)) != RC::SUCCESS) {
L
Longda 已提交
228
    LOG_ERROR("Failed to load first page of %s, due to %s.", file_name, strerror(errno));
羽飞's avatar
羽飞 已提交
229
    file_handle->hdr_frame->pin_count = 0;
L
Longda 已提交
230
    purge_page(file_handle->hdr_frame);
羽飞's avatar
羽飞 已提交
231 232 233 234 235 236 237 238
    close(fd);
    delete file_handle;
    return tmp;
  }

  file_handle->hdr_page = &(file_handle->hdr_frame->page);
  file_handle->bitmap = file_handle->hdr_page->data + BP_FILE_SUB_HDR_SIZE;
  file_handle->file_sub_header = (BPFileSubHeader *)file_handle->hdr_page->data;
L
Longda 已提交
239 240 241
  open_list_[empty_id] = file_handle;
  *file_id = empty_id;

羽飞's avatar
羽飞 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255
  LOG_INFO("Successfully open %s. file_id=%d, hdr_frame=%p", file_name, *file_id, file_handle->hdr_frame);
  return RC::SUCCESS;
}

RC DiskBufferPool::close_file(int file_id)
{
  RC tmp;
  if ((tmp = check_file_id(file_id)) != RC::SUCCESS) {
    LOG_ERROR("Failed to close file, due to invalid fileId %d", file_id);
    return tmp;
  }

  BPFileHandle *file_handle = open_list_[file_id];
  file_handle->hdr_frame->pin_count--;
L
Longda 已提交
256
  if ((tmp = purge_all_pages(file_handle)) != RC::SUCCESS) {
羽飞's avatar
羽飞 已提交
257
    file_handle->hdr_frame->pin_count++;
L
Longda 已提交
258
    LOG_ERROR("Failed to close file %d:%s, due to failed to purge all pages.", file_id, file_handle->file_name);
羽飞's avatar
羽飞 已提交
259 260 261
    return tmp;
  }

L
Longda 已提交
262 263
  disposed_pages.erase(file_handle->file_desc);

羽飞's avatar
羽飞 已提交
264 265 266 267 268 269
  if (close(file_handle->file_desc) < 0) {
    LOG_ERROR("Failed to close fileId:%d, fileName:%s, error:%s", file_id, file_handle->file_name, strerror(errno));
    return RC::IOERR_CLOSE;
  }
  open_list_[file_id] = nullptr;
  LOG_INFO("Successfully close file %d:%s.", file_id, file_handle->file_name);
L
Longda 已提交
270
  delete file_handle;
羽飞's avatar
羽飞 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
  return RC::SUCCESS;
}

RC DiskBufferPool::get_this_page(int file_id, PageNum page_num, BPPageHandle *page_handle)
{
  RC tmp;
  if ((tmp = check_file_id(file_id)) != RC::SUCCESS) {
    LOG_ERROR("Failed to load page %d, due to invalid fileId %d", page_num, file_id);
    return tmp;
  }

  BPFileHandle *file_handle = open_list_[file_id];
  if ((tmp = check_page_num(page_num, file_handle)) != RC::SUCCESS) {
    LOG_ERROR("Failed to load page %s:%d, due to invalid pageNum.", file_handle->file_name, page_num);
    return tmp;
  }

L
Longda 已提交
288 289 290 291 292 293
  Frame *used_match_frame = bp_manager_.get(file_handle->file_desc, page_num);
  if (used_match_frame != nullptr) {
    page_handle->frame = used_match_frame;
    page_handle->frame->pin_count++;
    page_handle->frame->acc_time = current_time();
    page_handle->open = true;
羽飞's avatar
羽飞 已提交
294

L
Longda 已提交
295 296 297
    bp_manager_.mark_modified(used_match_frame);

    return RC::SUCCESS;
羽飞's avatar
羽飞 已提交
298 299 300
  }

  // Allocate one page and load the data into this page
L
Longda 已提交
301
  if ((tmp = allocate_page(&(page_handle->frame))) != RC::SUCCESS) {
羽飞's avatar
羽飞 已提交
302 303 304 305 306 307 308 309 310 311
    LOG_ERROR("Failed to load page %s:%d, due to failed to alloc page.", file_handle->file_name, page_num);
    return tmp;
  }
  page_handle->frame->dirty = false;
  page_handle->frame->file_desc = file_handle->file_desc;
  page_handle->frame->pin_count = 1;
  page_handle->frame->acc_time = current_time();
  if ((tmp = load_page(page_num, file_handle, page_handle->frame)) != RC::SUCCESS) {
    LOG_ERROR("Failed to load page %s:%d", file_handle->file_name, page_num);
    page_handle->frame->pin_count = 0;
L
Longda 已提交
312
    purge_page(page_handle->frame);
羽飞's avatar
羽飞 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
    return tmp;
  }

  page_handle->open = true;
  return RC::SUCCESS;
}

RC DiskBufferPool::allocate_page(int file_id, BPPageHandle *page_handle)
{
  RC tmp;
  if ((tmp = check_file_id(file_id)) != RC::SUCCESS) {
    LOG_ERROR("Failed to alloc page, due to invalid fileId %d", file_id);
    return tmp;
  }

  BPFileHandle *file_handle = open_list_[file_id];

  int byte = 0, bit = 0;
  if ((file_handle->file_sub_header->allocated_pages) < (file_handle->file_sub_header->page_count)) {
    // There is one free page
    for (int i = 0; i < file_handle->file_sub_header->page_count; i++) {
      byte = i / 8;
      bit = i % 8;
      if (((file_handle->bitmap[byte]) & (1 << bit)) == 0) {
        (file_handle->file_sub_header->allocated_pages)++;
        file_handle->bitmap[byte] |= (1 << bit);
L
Longda 已提交
339
        // TODO,  do we need clean the loaded page's data?
羽飞's avatar
羽飞 已提交
340 341 342 343 344
        return get_this_page(file_id, i, page_handle);
      }
    }
  }

L
Longda 已提交
345
  if ((tmp = allocate_page(&(page_handle->frame))) != RC::SUCCESS) {
羽飞's avatar
羽飞 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    LOG_ERROR("Failed to allocate page %s, due to no free page.", file_handle->file_name);
    return tmp;
  }

  PageNum page_num = file_handle->file_sub_header->page_count;
  file_handle->file_sub_header->allocated_pages++;
  file_handle->file_sub_header->page_count++;

  byte = page_num / 8;
  bit = page_num % 8;
  file_handle->bitmap[byte] |= (1 << bit);
  file_handle->hdr_frame->dirty = true;

  page_handle->frame->dirty = false;
  page_handle->frame->file_desc = file_handle->file_desc;
  page_handle->frame->pin_count = 1;
  page_handle->frame->acc_time = current_time();
  memset(&(page_handle->frame->page), 0, sizeof(Page));
  page_handle->frame->page.page_num = file_handle->file_sub_header->page_count - 1;

L
Longda 已提交
366 367 368 369 370
  // Use flush operation to extension file
  if ((tmp = flush_page(page_handle->frame)) != RC::SUCCESS) {
    LOG_WARN("Failed to alloc page %s , due to failed to extend one page.", file_handle->file_name);
    // skip return false, delay flush the extended page
    // return tmp;
羽飞's avatar
羽飞 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
  }

  page_handle->open = true;
  return RC::SUCCESS;
}

RC DiskBufferPool::get_page_num(BPPageHandle *page_handle, PageNum *page_num)
{
  if (!page_handle->open)
    return RC::BUFFERPOOL_CLOSED;
  *page_num = page_handle->frame->page.page_num;
  return RC::SUCCESS;
}

RC DiskBufferPool::get_data(BPPageHandle *page_handle, char **data)
{
  if (!page_handle->open)
    return RC::BUFFERPOOL_CLOSED;
  *data = page_handle->frame->page.data;
  return RC::SUCCESS;
}

RC DiskBufferPool::mark_dirty(BPPageHandle *page_handle)
{
  page_handle->frame->dirty = true;
  return RC::SUCCESS;
}

RC DiskBufferPool::unpin_page(BPPageHandle *page_handle)
{
  page_handle->open = false;
L
Longda 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
  if (--page_handle->frame->pin_count == 0) {
    int file_desc = page_handle->frame->file_desc;
    auto it = disposed_pages.find(file_desc);
    if (it != disposed_pages.end()) {
      BPDisposedPages &disposed_page = it->second;
      PageNum page_num = page_handle->frame->page.page_num;
      auto pages_it = disposed_page.pages.find(page_num);
      if (pages_it != disposed_page.pages.end()) {
        LOG_INFO("Dispose file_id:%d, page:%d", disposed_page.file_id, page_num);
        dispose_page(disposed_page.file_id, page_num);
        disposed_page.pages.erase(pages_it);
      }
    }
  }

羽飞's avatar
羽飞 已提交
417 418 419 420
  return RC::SUCCESS;
}

/**
L
Longda 已提交
421 422
 * dispose_page will delete the data of the page of pageNum, free the page both from buffer pool and data file.
 * purge_page will purge the page of pageNum, free the page from buffer pool
羽飞's avatar
羽飞 已提交
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
 * @param fileID
 * @param pageNum
 * @return
 */
RC DiskBufferPool::dispose_page(int file_id, PageNum page_num)
{
  RC rc;
  if ((rc = check_file_id(file_id)) != RC::SUCCESS) {
    LOG_ERROR("Failed to alloc page, due to invalid fileId %d", file_id);
    return rc;
  }

  BPFileHandle *file_handle = open_list_[file_id];
  if ((rc = check_page_num(page_num, file_handle)) != RC::SUCCESS) {
    LOG_ERROR("Failed to dispose page %s:%d, due to invalid pageNum", file_handle->file_name, page_num);
    return rc;
  }

L
Longda 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453
  rc = purge_page(file_handle, page_num);
  if (rc != RC::SUCCESS) {
    LOG_INFO("Dispose page %s:%d later, due to this page is being used", file_handle->file_name, page_num);

    auto it = disposed_pages.find(file_handle->file_desc);
    if (it == disposed_pages.end()) {
      BPDisposedPages disposed_page;
      disposed_page.file_id = file_id;
      disposed_page.pages.insert(page_num);
      disposed_pages.insert(std::pair<int, BPDisposedPages>(file_handle->file_desc, disposed_page));
    } else {
      BPDisposedPages &disposed_page = it->second;
      disposed_page.pages.insert(page_num);
羽飞's avatar
羽飞 已提交
454 455
    }

L
Longda 已提交
456
    return rc;
羽飞's avatar
羽飞 已提交
457 458 459 460 461 462 463 464 465 466
  }

  file_handle->hdr_frame->dirty = true;
  file_handle->file_sub_header->allocated_pages--;
  // file_handle->pFileSubHeader->pageCount--;
  char tmp = 1 << (page_num % 8);
  file_handle->bitmap[page_num / 8] &= ~tmp;
  return RC::SUCCESS;
}

L
Longda 已提交
467
RC DiskBufferPool::purge_page(int file_id, PageNum page_num)
羽飞's avatar
羽飞 已提交
468 469 470 471 472 473 474
{
  RC rc;
  if ((rc = check_file_id(file_id)) != RC::SUCCESS) {
    LOG_ERROR("Failed to alloc page, due to invalid fileId %d", file_id);
    return rc;
  }
  BPFileHandle *file_handle = open_list_[file_id];
L
Longda 已提交
475 476 477 478 479 480
  return purge_page(file_handle, page_num);
}

RC DiskBufferPool::purge_page(Frame *buf)
{
  if (buf->pin_count > 0) {
羽飞's avatar
羽飞 已提交
481
    LOG_INFO("Begin to free page %d of %d(file id), but it's pinned, pin_count:%d.",
L
Longda 已提交
482 483 484 485 486 487 488 489 490
        buf->page.page_num,
        buf->file_desc,
        buf->pin_count);
    return RC::LOCKED_UNLOCK;
  }

  if (buf->dirty) {
    RC rc = flush_page(buf);
    if (rc != RC::SUCCESS) {
羽飞's avatar
羽飞 已提交
491
      LOG_WARN("Failed to flush page %d of %d(file desc) during purge page.", buf->page.page_num, buf->file_desc);
L
Longda 已提交
492 493 494 495
      return rc;
    }
  }

羽飞's avatar
羽飞 已提交
496
  LOG_DEBUG("Successfully purge frame =%p, page %d of %d(file desc)", buf, buf->page.page_num, buf->file_desc);
L
Longda 已提交
497 498
  bp_manager_.free(buf);
  return RC::SUCCESS;
羽飞's avatar
羽飞 已提交
499
}
L
Longda 已提交
500

羽飞's avatar
羽飞 已提交
501 502 503 504 505 506 507
/**
 * dispose_page will delete the data of the page of pageNum
 * force_page will flush the page of pageNum
 * @param fileHandle
 * @param pageNum
 * @return
 */
L
Longda 已提交
508
RC DiskBufferPool::purge_page(BPFileHandle *file_handle, PageNum page_num)
羽飞's avatar
羽飞 已提交
509
{
L
Longda 已提交
510 511 512
  Frame *used_frame = bp_manager_.get(file_handle->file_desc, page_num);
  if (used_frame != nullptr) {
    return purge_page(used_frame);
羽飞's avatar
羽飞 已提交
513
  }
L
Longda 已提交
514

羽飞's avatar
羽飞 已提交
515 516 517
  return RC::SUCCESS;
}

L
Longda 已提交
518
RC DiskBufferPool::purge_all_pages(int file_id)
羽飞's avatar
羽飞 已提交
519 520 521 522 523 524 525 526
{
  RC rc = check_file_id(file_id);
  if (rc != RC::SUCCESS) {
    LOG_ERROR("Failed to flush pages due to invalid file_id %d", file_id);
    return rc;
  }

  BPFileHandle *file_handle = open_list_[file_id];
L
Longda 已提交
527
  return purge_all_pages(file_handle);
羽飞's avatar
羽飞 已提交
528 529
}

L
Longda 已提交
530
RC DiskBufferPool::purge_all_pages(BPFileHandle *file_handle)
羽飞's avatar
羽飞 已提交
531
{
L
Longda 已提交
532 533 534 535
  std::list<Frame *> used = bp_manager_.find_list(file_handle->file_desc);
  for (std::list<Frame *>::iterator it = used.begin(); it != used.end(); ++it) {
    Frame *frame = *it;
    if (frame->pin_count > 0) {
羽飞's avatar
羽飞 已提交
536 537
      LOG_WARN("The page has been pinned, file_desc:%d, pagenum:%d, pin_count=%d",
	       frame->file_desc, frame->page.page_num, frame->pin_count);
羽飞's avatar
羽飞 已提交
538
      continue;
L
Longda 已提交
539 540 541
    }
    if (frame->dirty) {
      RC rc = flush_page(frame);
羽飞's avatar
羽飞 已提交
542 543 544 545 546
      if (rc != RC::SUCCESS) {
        LOG_ERROR("Failed to flush all pages' of %s.", file_handle->file_name);
        return rc;
      }
    }
L
Longda 已提交
547
    bp_manager_.free(frame);
羽飞's avatar
羽飞 已提交
548 549 550 551
  }
  return RC::SUCCESS;
}

羽飞's avatar
羽飞 已提交
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
RC DiskBufferPool::check_all_pages_unpinned(int file_id)
{
  RC rc = check_file_id(file_id);
  if (rc != RC::SUCCESS) {
    LOG_ERROR("Failed to flush pages due to invalid file_id %d", file_id);
    return rc;
  }

  BPFileHandle *file_handle = open_list_[file_id];
  std::list<Frame *> frames = bp_manager_.find_list(file_handle->file_desc);
  for (auto & frame : frames) {
    if (frame->page.page_num == 0 && frame->pin_count > 1) {
      LOG_WARN("This page has been pinned. file id=%d, page num:%d, pin count=%d",
	       file_id, frame->page.page_num, frame->pin_count);
    } else if (frame->page.page_num != 0 && frame->pin_count > 0) {
      LOG_WARN("This page has been pinned. file id=%d, page num:%d, pin count=%d",
	       file_id, frame->page.page_num, frame->pin_count);
    }
  }
  LOG_INFO("all pages have been checked of file id %d", file_id);
  return RC::SUCCESS;
}

L
Longda 已提交
575
RC DiskBufferPool::flush_page(Frame *frame)
羽飞's avatar
羽飞 已提交
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
{
  // The better way is use mmap the block into memory,
  // so it is easier to flush data to file.

  s64_t offset = ((s64_t)frame->page.page_num) * sizeof(Page);
  if (lseek(frame->file_desc, offset, SEEK_SET) == offset - 1) {
    LOG_ERROR("Failed to flush page %lld of %d due to failed to seek %s.", offset, frame->file_desc, strerror(errno));
    return RC::IOERR_SEEK;
  }

  if (write(frame->file_desc, &(frame->page), sizeof(Page)) != sizeof(Page)) {
    LOG_ERROR("Failed to flush page %lld of %d due to %s.", offset, frame->file_desc, strerror(errno));
    return RC::IOERR_WRITE;
  }
  frame->dirty = false;
  LOG_DEBUG("Flush block. file desc=%d, page num=%d", frame->file_desc, frame->page.page_num);

  return RC::SUCCESS;
}

L
Longda 已提交
596
RC DiskBufferPool::allocate_page(Frame **buffer)
羽飞's avatar
羽飞 已提交
597
{
L
Longda 已提交
598 599 600 601
  Frame *frame = bp_manager_.alloc();
  if (frame != nullptr) {
    *buffer = frame;
    return RC::SUCCESS;
羽飞's avatar
羽飞 已提交
602 603
  }

L
Longda 已提交
604 605
  frame = bp_manager_.begin_purge();
  if (frame == nullptr) {
羽飞's avatar
羽飞 已提交
606 607 608 609
    LOG_ERROR("All pages have been used and pinned.");
    return RC::NOMEM;
  }

L
Longda 已提交
610 611
  if (frame->dirty) {
    RC rc = flush_page(frame);
羽飞's avatar
羽飞 已提交
612
    if (rc != RC::SUCCESS) {
L
Longda 已提交
613
      LOG_ERROR("Failed to aclloc block due to failed to flush old block.");
羽飞's avatar
羽飞 已提交
614 615 616 617
      return rc;
    }
  }

L
Longda 已提交
618 619 620
  bp_manager_.mark_modified(frame);

  *buffer = frame;
羽飞's avatar
羽飞 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
  return RC::SUCCESS;
}

RC DiskBufferPool::check_file_id(int file_id)
{
  if (file_id < 0 || file_id >= MAX_OPEN_FILE) {
    LOG_ERROR("Invalid fileId:%d.", file_id);
    return RC::BUFFERPOOL_ILLEGAL_FILE_ID;
  }
  if (!open_list_[file_id]) {
    LOG_ERROR("Invalid fileId:%d, it is empty.", file_id);
    return RC::BUFFERPOOL_ILLEGAL_FILE_ID;
  }
  return RC::SUCCESS;
}

RC DiskBufferPool::get_page_count(int file_id, int *page_count)
{
  RC rc = RC::SUCCESS;
  if ((rc = check_file_id(file_id)) != RC::SUCCESS) {
    return rc;
  }
  *page_count = open_list_[file_id]->file_sub_header->page_count;
  return RC::SUCCESS;
}

RC DiskBufferPool::check_page_num(PageNum page_num, BPFileHandle *file_handle)
{
  if (page_num >= file_handle->file_sub_header->page_count) {
    LOG_ERROR("Invalid pageNum:%d, file's name:%s", page_num, file_handle->file_name);
    return RC::BUFFERPOOL_INVALID_PAGE_NUM;
  }
  if ((file_handle->bitmap[page_num / 8] & (1 << (page_num % 8))) == 0) {
    LOG_ERROR("Invalid pageNum:%d, file's name:%s", page_num, file_handle->file_name);
    return RC::BUFFERPOOL_INVALID_PAGE_NUM;
  }
  return RC::SUCCESS;
}

RC DiskBufferPool::load_page(PageNum page_num, BPFileHandle *file_handle, Frame *frame)
{
  s64_t offset = ((s64_t)page_num) * sizeof(Page);
  if (lseek(file_handle->file_desc, offset, SEEK_SET) == -1) {
    LOG_ERROR(
        "Failed to load page %s:%d, due to failed to lseek:%s.", file_handle->file_name, page_num, strerror(errno));

    return RC::IOERR_SEEK;
  }
  if (read(file_handle->file_desc, &(frame->page), sizeof(Page)) != sizeof(Page)) {
    LOG_ERROR(
        "Failed to load page %s:%d, due to failed to read data:%s.", file_handle->file_name, page_num, strerror(errno));
    return RC::IOERR_READ;
  }
  return RC::SUCCESS;
}