libfoedus-core
FOEDUS Core Library
page_pool_pimpl.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 <algorithm>
23 #include <string>
24 
25 #include "foedus/engine.hpp"
35 #include "foedus/thread/thread.hpp"
37 
38 namespace foedus {
39 namespace memory {
41  : control_block_(nullptr),
42  memory_(nullptr),
43  memory_size_(0),
44  owns_(false),
45  rigorous_page_boundary_check_(false) {}
46 
48  PagePoolControlBlock* control_block,
49  void* memory,
50  uint64_t memory_size,
51  bool owns,
52  bool rigorous_page_boundary_check) {
53  control_block_ = control_block;
54  if (owns) {
56  }
57  memory_ = memory;
58  memory_size_ = memory_size;
59  owns_ = owns;
60  rigorous_page_boundary_check_ = rigorous_page_boundary_check;
61  pool_base_ = reinterpret_cast<storage::Page*>(memory_);
63 
64  uint64_t pointers_total_size = pool_size_ * sizeof(PagePoolOffset);
66 
67  free_pool_ = reinterpret_cast<PagePoolOffset*>(memory_);
69  if (rigorous_page_boundary_check) {
71  }
72  resolver_ = LocalPageResolver(pool_base_, pages_for_free_pool_, pool_size_);
73 }
74 
76  if (owns_) {
77  LOG(INFO) << get_debug_pool_name()
78  << " - total_pages=" << pool_size_ << ", pages_for_free_pool_=" << pages_for_free_pool_
79  << ", boundary_check=" << rigorous_page_boundary_check_;
81  LOG(INFO) << get_debug_pool_name() << " - Constructing circular free pool...";
82  // all pages after pages_for_free_pool_-th page is in the free pool at first
83  if (!rigorous_page_boundary_check_) {
85  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
87  }
88  } else {
89  // Use even-numbered pages only
91  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
92  free_pool_[i] = pages_for_free_pool_ + i * 2U;
93  }
94 
95  LOG(INFO) << get_debug_pool_name() << " - mprotect()-ing odd-numbered pages...";
97  bool error_reported = false;
98  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
99  uint64_t index = pages_for_free_pool_ + i * 2U + 1U;
100  static_assert(sizeof(assorted::ProtectedBoundary) == sizeof(storage::Page), "Hoosh!");
101  assorted::ProtectedBoundary* boundary = reinterpret_cast<assorted::ProtectedBoundary*>(
102  pool_base_ + index);
103  boundary->reset(std::to_string(index));
104  ErrorCode mprotect_ret = boundary->acquire_protect();
105  if (mprotect_ret != kErrorCodeOk && !error_reported) {
106  LOG(ERROR) << get_debug_pool_name() << " - mprotect() failed: " << assorted::os_error();
107  error_reported = true;
108  }
109  }
110  watch.stop();
111  LOG(INFO) << get_debug_pool_name() << " - mprotect()-ed " << free_pool_capacity_
112  << " pages in " << watch.elapsed_ms() << "ms";
113  }
114 
115  /* to enable this, we need a version for rigorous_page_boundary_check_
116  // [experimental] randomize the free pool pointers so that we can evenly utilize all
117  // memory banks
118  if (false) { // disabled for now
119  // this should be an option...
120  LOG(INFO) << get_debug_pool_name() << " - Randomizing free pool...";
121  struct Randomizer {
122  PagePoolOffset offset_;
123  uint32_t rank_;
124  static bool compare(const Randomizer& left, const Randomizer& right) {
125  return left.rank_ < right.rank_;
126  }
127  };
128  Randomizer* randomizers = new Randomizer[free_pool_capacity_];
129  assorted::UniformRandom rnd(123456L);
130  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
131  randomizers[i].offset_ = pages_for_free_pool_ + i;
132  randomizers[i].rank_ = rnd.next_uint32();
133  }
134  std::sort(randomizers, randomizers + free_pool_capacity_, Randomizer::compare);
135  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
136  free_pool_[i] = randomizers[i].offset_;
137  }
138  delete[] randomizers;
139  LOG(INFO) << get_debug_pool_name() << " - Randomized free pool.";
140  }
141  */
142 
145  LOG(INFO) << get_debug_pool_name() << " - Constructed circular free pool.";
147  }
148 
149  return kRetOk;
150 }
151 
153  if (owns_) {
156  LOG(INFO) << get_debug_pool_name() << " - releasing mprotect() odd-numbered pages...";
157  debugging::StopWatch watch;
158  bool error_reported = false;
159  for (uint64_t i = 0; i < free_pool_capacity_; ++i) {
160  uint64_t index = pages_for_free_pool_ + i * 2U + 1U;
161  assorted::ProtectedBoundary* boundary = reinterpret_cast<assorted::ProtectedBoundary*>(
162  pool_base_ + index);
163  ErrorCode mprotect_ret = boundary->release_protect();
164  if (mprotect_ret != kErrorCodeOk && !error_reported) {
165  LOG(ERROR) << get_debug_pool_name() << " - mprotect() failed: " << assorted::os_error();
166  error_reported = true;
167  }
168  boundary->assert_boundary();
169  ASSERT_ND(boundary->get_boundary_name() == std::to_string(index));
170  }
171  watch.stop();
172  LOG(INFO) << get_debug_pool_name() << " - released mprotect() " << free_pool_capacity_
173  << " pages in " << watch.elapsed_ms() << "ms";
174  }
175 
176  uint64_t free_count = get_free_pool_count();
177  if (free_count != free_pool_capacity_) {
178  // This is not a memory leak as we anyway releases everything, but it's a smell of bug.
179  LOG(WARNING) << get_debug_pool_name()
180  << " - Page Pool has not received back all free pages by its uninitialization!!"
181  << " count=" << free_count << ", capacity=" << free_pool_capacity_;
182  } else {
183  LOG(INFO) << get_debug_pool_name()
184  << " - Page Pool has received back all free pages. No suspicious behavior.";
185  }
187  }
188  return kRetOk;
189 }
190 
191 ErrorCode PagePoolPimpl::grab(uint32_t desired_grab_count, PagePoolOffsetChunk* chunk) {
192  ASSERT_ND(chunk->size() + desired_grab_count <= chunk->capacity());
193  VLOG(0) << get_debug_pool_name() << " - Grabbing " << desired_grab_count << " pages."
194  << " free_pool_count_=" << get_free_pool_count()
195  << "->"
196  << (get_free_pool_count() >= desired_grab_count
197  ? get_free_pool_count() - desired_grab_count
198  : 0);
200  uint64_t free_count = get_free_pool_count();
201  if (UNLIKELY(free_count == 0)) {
202  LOG(WARNING) << get_debug_pool_name() << " - No more free pages left in the pool";
204  }
205 
206  // grab from the head
208  uint64_t grab_count = std::min<uint64_t>(desired_grab_count, free_count);
210  if (free_pool_head() + grab_count > free_pool_capacity_) {
211  // wrap around
212  uint64_t wrap_count = free_pool_capacity_ - free_pool_head();
213  chunk->push_back(head, head + wrap_count);
214  free_pool_head() = 0;
215  decrease_free_pool_count(wrap_count);
216  grab_count -= wrap_count;
217  head = free_pool_;
218  }
219 
220  // no wrap around (or no more wrap around)
222  ASSERT_ND(free_pool_head() + grab_count <= free_pool_capacity_);
223  chunk->push_back(head, head + grab_count);
224  free_pool_head() += grab_count;
225  decrease_free_pool_count(grab_count);
227  return kErrorCodeOk;
228 }
229 
231  VLOG(1) << get_debug_pool_name()
232  << " - Grabbing just one page. free_pool_count_=" << get_free_pool_count() << "->"
233  << (get_free_pool_count() - 1);
234  *offset = 0;
236  uint64_t free_count = get_free_pool_count();
237  if (UNLIKELY(free_count == 0)) {
238  LOG(WARNING) << get_debug_pool_name() << " - No more free pages left in the pool";
240  }
241 
242  // grab from the head
245  // wrap around
246  free_pool_head() = 0;
247  head = free_pool_;
248  }
249 
250  // no wrap around (or no more wrap around)
252  *offset = *head;
253  ++free_pool_head();
255  return kErrorCodeOk;
256 }
257 
258 template <typename CHUNK>
259 void PagePoolPimpl::release_impl(uint32_t desired_release_count, CHUNK* chunk) {
260  ASSERT_ND(chunk->size() >= desired_release_count);
261  VLOG(0) << get_debug_pool_name() << " - Releasing " << desired_release_count << " pages."
262  << " free_pool_count_=" << get_free_pool_count() << "->"
263  << (get_free_pool_count() + desired_release_count);
265  uint64_t free_count = get_free_pool_count();
266  if (free_count + desired_release_count > free_pool_capacity_) {
267  // this can't happen unless something is wrong! This is a critical issue from which
268  // we can't recover because page pool is inconsistent!
269  LOG(ERROR) << get_debug_pool_name()
270  << " - PagePoolPimpl::release() More than full free-pool. inconsistent state!"
271  << " free_count/capacity/release_count=" << free_count << "/" << free_pool_capacity_
272  << "/" << desired_release_count;
273  // TASK(Hideaki) Do a duplicate-check here to identify the problemetic pages.
274  // crash here only in debug mode. otherwise just log the error
275  // ASSERT_ND(free_count + desired_release_count <= free_pool_capacity_);
276  // TASK(Hideaki) need to figure out why we hit this.
277  return;
278  }
279 
280  // append to the tail
282  uint64_t release_count = std::min<uint64_t>(desired_release_count, chunk->size());
283  uint64_t tail = free_pool_head() + free_count;
284  if (tail >= free_pool_capacity_) {
285  tail -= free_pool_capacity_;
286  }
287  if (tail + release_count > free_pool_capacity_) {
288  // wrap around
289  uint32_t wrap_count = free_pool_capacity_ - tail;
290  chunk->move_to(free_pool_ + tail, wrap_count);
291  increase_free_pool_count(wrap_count);
292  release_count -= wrap_count;
293  tail = 0;
294  }
295 
296  // no wrap around (or no more wrap around)
297  ASSERT_ND(tail + release_count <= free_pool_capacity_);
298  chunk->move_to(free_pool_ + tail, release_count);
299  increase_free_pool_count(release_count);
301 }
302 void PagePoolPimpl::release(uint32_t desired_release_count, PagePoolOffsetChunk* chunk) {
303  release_impl<PagePoolOffsetChunk>(desired_release_count, chunk);
304 }
305 void PagePoolPimpl::release(uint32_t desired_release_count, PagePoolOffsetDynamicChunk* chunk) {
306  release_impl<PagePoolOffsetDynamicChunk>(desired_release_count, chunk);
307 }
308 void PagePoolPimpl::release(uint32_t desired_release_count, PagePoolOffsetAndEpochChunk* chunk) {
309  release_impl<PagePoolOffsetAndEpochChunk>(desired_release_count, chunk);
310 }
311 
314  VLOG(1) << get_debug_pool_name() << " - Releasing just one page. free_pool_count_="
315  << get_free_pool_count() << "->" << (get_free_pool_count() + 1);
317  uint64_t free_count = get_free_pool_count();
318  if (free_count >= free_pool_capacity_) {
319  // this can't happen unless something is wrong! This is a critical issue from which
320  // we can't recover because page pool is inconsistent!
321  LOG(ERROR) << get_debug_pool_name()
322  << " - PagePoolPimpl::release_one() More than full free-pool. inconsistent state!";
324  }
325 
326  // append to the tail
327  uint64_t tail = free_pool_head() + free_count;
328  if (tail >= free_pool_capacity_) {
329  tail -= free_pool_capacity_;
330  }
331  if (tail == free_pool_capacity_) {
332  // wrap around
333  tail = 0;
334  }
335 
336  // no wrap around (or no more wrap around)
337  ASSERT_ND(tail + 1 <= free_pool_capacity_);
338  free_pool_[tail] = offset;
340 }
341 
342 
343 std::ostream& operator<<(std::ostream& o, const PagePoolPimpl& v) {
344  o << "<PagePool>"
345  << "<name_>" << v.get_debug_pool_name() << "</name_>"
346  << "<memory_>" << v.memory_ << "</memory_>"
347  << "<memory_size>" << v.memory_size_ << "</memory_size>"
348  << "<owns_>" << v.owns_ << "</owns_>"
349  << "<rigorous_page_boundary_check_>"
350  << v.rigorous_page_boundary_check_ << "</rigorous_page_boundary_check_>"
351  << "<pages_for_free_pool_>" << v.pages_for_free_pool_ << "</pages_for_free_pool_>"
352  << "<free_pool_capacity_>" << v.free_pool_capacity_ << "</free_pool_capacity_>"
353  << "<free_pool_head_>" << v.free_pool_head() << "</free_pool_head_>"
354  << "<free_pool_count_>" << v.get_free_pool_count() << "</free_pool_count_>"
355  << "</PagePool>";
356  return o;
357 }
358 
360  PagePool::Stat ret;
363  return ret;
364 }
365 
366 } // namespace memory
367 } // namespace foedus
void attach(PagePoolControlBlock *control_block, void *memory, uint64_t memory_size, bool owns, bool rigorous_page_boundary_check)
void increase_free_pool_count(uint64_t op)
A 4kb dummy data placed between separate memory regions so that we can check if/where a bogus memory ...
#define ERROR_STACK(e)
Instantiates ErrorStack with the given foedus::error_code, creating an error stack with the current f...
void reset(const std::string &boundary_name)
Fills the block with magic words.
std::string get_debug_pool_name() const
bool rigorous_page_boundary_check_
Whether to use mprotect() for page boundaries to detect bogus memory accesses.
void * memory_
The whole memory region of the pool.
uint64_t get_free_pool_count() const
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
double elapsed_ms() const
Definition: stop_watch.hpp:48
storage::Page * pool_base_
Just an auxiliary variable to the beginning of the pool.
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
uint64_t free_pool_capacity_
Size of free_pool_.
void release_one(PagePoolOffset offset)
soc::SharedMutex lock_
grab()/release() are protected with this lock.
PagePoolControlBlock * control_block_
ErrorCode grab(uint32_t desired_grab_count, PagePoolOffsetChunk *chunk)
#define COERCE_ERROR(x)
This macro calls x and aborts if encounters an error.
uint64_t pool_size_
Just an auxiliary variable of the size of pool.
PagePool::Stat get_stat() const
void release_impl(uint32_t desired_release_count, CHUNK *chunk)
0 means no-error.
Definition: error_code.hpp:87
ErrorStack initialize_once() override
void decrease_free_pool_count(uint64_t op)
void assert_free_pool() const
Not thread safe.
Pimpl object of PagePool.
0x0302 : "MEMORY : Duplicate entry in free-page pool." .
Definition: error_code.hpp:143
uint64_t stop()
Take another current time tick.
Definition: stop_watch.cpp:35
void release(uint32_t desired_release_count, PagePoolOffsetChunk *chunk)
ErrorStack uninitialize_once() override
Auto-lock scope object for SharedMutex.
Just a marker to denote that the memory region represents a data page.
Definition: page.hpp:334
uint64_t free_pool_head_
Inclusive head of the circular queue.
ErrorCode grab_one(PagePoolOffset *offset)
bool owns_
Whether this engine owns this page pool.
std::ostream & operator<<(std::ostream &o, const AlignedMemory &v)
LocalPageResolver resolver_
An object to resolve an offset in this page pool (thus local) to an actual pointer and vice versa...
0x0301 : "MEMORY : Not enough free volatile pages. Check the config of MemoryOptions" ...
Definition: error_code.hpp:142
Used to point to an already existing array.
Definition: page_pool.hpp:81
uint64_t free_pool_count_
Number of free pages.
Shared data in PagePoolPimpl.
assorted::FixedString< 60 > debug_pool_name_
just for debugging/logging.
std::string os_error()
Thread-safe strerror(errno).
void assert_boundary() const
Called at shutdown to check whether these boundaries were not accessed.
void push_back(PagePoolOffset pointer)
Definition: page_pool.hpp:68
To reduce the overhead of grabbing/releasing pages from pool, we pack this many pointers for each gra...
Definition: page_pool.hpp:47
uint64_t pages_for_free_pool_
This many first pages are used for free page maintainance.
const ErrorStack kRetOk
Normal return value for no-error case.
Resolves an offset in local (same NUMA node) page pool to a pointer and vice versa.
int64_t int_div_ceil(int64_t dividee, int64_t dividor)
Efficient ceil(dividee/dividor) for integer.
void clear() noexcept
Clear string.
Used to store an epoch value with each entry in PagePoolOffsetChunk.
Definition: page_pool.hpp:108
#define UNLIKELY(x)
Hints that x is highly likely false.
Definition: compiler.hpp:104
#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
ErrorCode release_protect()
Removes all access restrictions via mprotect().
A high-resolution stop watch.
Definition: stop_watch.hpp:30
const uint16_t kPageSize
A constant defining the page size (in bytes) of both snapshot pages and volatile pages.
Definition: storage_id.hpp:45
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
ErrorCode acquire_protect()
Puts a strict access prohibition via mprotect().
bool is_initialized() const override final
Returns whether the object has been already initialized or not.
uint64_t memory_size_
Byte size of this page pool.
PagePoolOffset * free_pool_
We maintain free pages as a simple circular queue.