Home | History | Annotate | Download | only in metrics
      1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/metrics/persistent_memory_allocator.h"
      6 
      7 #include <assert.h>
      8 #include <algorithm>
      9 
     10 #if defined(OS_WIN)
     11 #include "winbase.h"
     12 #elif defined(OS_POSIX)
     13 #include <sys/mman.h>
     14 #endif
     15 
     16 #include "base/files/memory_mapped_file.h"
     17 #include "base/logging.h"
     18 #include "base/memory/shared_memory.h"
     19 #include "base/metrics/histogram_macros.h"
     20 
     21 namespace {
     22 
     23 // Limit of memory segment size. It has to fit in an unsigned 32-bit number
     24 // and should be a power of 2 in order to accomodate almost any page size.
     25 const uint32_t kSegmentMaxSize = 1 << 30;  // 1 GiB
     26 
     27 // A constant (random) value placed in the shared metadata to identify
     28 // an already initialized memory segment.
     29 const uint32_t kGlobalCookie = 0x408305DC;
     30 
     31 // The current version of the metadata. If updates are made that change
     32 // the metadata, the version number can be queried to operate in a backward-
     33 // compatible manner until the memory segment is completely re-initalized.
     34 const uint32_t kGlobalVersion = 1;
     35 
     36 // Constant values placed in the block headers to indicate its state.
     37 const uint32_t kBlockCookieFree = 0;
     38 const uint32_t kBlockCookieQueue = 1;
     39 const uint32_t kBlockCookieWasted = (uint32_t)-1;
     40 const uint32_t kBlockCookieAllocated = 0xC8799269;
     41 
     42 // TODO(bcwhite): When acceptable, consider moving flags to std::atomic<char>
     43 // types rather than combined bitfield.
     44 
     45 // Flags stored in the flags_ field of the SharedMetaData structure below.
     46 enum : int {
     47   kFlagCorrupt = 1 << 0,
     48   kFlagFull    = 1 << 1
     49 };
     50 
     51 bool CheckFlag(const volatile std::atomic<uint32_t>* flags, int flag) {
     52   uint32_t loaded_flags = flags->load(std::memory_order_relaxed);
     53   return (loaded_flags & flag) != 0;
     54 }
     55 
     56 void SetFlag(volatile std::atomic<uint32_t>* flags, int flag) {
     57   uint32_t loaded_flags = flags->load(std::memory_order_relaxed);
     58   for (;;) {
     59     uint32_t new_flags = (loaded_flags & ~flag) | flag;
     60     // In the failue case, actual "flags" value stored in loaded_flags.
     61     if (flags->compare_exchange_weak(loaded_flags, new_flags))
     62       break;
     63   }
     64 }
     65 
     66 }  // namespace
     67 
     68 namespace base {
     69 
     70 // All allocations and data-structures must be aligned to this byte boundary.
     71 // Alignment as large as the physical bus between CPU and RAM is _required_
     72 // for some architectures, is simply more efficient on other CPUs, and
     73 // generally a Good Idea(tm) for all platforms as it reduces/eliminates the
     74 // chance that a type will span cache lines. Alignment mustn't be less
     75 // than 8 to ensure proper alignment for all types. The rest is a balance
     76 // between reducing spans across multiple cache lines and wasted space spent
     77 // padding out allocations. An alignment of 16 would ensure that the block
     78 // header structure always sits in a single cache line. An average of about
     79 // 1/2 this value will be wasted with every allocation.
     80 const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8;
     81 
     82 // The block-header is placed at the top of every allocation within the
     83 // segment to describe the data that follows it.
     84 struct PersistentMemoryAllocator::BlockHeader {
     85   uint32_t size;       // Number of bytes in this block, including header.
     86   uint32_t cookie;     // Constant value indicating completed allocation.
     87   std::atomic<uint32_t> type_id;  // Arbitrary number indicating data type.
     88   std::atomic<uint32_t> next;     // Pointer to the next block when iterating.
     89 };
     90 
     91 // The shared metadata exists once at the top of the memory segment to
     92 // describe the state of the allocator to all processes.
     93 struct PersistentMemoryAllocator::SharedMetadata {
     94   uint32_t cookie;     // Some value that indicates complete initialization.
     95   uint32_t size;       // Total size of memory segment.
     96   uint32_t page_size;  // Paging size within memory segment.
     97   uint32_t version;    // Version code so upgrades don't break.
     98   uint64_t id;         // Arbitrary ID number given by creator.
     99   uint32_t name;       // Reference to stored name string.
    100 
    101   // Above is read-only after first construction. Below may be changed and
    102   // so must be marked "volatile" to provide correct inter-process behavior.
    103 
    104   // Bitfield of information flags. Access to this should be done through
    105   // the CheckFlag() and SetFlag() methods defined above.
    106   volatile std::atomic<uint32_t> flags;
    107 
    108   // Offset/reference to first free space in segment.
    109   volatile std::atomic<uint32_t> freeptr;
    110 
    111   // The "iterable" queue is an M&S Queue as described here, append-only:
    112   // https://www.research.ibm.com/people/m/michael/podc-1996.pdf
    113   volatile std::atomic<uint32_t> tailptr;  // Last block of iteration queue.
    114   volatile BlockHeader queue;   // Empty block for linked-list head/tail.
    115 };
    116 
    117 // The "queue" block header is used to detect "last node" so that zero/null
    118 // can be used to indicate that it hasn't been added at all. It is part of
    119 // the SharedMetadata structure which itself is always located at offset zero.
    120 const PersistentMemoryAllocator::Reference
    121     PersistentMemoryAllocator::kReferenceQueue =
    122         offsetof(SharedMetadata, queue);
    123 
    124 const base::FilePath::CharType PersistentMemoryAllocator::kFileExtension[] =
    125     FILE_PATH_LITERAL(".pma");
    126 
    127 
    128 PersistentMemoryAllocator::Iterator::Iterator(
    129     const PersistentMemoryAllocator* allocator)
    130     : allocator_(allocator), last_record_(kReferenceQueue), record_count_(0) {}
    131 
    132 PersistentMemoryAllocator::Iterator::Iterator(
    133     const PersistentMemoryAllocator* allocator,
    134     Reference starting_after)
    135     : allocator_(allocator), last_record_(starting_after), record_count_(0) {
    136   // Ensure that the starting point is a valid, iterable block (meaning it can
    137   // be read and has a non-zero "next" pointer).
    138   const volatile BlockHeader* block =
    139       allocator_->GetBlock(starting_after, 0, 0, false, false);
    140   if (!block || block->next.load(std::memory_order_relaxed) == 0) {
    141     NOTREACHED();
    142     last_record_.store(kReferenceQueue, std::memory_order_release);
    143   }
    144 }
    145 
    146 PersistentMemoryAllocator::Reference
    147 PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) {
    148   // Make a copy of the existing count of found-records, acquiring all changes
    149   // made to the allocator, notably "freeptr" (see comment in loop for why
    150   // the load of that value cannot be moved above here) that occurred during
    151   // any previous runs of this method, including those by parallel threads
    152   // that interrupted it. It pairs with the Release at the end of this method.
    153   //
    154   // Otherwise, if the compiler were to arrange the two loads such that
    155   // "count" was fetched _after_ "freeptr" then it would be possible for
    156   // this thread to be interrupted between them and other threads perform
    157   // multiple allocations, make-iterables, and iterations (with the included
    158   // increment of |record_count_|) culminating in the check at the bottom
    159   // mistakenly determining that a loop exists. Isn't this stuff fun?
    160   uint32_t count = record_count_.load(std::memory_order_acquire);
    161 
    162   Reference last = last_record_.load(std::memory_order_acquire);
    163   Reference next;
    164   while (true) {
    165     const volatile BlockHeader* block =
    166         allocator_->GetBlock(last, 0, 0, true, false);
    167     if (!block)  // Invalid iterator state.
    168       return kReferenceNull;
    169 
    170     // The compiler and CPU can freely reorder all memory accesses on which
    171     // there are no dependencies. It could, for example, move the load of
    172     // "freeptr" to above this point because there are no explicit dependencies
    173     // between it and "next". If it did, however, then another block could
    174     // be queued after that but before the following load meaning there is
    175     // one more queued block than the future "detect loop by having more
    176     // blocks that could fit before freeptr" will allow.
    177     //
    178     // By "acquiring" the "next" value here, it's synchronized to the enqueue
    179     // of the node which in turn is synchronized to the allocation (which sets
    180     // freeptr). Thus, the scenario above cannot happen.
    181     next = block->next.load(std::memory_order_acquire);
    182     if (next == kReferenceQueue)  // No next allocation in queue.
    183       return kReferenceNull;
    184     block = allocator_->GetBlock(next, 0, 0, false, false);
    185     if (!block) {  // Memory is corrupt.
    186       allocator_->SetCorrupt();
    187       return kReferenceNull;
    188     }
    189 
    190     // Update the "last_record" pointer to be the reference being returned.
    191     // If it fails then another thread has already iterated past it so loop
    192     // again. Failing will also load the existing value into "last" so there
    193     // is no need to do another such load when the while-loop restarts. A
    194     // "strong" compare-exchange is used because failing unnecessarily would
    195     // mean repeating some fairly costly validations above.
    196     if (last_record_.compare_exchange_strong(last, next)) {
    197       *type_return = block->type_id.load(std::memory_order_relaxed);
    198       break;
    199     }
    200   }
    201 
    202   // Memory corruption could cause a loop in the list. Such must be detected
    203   // so as to not cause an infinite loop in the caller. This is done by simply
    204   // making sure it doesn't iterate more times than the absolute maximum
    205   // number of allocations that could have been made. Callers are likely
    206   // to loop multiple times before it is detected but at least it stops.
    207   const uint32_t freeptr = std::min(
    208       allocator_->shared_meta()->freeptr.load(std::memory_order_relaxed),
    209       allocator_->mem_size_);
    210   const uint32_t max_records =
    211       freeptr / (sizeof(BlockHeader) + kAllocAlignment);
    212   if (count > max_records) {
    213     allocator_->SetCorrupt();
    214     return kReferenceNull;
    215   }
    216 
    217   // Increment the count and release the changes made above. It pairs with
    218   // the Acquire at the top of this method. Note that this operation is not
    219   // strictly synchonized with fetching of the object to return, which would
    220   // have to be done inside the loop and is somewhat complicated to achieve.
    221   // It does not matter if it falls behind temporarily so long as it never
    222   // gets ahead.
    223   record_count_.fetch_add(1, std::memory_order_release);
    224   return next;
    225 }
    226 
    227 PersistentMemoryAllocator::Reference
    228 PersistentMemoryAllocator::Iterator::GetNextOfType(uint32_t type_match) {
    229   Reference ref;
    230   uint32_t type_found;
    231   while ((ref = GetNext(&type_found)) != 0) {
    232     if (type_found == type_match)
    233       return ref;
    234   }
    235   return kReferenceNull;
    236 }
    237 
    238 
    239 // static
    240 bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base,
    241                                                    size_t size,
    242                                                    size_t page_size,
    243                                                    bool readonly) {
    244   return ((base && reinterpret_cast<uintptr_t>(base) % kAllocAlignment == 0) &&
    245           (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) &&
    246           (size % kAllocAlignment == 0 || readonly) &&
    247           (page_size == 0 || size % page_size == 0 || readonly));
    248 }
    249 
    250 PersistentMemoryAllocator::PersistentMemoryAllocator(
    251     void* base,
    252     size_t size,
    253     size_t page_size,
    254     uint64_t id,
    255     base::StringPiece name,
    256     bool readonly)
    257     : mem_base_(static_cast<char*>(base)),
    258       mem_size_(static_cast<uint32_t>(size)),
    259       mem_page_(static_cast<uint32_t>((page_size ? page_size : size))),
    260       readonly_(readonly),
    261       corrupt_(0),
    262       allocs_histogram_(nullptr),
    263       used_histogram_(nullptr) {
    264   static_assert(sizeof(BlockHeader) % kAllocAlignment == 0,
    265                 "BlockHeader is not a multiple of kAllocAlignment");
    266   static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0,
    267                 "SharedMetadata is not a multiple of kAllocAlignment");
    268   static_assert(kReferenceQueue % kAllocAlignment == 0,
    269                 "\"queue\" is not aligned properly; must be at end of struct");
    270 
    271   // Ensure that memory segment is of acceptable size.
    272   CHECK(IsMemoryAcceptable(base, size, page_size, readonly));
    273 
    274   // These atomics operate inter-process and so must be lock-free. The local
    275   // casts are to make sure it can be evaluated at compile time to a constant.
    276   CHECK(((SharedMetadata*)0)->freeptr.is_lock_free());
    277   CHECK(((SharedMetadata*)0)->flags.is_lock_free());
    278   CHECK(((BlockHeader*)0)->next.is_lock_free());
    279   CHECK(corrupt_.is_lock_free());
    280 
    281   if (shared_meta()->cookie != kGlobalCookie) {
    282     if (readonly) {
    283       SetCorrupt();
    284       return;
    285     }
    286 
    287     // This block is only executed when a completely new memory segment is
    288     // being initialized. It's unshared and single-threaded...
    289     volatile BlockHeader* const first_block =
    290         reinterpret_cast<volatile BlockHeader*>(mem_base_ +
    291                                                 sizeof(SharedMetadata));
    292     if (shared_meta()->cookie != 0 ||
    293         shared_meta()->size != 0 ||
    294         shared_meta()->version != 0 ||
    295         shared_meta()->freeptr.load(std::memory_order_relaxed) != 0 ||
    296         shared_meta()->flags.load(std::memory_order_relaxed) != 0 ||
    297         shared_meta()->id != 0 ||
    298         shared_meta()->name != 0 ||
    299         shared_meta()->tailptr != 0 ||
    300         shared_meta()->queue.cookie != 0 ||
    301         shared_meta()->queue.next.load(std::memory_order_relaxed) != 0 ||
    302         first_block->size != 0 ||
    303         first_block->cookie != 0 ||
    304         first_block->type_id.load(std::memory_order_relaxed) != 0 ||
    305         first_block->next != 0) {
    306       // ...or something malicious has been playing with the metadata.
    307       SetCorrupt();
    308     }
    309 
    310     // This is still safe to do even if corruption has been detected.
    311     shared_meta()->cookie = kGlobalCookie;
    312     shared_meta()->size = mem_size_;
    313     shared_meta()->page_size = mem_page_;
    314     shared_meta()->version = kGlobalVersion;
    315     shared_meta()->id = id;
    316     shared_meta()->freeptr.store(sizeof(SharedMetadata),
    317                                  std::memory_order_release);
    318 
    319     // Set up the queue of iterable allocations.
    320     shared_meta()->queue.size = sizeof(BlockHeader);
    321     shared_meta()->queue.cookie = kBlockCookieQueue;
    322     shared_meta()->queue.next.store(kReferenceQueue, std::memory_order_release);
    323     shared_meta()->tailptr.store(kReferenceQueue, std::memory_order_release);
    324 
    325     // Allocate space for the name so other processes can learn it.
    326     if (!name.empty()) {
    327       const size_t name_length = name.length() + 1;
    328       shared_meta()->name = Allocate(name_length, 0);
    329       char* name_cstr = GetAsObject<char>(shared_meta()->name, 0);
    330       if (name_cstr)
    331         memcpy(name_cstr, name.data(), name.length());
    332     }
    333   } else {
    334     if (shared_meta()->size == 0 ||
    335         shared_meta()->version == 0 ||
    336         shared_meta()->freeptr.load(std::memory_order_relaxed) == 0 ||
    337         shared_meta()->tailptr == 0 ||
    338         shared_meta()->queue.cookie == 0 ||
    339         shared_meta()->queue.next.load(std::memory_order_relaxed) == 0) {
    340       SetCorrupt();
    341     }
    342     if (!readonly) {
    343       // The allocator is attaching to a previously initialized segment of
    344       // memory. If the initialization parameters differ, make the best of it
    345       // by reducing the local construction parameters to match those of
    346       // the actual memory area. This ensures that the local object never
    347       // tries to write outside of the original bounds.
    348       // Because the fields are const to ensure that no code other than the
    349       // constructor makes changes to them as well as to give optimization
    350       // hints to the compiler, it's necessary to const-cast them for changes
    351       // here.
    352       if (shared_meta()->size < mem_size_)
    353         *const_cast<uint32_t*>(&mem_size_) = shared_meta()->size;
    354       if (shared_meta()->page_size < mem_page_)
    355         *const_cast<uint32_t*>(&mem_page_) = shared_meta()->page_size;
    356 
    357       // Ensure that settings are still valid after the above adjustments.
    358       if (!IsMemoryAcceptable(base, mem_size_, mem_page_, readonly))
    359         SetCorrupt();
    360     }
    361   }
    362 }
    363 
    364 PersistentMemoryAllocator::~PersistentMemoryAllocator() {
    365   // It's strictly forbidden to do any memory access here in case there is
    366   // some issue with the underlying memory segment. The "Local" allocator
    367   // makes use of this to allow deletion of the segment on the heap from
    368   // within its destructor.
    369 }
    370 
    371 uint64_t PersistentMemoryAllocator::Id() const {
    372   return shared_meta()->id;
    373 }
    374 
    375 const char* PersistentMemoryAllocator::Name() const {
    376   Reference name_ref = shared_meta()->name;
    377   const char* name_cstr = GetAsObject<char>(name_ref, 0);
    378   if (!name_cstr)
    379     return "";
    380 
    381   size_t name_length = GetAllocSize(name_ref);
    382   if (name_cstr[name_length - 1] != '\0') {
    383     NOTREACHED();
    384     SetCorrupt();
    385     return "";
    386   }
    387 
    388   return name_cstr;
    389 }
    390 
    391 void PersistentMemoryAllocator::CreateTrackingHistograms(
    392     base::StringPiece name) {
    393   if (name.empty() || readonly_)
    394     return;
    395 
    396   std::string name_string = name.as_string();
    397   DCHECK(!used_histogram_);
    398   used_histogram_ = LinearHistogram::FactoryGet(
    399       "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21,
    400       HistogramBase::kUmaTargetedHistogramFlag);
    401 
    402   DCHECK(!allocs_histogram_);
    403   allocs_histogram_ = Histogram::FactoryGet(
    404       "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50,
    405       HistogramBase::kUmaTargetedHistogramFlag);
    406 }
    407 
    408 size_t PersistentMemoryAllocator::used() const {
    409   return std::min(shared_meta()->freeptr.load(std::memory_order_relaxed),
    410                   mem_size_);
    411 }
    412 
    413 size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const {
    414   const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
    415   if (!block)
    416     return 0;
    417   uint32_t size = block->size;
    418   // Header was verified by GetBlock() but a malicious actor could change
    419   // the value between there and here. Check it again.
    420   if (size <= sizeof(BlockHeader) || ref + size > mem_size_) {
    421     SetCorrupt();
    422     return 0;
    423   }
    424   return size - sizeof(BlockHeader);
    425 }
    426 
    427 uint32_t PersistentMemoryAllocator::GetType(Reference ref) const {
    428   const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
    429   if (!block)
    430     return 0;
    431   return block->type_id.load(std::memory_order_relaxed);
    432 }
    433 
    434 bool PersistentMemoryAllocator::ChangeType(Reference ref,
    435                                            uint32_t to_type_id,
    436                                            uint32_t from_type_id) {
    437   DCHECK(!readonly_);
    438   volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
    439   if (!block)
    440     return false;
    441 
    442   // This is a "strong" exchange because there is no loop that can retry in
    443   // the wake of spurious failures possible with "weak" exchanges.
    444   return block->type_id.compare_exchange_strong(from_type_id, to_type_id);
    445 }
    446 
    447 PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate(
    448     size_t req_size,
    449     uint32_t type_id) {
    450   Reference ref = AllocateImpl(req_size, type_id);
    451   if (ref) {
    452     // Success: Record this allocation in usage stats (if active).
    453     if (allocs_histogram_)
    454       allocs_histogram_->Add(static_cast<HistogramBase::Sample>(req_size));
    455   } else {
    456     // Failure: Record an allocation of zero for tracking.
    457     if (allocs_histogram_)
    458       allocs_histogram_->Add(0);
    459   }
    460   return ref;
    461 }
    462 
    463 PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl(
    464     size_t req_size,
    465     uint32_t type_id) {
    466   DCHECK(!readonly_);
    467 
    468   // Validate req_size to ensure it won't overflow when used as 32-bit value.
    469   if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) {
    470     NOTREACHED();
    471     return kReferenceNull;
    472   }
    473 
    474   // Round up the requested size, plus header, to the next allocation alignment.
    475   uint32_t size = static_cast<uint32_t>(req_size + sizeof(BlockHeader));
    476   size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1);
    477   if (size <= sizeof(BlockHeader) || size > mem_page_) {
    478     NOTREACHED();
    479     return kReferenceNull;
    480   }
    481 
    482   // Get the current start of unallocated memory. Other threads may
    483   // update this at any time and cause us to retry these operations.
    484   // This value should be treated as "const" to avoid confusion through
    485   // the code below but recognize that any failed compare-exchange operation
    486   // involving it will cause it to be loaded with a more recent value. The
    487   // code should either exit or restart the loop in that case.
    488   /* const */ uint32_t freeptr =
    489       shared_meta()->freeptr.load(std::memory_order_acquire);
    490 
    491   // Allocation is lockless so we do all our caculation and then, if saving
    492   // indicates a change has occurred since we started, scrap everything and
    493   // start over.
    494   for (;;) {
    495     if (IsCorrupt())
    496       return kReferenceNull;
    497 
    498     if (freeptr + size > mem_size_) {
    499       SetFlag(&shared_meta()->flags, kFlagFull);
    500       return kReferenceNull;
    501     }
    502 
    503     // Get pointer to the "free" block. If something has been allocated since
    504     // the load of freeptr above, it is still safe as nothing will be written
    505     // to that location until after the compare-exchange below.
    506     volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true);
    507     if (!block) {
    508       SetCorrupt();
    509       return kReferenceNull;
    510     }
    511 
    512     // An allocation cannot cross page boundaries. If it would, create a
    513     // "wasted" block and begin again at the top of the next page. This
    514     // area could just be left empty but we fill in the block header just
    515     // for completeness sake.
    516     const uint32_t page_free = mem_page_ - freeptr % mem_page_;
    517     if (size > page_free) {
    518       if (page_free <= sizeof(BlockHeader)) {
    519         SetCorrupt();
    520         return kReferenceNull;
    521       }
    522       const uint32_t new_freeptr = freeptr + page_free;
    523       if (shared_meta()->freeptr.compare_exchange_strong(freeptr,
    524                                                          new_freeptr)) {
    525         block->size = page_free;
    526         block->cookie = kBlockCookieWasted;
    527       }
    528       continue;
    529     }
    530 
    531     // Don't leave a slice at the end of a page too small for anything. This
    532     // can result in an allocation up to two alignment-sizes greater than the
    533     // minimum required by requested-size + header + alignment.
    534     if (page_free - size < sizeof(BlockHeader) + kAllocAlignment)
    535       size = page_free;
    536 
    537     const uint32_t new_freeptr = freeptr + size;
    538     if (new_freeptr > mem_size_) {
    539       SetCorrupt();
    540       return kReferenceNull;
    541     }
    542 
    543     // Save our work. Try again if another thread has completed an allocation
    544     // while we were processing. A "weak" exchange would be permissable here
    545     // because the code will just loop and try again but the above processing
    546     // is significant so make the extra effort of a "strong" exchange.
    547     if (!shared_meta()->freeptr.compare_exchange_strong(freeptr, new_freeptr))
    548       continue;
    549 
    550     // Given that all memory was zeroed before ever being given to an instance
    551     // of this class and given that we only allocate in a monotomic fashion
    552     // going forward, it must be that the newly allocated block is completely
    553     // full of zeros. If we find anything in the block header that is NOT a
    554     // zero then something must have previously run amuck through memory,
    555     // writing beyond the allocated space and into unallocated space.
    556     if (block->size != 0 ||
    557         block->cookie != kBlockCookieFree ||
    558         block->type_id.load(std::memory_order_relaxed) != 0 ||
    559         block->next.load(std::memory_order_relaxed) != 0) {
    560       SetCorrupt();
    561       return kReferenceNull;
    562     }
    563 
    564     block->size = size;
    565     block->cookie = kBlockCookieAllocated;
    566     block->type_id.store(type_id, std::memory_order_relaxed);
    567     return freeptr;
    568   }
    569 }
    570 
    571 void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const {
    572   uint32_t remaining = std::max(
    573       mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed),
    574       (uint32_t)sizeof(BlockHeader));
    575   meminfo->total = mem_size_;
    576   meminfo->free = IsCorrupt() ? 0 : remaining - sizeof(BlockHeader);
    577 }
    578 
    579 void PersistentMemoryAllocator::MakeIterable(Reference ref) {
    580   DCHECK(!readonly_);
    581   if (IsCorrupt())
    582     return;
    583   volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false);
    584   if (!block)  // invalid reference
    585     return;
    586   if (block->next.load(std::memory_order_acquire) != 0)  // Already iterable.
    587     return;
    588   block->next.store(kReferenceQueue, std::memory_order_release);  // New tail.
    589 
    590   // Try to add this block to the tail of the queue. May take multiple tries.
    591   // If so, tail will be automatically updated with a more recent value during
    592   // compare-exchange operations.
    593   uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire);
    594   for (;;) {
    595     // Acquire the current tail-pointer released by previous call to this
    596     // method and validate it.
    597     block = GetBlock(tail, 0, 0, true, false);
    598     if (!block) {
    599       SetCorrupt();
    600       return;
    601     }
    602 
    603     // Try to insert the block at the tail of the queue. The tail node always
    604     // has an existing value of kReferenceQueue; if that is somehow not the
    605     // existing value then another thread has acted in the meantime. A "strong"
    606     // exchange is necessary so the "else" block does not get executed when
    607     // that is not actually the case (which can happen with a "weak" exchange).
    608     uint32_t next = kReferenceQueue;  // Will get replaced with existing value.
    609     if (block->next.compare_exchange_strong(next, ref,
    610                                             std::memory_order_acq_rel,
    611                                             std::memory_order_acquire)) {
    612       // Update the tail pointer to the new offset. If the "else" clause did
    613       // not exist, then this could be a simple Release_Store to set the new
    614       // value but because it does, it's possible that other threads could add
    615       // one or more nodes at the tail before reaching this point. We don't
    616       // have to check the return value because it either operates correctly
    617       // or the exact same operation has already been done (by the "else"
    618       // clause) on some other thread.
    619       shared_meta()->tailptr.compare_exchange_strong(tail, ref,
    620                                                      std::memory_order_release,
    621                                                      std::memory_order_relaxed);
    622       return;
    623     } else {
    624       // In the unlikely case that a thread crashed or was killed between the
    625       // update of "next" and the update of "tailptr", it is necessary to
    626       // perform the operation that would have been done. There's no explicit
    627       // check for crash/kill which means that this operation may also happen
    628       // even when the other thread is in perfect working order which is what
    629       // necessitates the CompareAndSwap above.
    630       shared_meta()->tailptr.compare_exchange_strong(tail, next,
    631                                                      std::memory_order_acq_rel,
    632                                                      std::memory_order_acquire);
    633     }
    634   }
    635 }
    636 
    637 // The "corrupted" state is held both locally and globally (shared). The
    638 // shared flag can't be trusted since a malicious actor could overwrite it.
    639 // Because corruption can be detected during read-only operations such as
    640 // iteration, this method may be called by other "const" methods. In this
    641 // case, it's safe to discard the constness and modify the local flag and
    642 // maybe even the shared flag if the underlying data isn't actually read-only.
    643 void PersistentMemoryAllocator::SetCorrupt() const {
    644   LOG(ERROR) << "Corruption detected in shared-memory segment.";
    645   const_cast<std::atomic<bool>*>(&corrupt_)->store(true,
    646                                                    std::memory_order_relaxed);
    647   if (!readonly_) {
    648     SetFlag(const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags),
    649             kFlagCorrupt);
    650   }
    651 }
    652 
    653 bool PersistentMemoryAllocator::IsCorrupt() const {
    654   if (corrupt_.load(std::memory_order_relaxed) ||
    655       CheckFlag(&shared_meta()->flags, kFlagCorrupt)) {
    656     SetCorrupt();  // Make sure all indicators are set.
    657     return true;
    658   }
    659   return false;
    660 }
    661 
    662 bool PersistentMemoryAllocator::IsFull() const {
    663   return CheckFlag(&shared_meta()->flags, kFlagFull);
    664 }
    665 
    666 // Dereference a block |ref| and ensure that it's valid for the desired
    667 // |type_id| and |size|. |special| indicates that we may try to access block
    668 // headers not available to callers but still accessed by this module. By
    669 // having internal dereferences go through this same function, the allocator
    670 // is hardened against corruption.
    671 const volatile PersistentMemoryAllocator::BlockHeader*
    672 PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id,
    673                                     uint32_t size, bool queue_ok,
    674                                     bool free_ok) const {
    675   // Validation of parameters.
    676   if (ref % kAllocAlignment != 0)
    677     return nullptr;
    678   if (ref < (queue_ok ? kReferenceQueue : sizeof(SharedMetadata)))
    679     return nullptr;
    680   size += sizeof(BlockHeader);
    681   if (ref + size > mem_size_)
    682     return nullptr;
    683 
    684   // Validation of referenced block-header.
    685   if (!free_ok) {
    686     uint32_t freeptr = std::min(
    687         shared_meta()->freeptr.load(std::memory_order_relaxed), mem_size_);
    688     if (ref + size > freeptr)
    689       return nullptr;
    690     const volatile BlockHeader* const block =
    691         reinterpret_cast<volatile BlockHeader*>(mem_base_ + ref);
    692     if (block->size < size)
    693       return nullptr;
    694     if (ref + block->size > freeptr)
    695       return nullptr;
    696     if (ref != kReferenceQueue && block->cookie != kBlockCookieAllocated)
    697       return nullptr;
    698     if (type_id != 0 &&
    699         block->type_id.load(std::memory_order_relaxed) != type_id) {
    700       return nullptr;
    701     }
    702   }
    703 
    704   // Return pointer to block data.
    705   return reinterpret_cast<const volatile BlockHeader*>(mem_base_ + ref);
    706 }
    707 
    708 const volatile void* PersistentMemoryAllocator::GetBlockData(
    709     Reference ref,
    710     uint32_t type_id,
    711     uint32_t size) const {
    712   DCHECK(size > 0);
    713   const volatile BlockHeader* block =
    714       GetBlock(ref, type_id, size, false, false);
    715   if (!block)
    716     return nullptr;
    717   return reinterpret_cast<const volatile char*>(block) + sizeof(BlockHeader);
    718 }
    719 
    720 void PersistentMemoryAllocator::UpdateTrackingHistograms() {
    721   DCHECK(!readonly_);
    722   if (used_histogram_) {
    723     MemoryInfo meminfo;
    724     GetMemoryInfo(&meminfo);
    725     HistogramBase::Sample used_percent = static_cast<HistogramBase::Sample>(
    726         ((meminfo.total - meminfo.free) * 100ULL / meminfo.total));
    727     used_histogram_->Add(used_percent);
    728   }
    729 }
    730 
    731 
    732 //----- LocalPersistentMemoryAllocator -----------------------------------------
    733 
    734 LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator(
    735     size_t size,
    736     uint64_t id,
    737     base::StringPiece name)
    738     : PersistentMemoryAllocator(AllocateLocalMemory(size),
    739                                 size, 0, id, name, false) {}
    740 
    741 LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() {
    742   DeallocateLocalMemory(const_cast<char*>(mem_base_), mem_size_);
    743 }
    744 
    745 // static
    746 void* LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) {
    747 #if defined(OS_WIN)
    748   void* address =
    749       ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    750   DPCHECK(address);
    751   return address;
    752 #elif defined(OS_POSIX)
    753   // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac.
    754   // MAP_SHARED is not available on Linux <2.4 but required on Mac.
    755   void* address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
    756                          MAP_ANON | MAP_SHARED, -1, 0);
    757   DPCHECK(MAP_FAILED != address);
    758   return address;
    759 #else
    760 #error This architecture is not (yet) supported.
    761 #endif
    762 }
    763 
    764 // static
    765 void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory,
    766                                                            size_t size) {
    767 #if defined(OS_WIN)
    768   BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT);
    769   DPCHECK(success);
    770 #elif defined(OS_POSIX)
    771   int result = ::munmap(memory, size);
    772   DPCHECK(0 == result);
    773 #else
    774 #error This architecture is not (yet) supported.
    775 #endif
    776 }
    777 
    778 
    779 //----- SharedPersistentMemoryAllocator ----------------------------------------
    780 
    781 SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator(
    782     std::unique_ptr<SharedMemory> memory,
    783     uint64_t id,
    784     base::StringPiece name,
    785     bool read_only)
    786     : PersistentMemoryAllocator(static_cast<uint8_t*>(memory->memory()),
    787                                 memory->mapped_size(),
    788                                 0,
    789                                 id,
    790                                 name,
    791                                 read_only),
    792       shared_memory_(std::move(memory)) {}
    793 
    794 SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() {}
    795 
    796 // static
    797 bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
    798     const SharedMemory& memory) {
    799   return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, false);
    800 }
    801 
    802 
    803 #if !defined(OS_NACL)
    804 //----- FilePersistentMemoryAllocator ------------------------------------------
    805 
    806 FilePersistentMemoryAllocator::FilePersistentMemoryAllocator(
    807     std::unique_ptr<MemoryMappedFile> file,
    808     size_t max_size,
    809     uint64_t id,
    810     base::StringPiece name,
    811     bool read_only)
    812     : PersistentMemoryAllocator(const_cast<uint8_t*>(file->data()),
    813                                 max_size != 0 ? max_size : file->length(),
    814                                 0,
    815                                 id,
    816                                 name,
    817                                 read_only),
    818       mapped_file_(std::move(file)) {}
    819 
    820 FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() {}
    821 
    822 // static
    823 bool FilePersistentMemoryAllocator::IsFileAcceptable(
    824     const MemoryMappedFile& file,
    825     bool read_only) {
    826   return IsMemoryAcceptable(file.data(), file.length(), 0, read_only);
    827 }
    828 #endif  // !defined(OS_NACL)
    829 
    830 }  // namespace base
    831