blob: 8cc0b2943431f13b64a1fcb20d36d50b5fc1944c [file] [log] [blame]
/*
* Copyright (c) 2018-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PAS_SEGREGATED_HEAP_H
#define PAS_SEGREGATED_HEAP_H
#include "pas_allocator_scavenge_action.h"
#include "pas_compact_atomic_allocator_index_ptr.h"
#include "pas_compact_atomic_ptr.h"
#include "pas_compact_atomic_segregated_size_directory_ptr.h"
#include "pas_compact_heap_ptr.h"
#include "pas_config.h"
#include "pas_count_lookup_mode.h"
#include "pas_hashtable.h"
#include "pas_heap_config.h"
#include "pas_heap_runtime_config.h"
#include "pas_heap_summary.h"
#include "pas_large_map_entry.h"
#include "pas_local_allocator.h"
#include "pas_mutation_count.h"
#include "pas_segregated_heap_lookup_kind.h"
#include "pas_segregated_size_directory_creation_mode.h"
#include "pas_page_sharing_mode.h"
#include "pas_segregated_page.h"
PAS_BEGIN_EXTERN_C;
#ifndef pas_heap
#define pas_heap __pas_heap
#endif
enum pas_segregated_heap_medium_size_directory_search_mode {
pas_segregated_heap_medium_size_directory_search_within_size_class_progression,
pas_segregated_heap_medium_size_directory_search_least_greater_equal
};
typedef enum pas_segregated_heap_medium_size_directory_search_mode pas_segregated_heap_medium_size_directory_search_mode;
struct pas_heap;
struct pas_segregated_size_directory;
struct pas_segregated_heap;
struct pas_segregated_heap_medium_directory_tuple;
struct pas_segregated_heap_rare_data;
typedef struct pas_heap pas_heap;
typedef struct pas_segregated_size_directory pas_segregated_size_directory;
typedef struct pas_segregated_heap pas_segregated_heap;
typedef struct pas_segregated_heap_medium_directory_tuple pas_segregated_heap_medium_directory_tuple;
typedef struct pas_segregated_heap_rare_data pas_segregated_heap_rare_data;
PAS_DEFINE_COMPACT_ATOMIC_PTR(pas_segregated_heap_rare_data, pas_segregated_heap_rare_data_ptr);
PAS_DEFINE_COMPACT_ATOMIC_PTR(pas_segregated_heap_medium_directory_tuple,
pas_segregated_heap_medium_directory_tuple_ptr);
struct pas_segregated_heap {
pas_heap_runtime_config* runtime_config;
pas_allocator_index* index_to_small_allocator_index;
pas_compact_atomic_segregated_size_directory_ptr* index_to_small_size_directory;
/* This is the head of the linked list of segregated size directories. Also, if there is
a basic size directory (the one for index == 1 or index == *cached_index), then it will
be at the head of the list with the is_basic_size_directory bit set. */
pas_compact_atomic_segregated_size_directory_ptr basic_size_directory_and_head;
pas_segregated_heap_rare_data_ptr rare_data;
pas_compact_atomic_bitfit_heap_ptr bitfit_heap;
/* Below this index (exclusive), we can look up the allocator index and directiry directly in
the index_to_XYZ tables. Above this index (inclusive), we have to use the rare data and it
becomes a binary search.
NOTE: The directories below this bound _may_ be medium-sized if our heuristics say that this
is a better fit. That's rare but could happen.
Also, confusingly, this field being non-zero means "we have decided to cache small index
lookup". Note that it _is_ OK for this to be zero but for us to have a rare_data with a
non-zero number of medium directories. */
unsigned small_index_upper_bound;
};
typedef unsigned pas_segregated_heap_medium_directory_index;
struct pas_segregated_heap_medium_directory_tuple {
/* NOTE: This directory is always going to be medium sized because this data structure is only
used for sizes that are beyond the max object size for small pages. But nobody strongly
relies on that property. */
pas_compact_atomic_segregated_size_directory_ptr directory;
pas_allocator_index allocator_index;
/* This is the `index` we would use to do a lookup. A medium directory tuple represents a
contiguous range of indices that map to a single directory. */
pas_segregated_heap_medium_directory_index begin_index; /* inclusive */
pas_segregated_heap_medium_directory_index end_index; /* inclusive */
};
struct pas_segregated_heap_rare_data {
/* This code is counter-locked. Note that odd values mean that we are mutating right now. */
pas_mutation_count mutation_count;
/* This contains all of the medium page directories in sorted order. Note that it must be
allocated from a heap that never returns memory to the OS; otherwise our counter-synchronized
reading code could crash. We currently achieve this by never freeing what we allocate
here. */
pas_segregated_heap_medium_directory_tuple_ptr medium_directories;
unsigned num_medium_directories;
unsigned medium_directories_capacity;
};
/* NOTE: it's possible to construct a pas_segregated_heap outside a pas_heap. Such a
segregated_heap will assume type_size == 1. */
PAS_API void pas_segregated_heap_construct(pas_segregated_heap* segregated_heap,
pas_heap* parent_heap,
pas_heap_config* config,
pas_heap_runtime_config* runtime_config);
PAS_API pas_bitfit_heap* pas_segregated_heap_get_bitfit(pas_segregated_heap* heap,
pas_heap_config* heap_config,
pas_lock_hold_mode heap_lock_hold_mode);
static inline size_t
pas_segregated_heap_index_for_primitive_count(size_t count,
pas_heap_config config)
{
return (count + pas_segregated_page_config_min_align(config.small_segregated_config) - 1)
>> config.small_segregated_config.base.min_align_shift;
}
static inline size_t
pas_segregated_heap_primitive_count_for_index(size_t index,
pas_heap_config config)
{
return index << config.small_segregated_config.base.min_align_shift;
}
static inline size_t pas_segregated_heap_index_for_count(pas_segregated_heap* heap,
size_t count,
pas_heap_config config)
{
/* FIXME: This is an obvious source of inefficiency. Should pass the lookup kind down. */
if (heap->runtime_config->lookup_kind == pas_segregated_heap_lookup_primitive)
count = pas_segregated_heap_index_for_primitive_count(count, config);
return count;
}
static inline size_t pas_segregated_heap_count_for_index(pas_segregated_heap* heap,
size_t index,
pas_heap_config config)
{
/* FIXME: This is an obvious source of inefficiency. Should pass the lookup kind down. */
if (heap->runtime_config->lookup_kind == pas_segregated_heap_lookup_primitive)
index = pas_segregated_heap_primitive_count_for_index(index, config);
return index;
}
PAS_API pas_segregated_heap_medium_directory_tuple*
pas_segregated_heap_medium_directory_tuple_for_index(
pas_segregated_heap* heap,
size_t index,
pas_segregated_heap_medium_size_directory_search_mode search_mode,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_API unsigned pas_segregated_heap_medium_allocator_index_for_index(
pas_segregated_heap* heap,
size_t index,
pas_segregated_heap_medium_size_directory_search_mode search_mode,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_API pas_segregated_size_directory* pas_segregated_heap_medium_size_directory_for_index(
pas_segregated_heap* heap,
size_t index,
pas_segregated_heap_medium_size_directory_search_mode search_mode,
pas_lock_hold_mode heap_lock_hold_mode);
static PAS_ALWAYS_INLINE unsigned
pas_segregated_heap_allocator_index_for_index(pas_segregated_heap* heap,
size_t index,
pas_lock_hold_mode heap_lock_hold_mode)
{
pas_allocator_index* index_to_allocator_index;
if (PAS_UNLIKELY(index >= (size_t)heap->small_index_upper_bound)) {
return pas_segregated_heap_medium_allocator_index_for_index(
heap, index,
pas_segregated_heap_medium_size_directory_search_within_size_class_progression,
heap_lock_hold_mode);
}
index_to_allocator_index = heap->index_to_small_allocator_index;
if (!index_to_allocator_index)
return UINT_MAX; /* Guard against races. */
return index_to_allocator_index[index];
}
static PAS_ALWAYS_INLINE unsigned
pas_segregated_heap_allocator_index_for_count_not_primitive(pas_segregated_heap* heap,
size_t count)
{
size_t index;
index = count; /* For non-primitives, index is count. */
return pas_segregated_heap_allocator_index_for_index(heap, index,
pas_lock_is_not_held);
}
PAS_API unsigned
pas_segregated_heap_ensure_allocator_index(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
size_t count,
pas_count_lookup_mode count_lookup_mode,
pas_heap_config* config,
unsigned* cached_index);
/* This may return UINT_MAX if it determines that the wasteage from allocating this count with
the small heap is greater than the wasteage from rounding up to large alignment and allocating
with the large allocator.
This will try to avoid creating new size classes if there is a size class that is pretty close
in size to the one you ask for, except if that size class fails to obey the alignment
requirement.
Need to hold heap lock to call this.
NOTE: force_count_lookup == true means "it's not good enough for me to have this cached in
basic_size_directory since I will directly do a lookup in index_to_small_allocator_index or
the rare_data". force_count_lookup == false means "please assume that if this can be cached in
the basic_size_directory then I will want this cached in the heap_ref".
FIXME: Merge these two comments. */
/* May return NULL; see comment for
pas_segregated_heap_ensure_allocator_index_for_count. Need to hold heap lock to
call this. OK to pass NULL for cached_index; only primitive_heap_ref's use
that. */
PAS_API pas_segregated_size_directory*
pas_segregated_heap_ensure_size_directory_for_count(
pas_segregated_heap* heap,
size_t count, size_t alignment,
pas_count_lookup_mode count_lookup_mode,
pas_heap_config* config,
unsigned* cached_index,
pas_segregated_size_directory_creation_mode creation_mode);
PAS_API size_t pas_segregated_heap_get_num_free_bytes(pas_segregated_heap* heap);
typedef bool (*pas_segregated_heap_for_each_size_directory_callback)(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
void* arg);
PAS_API bool pas_segregated_heap_for_each_size_directory(
pas_segregated_heap* heap,
pas_segregated_heap_for_each_size_directory_callback callback,
void* arg);
typedef bool (*pas_segregated_heap_for_each_committed_view_callback)(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
pas_segregated_view view,
void* arg);
PAS_API bool pas_segregated_heap_for_each_committed_view(
pas_segregated_heap* heap,
pas_segregated_heap_for_each_committed_view_callback callback,
void* arg);
typedef bool (*pas_segregated_heap_for_each_view_index_callback)(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
size_t index,
pas_segregated_view viewp,
void* arg);
PAS_API bool pas_segregated_heap_for_each_view_index(
pas_segregated_heap* heap,
pas_segregated_heap_for_each_view_index_callback callback,
void* arg);
typedef bool (*pas_segregated_heap_for_each_live_object_callback)(
pas_segregated_heap* heap,
uintptr_t begin,
size_t size,
void* arg);
PAS_API bool pas_segregated_heap_for_each_live_object(
pas_segregated_heap* heap,
pas_segregated_heap_for_each_live_object_callback callback,
void *arg);
PAS_API size_t pas_segregated_heap_num_committed_views(pas_segregated_heap* heap);
PAS_API size_t pas_segregated_heap_num_empty_views(pas_segregated_heap* heap);
PAS_API size_t pas_segregated_heap_num_empty_granules(pas_segregated_heap* heap);
PAS_API size_t pas_segregated_heap_num_views(pas_segregated_heap* heap);
PAS_API pas_heap_summary pas_segregated_heap_compute_summary(pas_segregated_heap* heap);
PAS_END_EXTERN_C;
#endif /* PAS_SEGREGATED_HEAP_H */