text: // Copyright 2016 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/zone/accounting-allocator.h"
  
  #include <memory>
  
  #include "src/base/bounded-page-allocator.h"
  #include "src/base/logging.h"
  #include "src/base/macros.h"
  #include "src/utils/allocation.h"
  #include "src/zone/zone-compression.h"
  #include "src/zone/zone-segment.h"
  
  namespace v8 {
  namespace internal {
  
  // These definitions are here in order to please the linker, which in debug mode
  // sometimes requires static constants to be defined in .cc files.
  const size_t ZoneCompression::kReservationSize;
  const size_t ZoneCompression::kReservationAlignment;
  
  namespace {
  
  static constexpr size_t kZonePageSize = 256 * KB;
  
  VirtualMemory ReserveAddressSpace(v8::PageAllocator* platform_allocator) {
    DCHECK(IsAligned(ZoneCompression::kReservationSize,
                     platform_allocator->AllocatePageSize()));
  
    void* hint = reinterpret_cast<void*>(RoundDown(
        reinterpret_cast<uintptr_t>(platform_allocator->GetRandomMmapAddr()),
        ZoneCompression::kReservationAlignment));
  
    VirtualMemory memory(platform_allocator, ZoneCompression::kReservationSize,
                         hint, ZoneCompression::kReservationAlignment);
    if (memory.IsReserved()) {
      CHECK(IsAligned(memory.address(), ZoneCompression::kReservationAlignment));
      return memory;
    }
  
    FATAL(
        "Fatal process out of memory: Failed to reserve memory for compressed "
        "zones");
    UNREACHABLE();
  }
  
  std::unique_ptr<v8::base::BoundedPageAllocator> CreateBoundedAllocator(
      v8::PageAllocator* platform_allocator, Address reservation_start) {
    CHECK(reservation_start);
    CHECK(IsAligned(reservation_start, ZoneCompression::kReservationAlignment));
  
    auto allocator = std::make_unique<v8::base::BoundedPageAllocator>(
        platform_allocator, reservation_start, ZoneCompression::kReservationSize,
        kZonePageSize);
  
    // Exclude first page from allocation to ensure that accesses through
    // decompressed null pointer will seg-fault.
    allocator->AllocatePagesAt(reservation_start, kZonePageSize,
                               v8::PageAllocator::kNoAccess);
    return allocator;
  }
  
  }  // namespace
  
  AccountingAllocator::AccountingAllocator() {
    if (COMPRESS_ZONES_BOOL) {
      v8::PageAllocator* platform_page_allocator = GetPlatformPageAllocator();
      VirtualMemory memory = ReserveAddressSpace(platform_page_allocator);
      reserved_area_ = std::make_unique<VirtualMemory>(std::move(memory));
      bounded_page_allocator_ = CreateBoundedAllocator(platform_page_allocator,
                                                       reserved_area_->address());
    }
  }
  
  AccountingAllocator::~AccountingAllocator() = default;
  
  Segment* AccountingAllocator::AllocateSegment(size_t bytes,
                                                bool supports_compression) {
    void* memory;
    if (COMPRESS_ZONES_BOOL && supports_compression) {
      bytes = RoundUp(bytes, kZonePageSize);
      memory = AllocatePages(bounded_page_allocator_.get(), nullptr, bytes,
                             kZonePageSize, PageAllocator::kReadWrite);
  
    } else {
      memory = AllocWithRetry(bytes);
    }
    if (memory == nullptr) return nullptr;
  
    size_t current =
        current_memory_usage_.fetch_add(bytes, std::memory_order_relaxed) + bytes;
    size_t max = max_memory_usage_.load(std::memory_order_relaxed);
    while (current > max && !max_memory_usage_.compare_exchange_weak(
                                max, current, std::memory_order_relaxed)) {
      // {max} was updated by {compare_exchange_weak}; retry.
    }
    DCHECK_LE(sizeof(Segment), bytes);
    return new (memory) Segment(bytes);
  }
  
  void AccountingAllocator::ReturnSegment(Segment* segment,
                                          bool supports_compression) {
    segment->ZapContents();
    size_t segment_size = segment->total_size();
    current_memory_usage_.fetch_sub(segment_size, std::memory_order_relaxed);
    segment->ZapHeader();
    if (COMPRESS_ZONES_BOOL && supports_compression) {
      CHECK(FreePages(bounded_page_allocator_.get(), segment, segment_size));
  
    } else {
      free(segment);
    }
  }
  
  }  // namespace internal
  }  // namespace v8
  