|  | // Copyright (c) 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ | 
|  | #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "base/allocator/partition_allocator/partition_alloc_constants.h" | 
|  | #include "base/base_export.h" | 
|  | #include "base/compiler_specific.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  |  | 
|  | struct PartitionPage; | 
|  | struct PartitionRootBase; | 
|  |  | 
|  | struct PartitionBucket { | 
|  | // Accessed most in hot path => goes first. | 
|  | PartitionPage* active_pages_head; | 
|  |  | 
|  | PartitionPage* empty_pages_head; | 
|  | PartitionPage* decommitted_pages_head; | 
|  | uint32_t slot_size; | 
|  | uint32_t num_system_pages_per_slot_span : 8; | 
|  | uint32_t num_full_pages : 24; | 
|  |  | 
|  | // Public API. | 
|  | void Init(uint32_t new_slot_size); | 
|  |  | 
|  | // Note the matching Free() functions are in PartitionPage. | 
|  | BASE_EXPORT NOINLINE void* SlowPathAlloc(PartitionRootBase* root, | 
|  | int flags, | 
|  | size_t size); | 
|  |  | 
|  | ALWAYS_INLINE bool is_direct_mapped() const { | 
|  | return !num_system_pages_per_slot_span; | 
|  | } | 
|  | ALWAYS_INLINE size_t get_bytes_per_span() const { | 
|  | // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153 | 
|  | // https://crbug.com/680657 | 
|  | return num_system_pages_per_slot_span * kSystemPageSize; | 
|  | } | 
|  | ALWAYS_INLINE uint16_t get_slots_per_span() const { | 
|  | // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153 | 
|  | // https://crbug.com/680657 | 
|  | return static_cast<uint16_t>(get_bytes_per_span() / slot_size); | 
|  | } | 
|  |  | 
|  | static ALWAYS_INLINE size_t get_direct_map_size(size_t size) { | 
|  | // Caller must check that the size is not above the kGenericMaxDirectMapped | 
|  | // limit before calling. This also guards against integer overflow in the | 
|  | // calculation here. | 
|  | DCHECK(size <= kGenericMaxDirectMapped); | 
|  | return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; | 
|  | } | 
|  |  | 
|  | // TODO(ajwong): Can this be made private?  https://crbug.com/787153 | 
|  | static PartitionBucket* get_sentinel_bucket(); | 
|  |  | 
|  | // This helper function scans a bucket's active page list for a suitable new | 
|  | // active page.  When it finds a suitable new active page (one that has | 
|  | // free slots and is not empty), it is set as the new active page. If there | 
|  | // is no suitable new active page, the current active page is set to | 
|  | // PartitionPage::get_sentinel_page(). As potential pages are scanned, they | 
|  | // are tidied up according to their state. Empty pages are swept on to the | 
|  | // empty page list, decommitted pages on to the decommitted page list and full | 
|  | // pages are unlinked from any list. | 
|  | // | 
|  | // This is where the guts of the bucket maintenance is done! | 
|  | bool SetNewActivePage(); | 
|  |  | 
|  | private: | 
|  | static void OutOfMemory(const PartitionRootBase* root); | 
|  | static void OutOfMemoryWithLotsOfUncommitedPages(); | 
|  |  | 
|  | static NOINLINE void OnFull(); | 
|  |  | 
|  | // Returns a natural number of PartitionPages (calculated by | 
|  | // get_system_pages_per_slot_span()) to allocate from the current | 
|  | // SuperPage when the bucket runs out of slots. | 
|  | ALWAYS_INLINE uint16_t get_pages_per_slot_span(); | 
|  |  | 
|  | // Returns the number of system pages in a slot span. | 
|  | // | 
|  | // The calculation attemps to find the best number of System Pages to | 
|  | // allocate for the given slot_size to minimize wasted space. It uses a | 
|  | // heuristic that looks at number of bytes wasted after the last slot and | 
|  | // attempts to account for the PTE usage of each System Page. | 
|  | uint8_t get_system_pages_per_slot_span(); | 
|  |  | 
|  | // Allocates a new slot span with size |num_partition_pages| from the | 
|  | // current extent. Metadata within this slot span will be uninitialized. | 
|  | // Returns nullptr on error. | 
|  | ALWAYS_INLINE void* AllocNewSlotSpan(PartitionRootBase* root, | 
|  | int flags, | 
|  | uint16_t num_partition_pages); | 
|  |  | 
|  | // Each bucket allocates a slot span when it runs out of slots. | 
|  | // A slot span's size is equal to get_pages_per_slot_span() number of | 
|  | // PartitionPages. This function initializes all PartitionPage within the | 
|  | // span to point to the first PartitionPage which holds all the metadata | 
|  | // for the span and registers this bucket as the owner of the span. It does | 
|  | // NOT put the slots into the bucket's freelist. | 
|  | ALWAYS_INLINE void InitializeSlotSpan(PartitionPage* page); | 
|  |  | 
|  | // Allocates one slot from the given |page| and then adds the remainder to | 
|  | // the current bucket. If the |page| was freshly allocated, it must have been | 
|  | // passed through InitializeSlotSpan() first. | 
|  | ALWAYS_INLINE char* AllocAndFillFreelist(PartitionPage* page); | 
|  |  | 
|  | static PartitionBucket sentinel_bucket_; | 
|  | }; | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base | 
|  |  | 
|  | #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ |