libfoedus-core
FOEDUS Core Library
masstree_grow_impl.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <glog/logging.h>
21 
22 #include "foedus/assert_nd.hpp"
29 #include "foedus/thread/thread.hpp"
30 
31 namespace foedus {
32 namespace storage {
33 namespace masstree {
34 
35 // See Adopt for what is Case A/B.
37  thread::Thread* context,
38  DualPagePointer* pointer,
39  MasstreePage* cur_root) {
40  ASSERT_ND(cur_root->is_locked());
41  const auto& resolver = context->get_global_volatile_page_resolver();
42  MasstreePage* minor
43  = reinterpret_cast<MasstreePage*>(resolver.resolve_offset(cur_root->get_foster_minor()));
44  MasstreePage* major
45  = reinterpret_cast<MasstreePage*>(resolver.resolve_offset(cur_root->get_foster_major()));
46 
47  ASSERT_ND(minor->get_low_fence() == cur_root->get_low_fence());
48  ASSERT_ND(minor->get_high_fence() == cur_root->get_foster_fence());
49  ASSERT_ND(major->get_low_fence() == cur_root->get_foster_fence());
50  ASSERT_ND(major->get_high_fence() == cur_root->get_high_fence());
51  ASSERT_ND(!minor->header().snapshot_);
52  ASSERT_ND(!major->header().snapshot_);
53  ASSERT_ND(minor->is_empty_range() != major->is_empty_range());
54 
55  VolatilePagePointer nonempty_pointer;
56  MasstreePage* empty_child;
57  if (minor->is_empty_range()) {
58  empty_child = minor;
59  nonempty_pointer = cur_root->get_foster_major();
60  } else {
61  empty_child = major;
62  nonempty_pointer = cur_root->get_foster_minor();
63  }
64 
65  // snapshot pointer does NOT have to be reset. This is still logically the same page.
66  pointer->volatile_pointer_ = nonempty_pointer;
67 
68  // Same as Adopt::adopt_case_a()
69  ASSERT_ND(!empty_child->is_locked());
71  context->collect_retired_volatile_page(empty_child->get_volatile_page_id());
72  cur_root->set_retired();
74 }
75 
77  thread::Thread* context,
78  DualPagePointer* pointer,
79  MasstreePage* cur_root) {
80  ASSERT_ND(cur_root->is_locked());
81  const auto& resolver = context->get_global_volatile_page_resolver();
82 
83  // In this case, we create a new intermediate page.
84  memory::NumaCoreMemory* memory = context->get_thread_memory();
86  if (new_pointer.is_null()) {
88  }
89  MasstreeIntermediatePage* new_root = reinterpret_cast<MasstreeIntermediatePage*>(
90  resolver.resolve_offset_newpage(new_pointer));
91  new_root->initialize_volatile_page(
92  cur_root->header().storage_id_,
93  new_pointer,
94  cur_root->get_layer(),
95  cur_root->get_btree_level() + 1U,
96  kInfimumSlice, // infimum slice
97  kSupremumSlice); // high-fence is supremum
98 
99  new_root->header().key_count_ = 0;
100  auto& mini_page = new_root->get_minipage(0);
101  mini_page.key_count_ = 1;
102  mini_page.pointers_[0].snapshot_pointer_ = 0;
103  mini_page.pointers_[0].volatile_pointer_ = cur_root->get_foster_minor();
104  mini_page.pointers_[1].snapshot_pointer_ = 0;
105  mini_page.pointers_[1].volatile_pointer_ = cur_root->get_foster_major();
106  mini_page.separators_[0] = cur_root->get_foster_fence();
107 
108  // Let's install a pointer to the new root page
109  assorted::memory_fence_release(); // must be after populating the new_root.
110  // snapshot pointer does NOT have to be reset. This is still logically the same page.
111  pointer->volatile_pointer_ = new_pointer;
112 
113  // the old root page is now retired
114  cur_root->set_retired();
116  return kErrorCodeOk;
117 }
118 
121  auto* cb = storage.get_control_block();
122 
123  // Take the special lock in control block.
124  // This locking happens outside of the normal lock/commit protocol.
125  // In order to avoid contention, we take the lock only when it looks available.
126  if (cb->first_root_locked_) {
127  DVLOG(0) << "Interesting. other thread seems growing the first-layer. let him do that";
128  return kErrorCodeOk;
129  }
130  // Always a try-lock to avoid deadlock. Remember, this is a special lock!
131  assorted::DumbSpinlock first_root_lock(&cb->first_root_locked_, false);
132  if (!first_root_lock.try_lock()) {
133  DVLOG(0) << "Interesting. other thread is growing the first-layer. let him do that";
134  return kErrorCodeOk;
135  }
136  ASSERT_ND(first_root_lock.is_locked_by_me());
137 
138  const auto& resolver = context_->get_global_volatile_page_resolver();
139  DualPagePointer* pointer = &cb->root_page_pointer_;
140  ASSERT_ND(!pointer->volatile_pointer_.is_null());
141  Page* cur_root_page = resolver.resolve_offset(pointer->volatile_pointer_);
142  MasstreePage* cur_root = reinterpret_cast<MasstreePage*>(cur_root_page);
143  ASSERT_ND(cur_root->is_layer_root());
144  ASSERT_ND(cur_root->get_layer() == 0);
145 
146  CHECK_ERROR_CODE(context_->sysxct_page_lock(sysxct_workspace, cur_root_page));
147  ASSERT_ND(cur_root->is_locked());
148  // After locking the page, check the state of the page
149  if (!cur_root->is_moved()) {
150  DVLOG(0) << "Interesting. concurrent thread has already grown it.";
151  return kErrorCodeOk;
152  }
153 
154  ASSERT_ND(!cur_root->is_retired());
155  if (cur_root->get_foster_fence() == cur_root->get_low_fence()
156  || cur_root->get_foster_fence() == cur_root->get_high_fence()) {
157  DVLOG(0) << "Adopting Empty-range child for first layer root. storage_id=" << storage_id_;
158  grow_case_a_common(context_, pointer, cur_root);
159  } else {
160  LOG(INFO) << "Growing first layer root. storage_id=" << storage_id_;
161  CHECK_ERROR_CODE(grow_case_b_common(context_, pointer, cur_root));
162  }
163 
164  return kErrorCodeOk;
165 }
166 
170  // Once it becomes a next-layer pointer, it never goes back to a normal record, thus this is safe.
172 
173  auto* record = parent_->get_owner_id(pointer_index_);
174  auto parent_page_id = parent_->get_volatile_page_id();
175  CHECK_ERROR_CODE(context_->sysxct_record_lock(sysxct_workspace, parent_page_id, record));
177 
178  // After locking the record, check the state of the pointer
179  if (record->is_moved()) {
180  DVLOG(0) << "Interesting. concurrent thread has split or is splitting the parent page.";
181  return kErrorCodeOk; // no hurry. root-grow can be delayed
182  }
183  ASSERT_ND(!record->is_deleted());
184  ASSERT_ND(record->is_next_layer());
185 
186  const auto& resolver = context_->get_global_volatile_page_resolver();
188  ASSERT_ND(!pointer->volatile_pointer_.is_null());
189  Page* cur_root_page = resolver.resolve_offset(pointer->volatile_pointer_);
190  MasstreePage* cur_root = reinterpret_cast<MasstreePage*>(cur_root_page);
191  ASSERT_ND(cur_root->is_layer_root());
192 
193  CHECK_ERROR_CODE(context_->sysxct_page_lock(sysxct_workspace, cur_root_page));
194  ASSERT_ND(cur_root->is_locked());
195  // After locking the page, check the state of the page
196  if (!cur_root->is_moved()) {
197  DVLOG(0) << "Interesting. concurrent thread has already grown it.";
198  return kErrorCodeOk;
199  }
200 
201  // But, is_retired is impossible because we locked the parent record before following
202  ASSERT_ND(!cur_root->is_retired());
203  if (cur_root->get_foster_fence() == cur_root->get_low_fence()
204  || cur_root->get_foster_fence() == cur_root->get_high_fence()) {
205  DVLOG(1) << "Easier. Empty-range foster child for non-first layer root."
206  "storage_id=" << parent_->header().storage_id_;
207  grow_case_a_common(context_, pointer, cur_root);
208  } else {
209  DVLOG(0) << "Growing non-first layer root. storage_id=" << parent_->header().storage_id_;
210  CHECK_ERROR_CODE(grow_case_b_common(context_, pointer, cur_root));
211  }
212 
213  return kErrorCodeOk;
214 }
215 
216 } // namespace masstree
217 } // namespace storage
218 } // namespace foedus
const memory::GlobalVolatilePageResolver & get_global_volatile_page_resolver() const
Returns the page resolver to convert page ID to page pointer.
Definition: thread.cpp:125
const KeySlice kInfimumSlice
Represents a pointer to another page (usually a child page).
Definition: storage_id.hpp:271
memory::NumaCoreMemory * get_thread_memory() const
Returns the private memory repository of this thread.
Definition: thread.cpp:57
bool is_empty_range() const __attribute__((always_inline))
An empty-range page, either intermediate or border, never has any entries.
virtual ErrorCode run(xct::SysxctWorkspace *sysxct_workspace) override
Execute the system transaction.
bool is_locked() const __attribute__((always_inline))
thread::Thread *const context_
Thread context.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
bool is_moved() const __attribute__((always_inline))
ErrorCode sysxct_record_lock(xct::SysxctWorkspace *sysxct_workspace, storage::VolatilePagePointer page_id, xct::RwLockableXctId *lock)
Takes a lock for a sysxct running under this thread.
bool is_border() const __attribute__((always_inline))
Represents one thread running on one NUMA core.
Definition: thread.hpp:48
void collect_retired_volatile_page(storage::VolatilePagePointer ptr)
Keeps the specified volatile page as retired as of the current epoch.
Definition: thread.cpp:113
StorageId storage_id_
ID of the storage this page belongs to.
Definition: page.hpp:196
Represents a pointer to a volatile page with modification count for preventing ABA.
Definition: storage_id.hpp:194
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
DualPagePointer * get_next_layer(SlotIndex index) __attribute__((always_inline))
A simple spinlock using a boolean field.
bool is_layer_root() const __attribute__((always_inline))
Common base of MasstreeIntermediatePage and MasstreeBorderPage.
storage::VolatilePagePointer grab_free_volatile_page_pointer()
Wrapper for grab_free_volatile_page().
void grow_case_a_common(thread::Thread *context, DualPagePointer *pointer, MasstreePage *cur_root)
Engine * get_engine() const
Definition: thread.cpp:52
VolatilePagePointer get_foster_minor() const __attribute__((always_inline))
bool is_retired() const __attribute__((always_inline))
VolatilePagePointer get_foster_major() const __attribute__((always_inline))
Repository of memories dynamically acquired within one CPU core (thread).
VolatilePagePointer volatile_pointer_
Definition: storage_id.hpp:308
0 means no-error.
Definition: error_code.hpp:87
uint16_t key_count_
physical key count (those keys might be deleted) in this page.
Definition: page.hpp:219
bool try_lock()
try-version of the lock.
MasstreeBorderPage *const parent_
The border page of the parent layer.
ErrorCode sysxct_page_lock(xct::SysxctWorkspace *sysxct_workspace, storage::Page *page)
Takes a page lock in the same page for a sysxct running under this thread.
ErrorCode grow_case_b_common(thread::Thread *context, DualPagePointer *pointer, MasstreePage *cur_root)
uint8_t get_btree_level() const __attribute__((always_inline))
used only in masstree.
Just a marker to denote that the memory region represents a data page.
Definition: page.hpp:334
uint8_t get_layer() const __attribute__((always_inline))
Layer-0 stores the first 8 byte slice, Layer-1 next 8 byte...
void set_retired() __attribute__((always_inline))
KeySlice get_high_fence() const __attribute__((always_inline))
0x0301 : "MEMORY : Not enough free volatile pages. Check the config of MemoryOptions" ...
Definition: error_code.hpp:142
KeySlice get_low_fence() const __attribute__((always_inline))
const uint16_t pointer_index_
Index of the pointer in parent.
KeySlice get_foster_fence() const __attribute__((always_inline))
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
const PageVersion * get_version_address() const __attribute__((always_inline))
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, uint8_t layer, uint8_t level, KeySlice low_fence, KeySlice high_fence)
VolatilePagePointer get_volatile_page_id() const
PageVersionStatus status_
Definition: page.hpp:172
virtual ErrorCode run(xct::SysxctWorkspace *sysxct_workspace) override
Execute the system transaction.
const KeySlice kSupremumSlice
xct::RwLockableXctId * get_owner_id(SlotIndex index) __attribute__((always_inline))
Represents one intermediate page in Masstree Storage.
bool snapshot_
Whether this page image is of a snapshot page.
Definition: page.hpp:211
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
void memory_fence_release()
Equivalent to std::atomic_thread_fence(std::memory_order_release).
thread::Thread *const context_
Thread context.
CONTROL_BLOCK * get_control_block() const
Definition: attachable.hpp:97
bool does_point_to_layer(SlotIndex index) const __attribute__((always_inline))
StorageId storage_id_
ID of the masstree storage to grow.
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
Per-thread reused work memory for system transactions.
bool is_keylocked() const __attribute__((always_inline))
Definition: xct_id.hpp:1140