1#include "duckdb/storage/buffer/block_handle.hpp"
2#include "duckdb/storage/block.hpp"
3#include "duckdb/storage/block_manager.hpp"
4#include "duckdb/storage/buffer/buffer_handle.hpp"
5#include "duckdb/storage/buffer_manager.hpp"
6#include "duckdb/storage/buffer/buffer_pool.hpp"
7#include "duckdb/common/file_buffer.hpp"
8
9namespace duckdb {
10
11BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p)
12 : block_manager(block_manager), readers(0), block_id(block_id_p), buffer(nullptr), eviction_timestamp(0),
13 can_destroy(false), memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) {
14 eviction_timestamp = 0;
15 state = BlockState::BLOCK_UNLOADED;
16 memory_usage = Storage::BLOCK_ALLOC_SIZE;
17}
18
19BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, unique_ptr<FileBuffer> buffer_p,
20 bool can_destroy_p, idx_t block_size, BufferPoolReservation &&reservation)
21 : block_manager(block_manager), readers(0), block_id(block_id_p), eviction_timestamp(0), can_destroy(can_destroy_p),
22 memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) {
23 buffer = std::move(buffer_p);
24 state = BlockState::BLOCK_LOADED;
25 memory_usage = block_size;
26 memory_charge = std::move(reservation);
27}
28
29BlockHandle::~BlockHandle() { // NOLINT: allow internal exceptions
30 // being destroyed, so any unswizzled pointers are just binary junk now.
31 unswizzled = nullptr;
32 auto &buffer_manager = block_manager.buffer_manager;
33 // no references remain to this block: erase
34 if (buffer && state == BlockState::BLOCK_LOADED) {
35 D_ASSERT(memory_charge.size > 0);
36 // the block is still loaded in memory: erase it
37 buffer.reset();
38 memory_charge.Resize(new_size: 0);
39 } else {
40 D_ASSERT(memory_charge.size == 0);
41 }
42 buffer_manager.GetBufferPool().PurgeQueue();
43 block_manager.UnregisterBlock(block_id, can_destroy);
44}
45
46unique_ptr<Block> AllocateBlock(BlockManager &block_manager, unique_ptr<FileBuffer> reusable_buffer,
47 block_id_t block_id) {
48 if (reusable_buffer) {
49 // re-usable buffer: re-use it
50 if (reusable_buffer->type == FileBufferType::BLOCK) {
51 // we can reuse the buffer entirely
52 auto &block = reinterpret_cast<Block &>(*reusable_buffer);
53 block.id = block_id;
54 return unique_ptr_cast<FileBuffer, Block>(src: std::move(reusable_buffer));
55 }
56 auto block = block_manager.CreateBlock(block_id, source_buffer: reusable_buffer.get());
57 reusable_buffer.reset();
58 return block;
59 } else {
60 // no re-usable buffer: allocate a new block
61 return block_manager.CreateBlock(block_id, source_buffer: nullptr);
62 }
63}
64
65BufferHandle BlockHandle::Load(shared_ptr<BlockHandle> &handle, unique_ptr<FileBuffer> reusable_buffer) {
66 if (handle->state == BlockState::BLOCK_LOADED) {
67 // already loaded
68 D_ASSERT(handle->buffer);
69 return BufferHandle(handle, handle->buffer.get());
70 }
71
72 auto &block_manager = handle->block_manager;
73 if (handle->block_id < MAXIMUM_BLOCK) {
74 auto block = AllocateBlock(block_manager, reusable_buffer: std::move(reusable_buffer), block_id: handle->block_id);
75 block_manager.Read(block&: *block);
76 handle->buffer = std::move(block);
77 } else {
78 if (handle->can_destroy) {
79 return BufferHandle();
80 } else {
81 handle->buffer =
82 block_manager.buffer_manager.ReadTemporaryBuffer(id: handle->block_id, buffer: std::move(reusable_buffer));
83 }
84 }
85 handle->state = BlockState::BLOCK_LOADED;
86 return BufferHandle(handle, handle->buffer.get());
87}
88
89unique_ptr<FileBuffer> BlockHandle::UnloadAndTakeBlock() {
90 if (state == BlockState::BLOCK_UNLOADED) {
91 // already unloaded: nothing to do
92 return nullptr;
93 }
94 D_ASSERT(!unswizzled);
95 D_ASSERT(CanUnload());
96
97 if (block_id >= MAXIMUM_BLOCK && !can_destroy) {
98 // temporary block that cannot be destroyed: write to temporary file
99 block_manager.buffer_manager.WriteTemporaryBuffer(block_id, buffer&: *buffer);
100 }
101 memory_charge.Resize(new_size: 0);
102 state = BlockState::BLOCK_UNLOADED;
103 return std::move(buffer);
104}
105
106void BlockHandle::Unload() {
107 auto block = UnloadAndTakeBlock();
108 block.reset();
109}
110
111bool BlockHandle::CanUnload() {
112 if (state == BlockState::BLOCK_UNLOADED) {
113 // already unloaded
114 return false;
115 }
116 if (readers > 0) {
117 // there are active readers
118 return false;
119 }
120 if (block_id >= MAXIMUM_BLOCK && !can_destroy && !block_manager.buffer_manager.HasTemporaryDirectory()) {
121 // in order to unload this block we need to write it to a temporary buffer
122 // however, no temporary directory is specified!
123 // hence we cannot unload the block
124 return false;
125 }
126 return true;
127}
128
129} // namespace duckdb
130