frame.h 4.5 KB
Newer Older
羽飞's avatar
羽飞 已提交
1
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
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. */

//
// Created by lianyu on 2022/10/29.
//

#pragma once

#include <pthread.h>
#include <string.h>
#include <string>
#include <mutex>
#include <set>
#include <atomic>

#include "storage/buffer/page.h"
#include "common/log/log.h"
#include "common/lang/mutex.h"
羽飞's avatar
羽飞 已提交
27
#include "common/types.h"
28

羽飞's avatar
羽飞 已提交
29 30 31 32
/**
 * @brief 页帧标识符
 * @ingroup BufferPool
 */
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
class FrameId 
{
public:
  FrameId(int file_desc, PageNum page_num);
  bool    equal_to(const FrameId &other) const;
  bool    operator==(const FrameId &other) const;
  size_t  hash() const;
  int     file_desc() const;
  PageNum page_num() const;

  friend std::string to_string(const FrameId &frame_id);
private:
  int     file_desc_;
  PageNum page_num_;
};

羽飞's avatar
羽飞 已提交
49 50 51
/**
 * @brief 页帧
 * @ingroup BufferPool
羽飞's avatar
羽飞 已提交
52 53 54 55 56 57 58 59
 * @details 页帧是磁盘文件在内存中的表示。磁盘文件按照页面来操作,操作之前先映射到内存中,
 * 将磁盘数据读取到内存中,也就是页帧。
 * 
 * 当某个页面被淘汰时,如果有些内容曾经变更过,那么就需要将这些内容刷新到磁盘上。这里有
 * 一个dirty标识,用来标识页面是否被修改过。
 * 
 * 为了防止在使用过程中页面被淘汰,这里使用了pin count,当页面被使用时,pin count会增加,
 * 当页面不再使用时,pin count会减少。当pin count为0时,页面可以被淘汰。
羽飞's avatar
羽飞 已提交
60
 */
61 62 63
class Frame
{
public:
羽飞's avatar
羽飞 已提交
64 65
  ~Frame()
  {
66
    // LOG_DEBUG("deallocate frame. this=%p, lbt=%s", this, common::lbt());
羽飞's avatar
羽飞 已提交
67 68 69
  }

  /**
羽飞's avatar
羽飞 已提交
70 71
   * @brief reinit 和 reset 在 MemPoolSimple 中使用
   * @details 在 MemPoolSimple 分配和释放一个Frame对象时,不会调用构造函数和析构函数,
羽飞's avatar
羽飞 已提交
72 73 74 75 76 77 78
   * 而是调用reinit和reset。
   */
  void reinit()
  {}
  void reset()
  {}
  
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
  void clear_page()
  {
    memset(&page_, 0, sizeof(page_));
  }

  int     file_desc() const { return file_desc_; }
  void    set_file_desc(int fd) { file_desc_ = fd; }
  Page &  page() { return page_; }
  PageNum page_num() const { return page_.page_num; }
  void    set_page_num(PageNum page_num) { page_.page_num = page_num; }
  FrameId frame_id() const { return FrameId(file_desc_, page_.page_num); }
  LSN     lsn() const { return page_.lsn; }
  void    set_lsn(LSN lsn) { page_.lsn = lsn; }

  /// 刷新访问时间 TODO touch is better?
  void access();

  /**
羽飞's avatar
羽飞 已提交
97
   * @brief 标记指定页面为“脏”页。如果修改了页面的内容,则应调用此函数,
98 99 100 101 102 103 104 105 106 107 108
   * 以便该页面被淘汰出缓冲区时系统将新的页面数据写入磁盘文件
   */
  void mark_dirty() { dirty_ = true; }
  void clear_dirty() { dirty_ = false; }
  bool dirty() const { return dirty_; }

  char *data() { return page_.data; }

  bool can_purge() { return pin_count_.load() == 0; }

  /**
羽飞's avatar
羽飞 已提交
109
   * @brief 给当前页帧增加引用计数
110 111 112 113 114
   * pin通常都会加着frame manager锁来访问
   */
  void pin();

  /**
羽飞's avatar
羽飞 已提交
115
   * @brief 释放一个当前页帧的引用计数
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
   * 与pin对应,但是通常不会加着frame manager的锁来访问
   */
  int  unpin();
  int  pin_count() const { return pin_count_.load(); }

  void write_latch();
  void write_latch(intptr_t xid);

  void write_unlatch();
  void write_unlatch(intptr_t xid);
  
  void read_latch();
  void read_latch(intptr_t xid);
  bool try_read_latch();

  void read_unlatch();
  void read_unlatch(intptr_t xid);

  friend std::string to_string(const Frame &frame);

private:
  friend class  BufferPool;

  bool              dirty_     = false;
  std::atomic<int>  pin_count_{0};
  unsigned long     acc_time_  = 0;
  int               file_desc_ = -1;
  Page              page_;

  /// 在非并发编译时,加锁解锁动作将什么都不做
羽飞's avatar
羽飞 已提交
146
  common::RecursiveSharedMutex     lock_;
147 148 149 150 151

  /// 使用一些手段来做测试,提前检测出头疼的死锁问题
  /// 如果编译时没有增加调试选项,这些代码什么都不做
  common::DebugMutex  debug_lock_;
  intptr_t            write_locker_ = 0;
羽飞's avatar
羽飞 已提交
152 153
  int                 write_recursive_count_ = 0;
  std::unordered_map<intptr_t, int>  read_lockers_;
154 155
};