blob: b99c48663569ec71a8a134ca3f84928b0d5747bf [file] [log] [blame]
/*
* Copyright (c) 2019-2020 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.
*/
#include "pas_config.h"
#if LIBPAS_ENABLED
#include "pas_segregated_partial_view.h"
#include "pas_epoch.h"
#include "pas_immortal_heap.h"
#include "pas_segregated_page_inlines.h"
#include "pas_segregated_partial_view_inlines.h"
#include "pas_segregated_shared_page_directory.h"
#include "pas_segregated_shared_view_inlines.h"
#include "pas_shared_handle_or_page_boundary_inlines.h"
pas_segregated_partial_view*
pas_segregated_partial_view_create(
pas_segregated_size_directory* directory,
size_t index)
{
static const bool verbose = false;
pas_segregated_partial_view* result;
if (verbose)
pas_log("Creating partial view.\n");
result = pas_immortal_heap_allocate(
sizeof(pas_segregated_partial_view),
"pas_segregated_partial_view",
pas_object_allocation);
/* We attach to a shared view lazily - once we know what we're allocating. */
pas_compact_segregated_shared_view_ptr_store(&result->shared_view, NULL);
pas_compact_segregated_size_directory_ptr_store(&result->directory, directory);
PAS_ASSERT((uint8_t)index == index);
result->index = (uint8_t)index;
result->alloc_bits_offset = 0;
result->alloc_bits_size = 0;
pas_compact_tagged_unsigned_ptr_store(&result->alloc_bits, NULL);
result->inline_alloc_bits = 0; /* There isn't a real big need to do this, but it helps keep
things sane. */
result->is_in_use_for_allocation = false;
result->eligibility_notification_has_been_deferred = false;
result->eligibility_has_been_noted = false;
result->noted_in_scan = false;
result->is_attached_to_shared_handle = false;
return result;
}
void pas_segregated_partial_view_note_eligibility(
pas_segregated_partial_view* view,
pas_segregated_page* page)
{
static const bool verbose = false;
if (page->lock_ptr)
pas_lock_assert_held(page->lock_ptr);
PAS_ASSERT(!view->eligibility_has_been_noted);
if (verbose)
pas_log("Noting eligibility in page %p, view %p.\n", page, view);
if (view->is_in_use_for_allocation)
view->eligibility_notification_has_been_deferred = true;
else {
if (verbose)
pas_log("%p: Actually telling the directory that we're eligible.\n", view);
pas_segregated_directory_view_did_become_eligible(
&pas_compact_segregated_size_directory_ptr_load_non_null(&view->directory)->base,
pas_segregated_partial_view_as_view_non_null(view));
}
view->eligibility_has_been_noted = true;
}
void pas_segregated_partial_view_set_is_in_use_for_allocation(
pas_segregated_partial_view* view,
pas_segregated_shared_view* shared_view,
pas_segregated_shared_handle* shared_handle)
{
static const bool verbose = false;
pas_segregated_shared_page_directory* shared_page_directory;
pas_segregated_directory* directory;
size_t index;
shared_page_directory = shared_handle->directory;
directory = &shared_page_directory->base;
index = shared_view->index;
if (verbose) {
pas_log("Setting partial %p, shared %p (index %zu) as in use for allocation.\n",
view, shared_view, index);
}
PAS_TESTING_ASSERT(!pas_segregated_partial_view_is_eligible(view));
PAS_TESTING_ASSERT(!view->is_in_use_for_allocation);
view->is_in_use_for_allocation = true;
shared_view->is_in_use_for_allocation_count++;
PAS_ASSERT(shared_view->is_in_use_for_allocation_count); /* Check that it didn't overflow. */
if (verbose) {
pas_log("After setting partial %p, shared %p as in use, in use count is %u.\n",
view, shared_view, shared_view->is_in_use_for_allocation_count);
}
view->eligibility_has_been_noted = false;
}
bool pas_segregated_partial_view_should_table(
pas_segregated_partial_view* view,
pas_segregated_page_config* page_config)
{
pas_segregated_shared_view* shared_view;
pas_shared_handle_or_page_boundary shared_handle_or_page_boundary;
pas_segregated_shared_handle* shared_handle;
pas_segregated_page* page;
shared_view = pas_compact_segregated_shared_view_ptr_load(&view->shared_view);
if (!shared_view) {
/* It would be weird if we were asked if we should be tabled in the primordial case. But if
it does happen, then let's not table. */
return false;
}
if (!shared_view->is_owned)
return true;
/* If the shared view is owned, then it should have a shared handle. */
shared_handle_or_page_boundary = shared_view->shared_handle_or_page_boundary;
shared_handle = pas_unwrap_shared_handle(shared_handle_or_page_boundary, *page_config);
page = pas_segregated_page_for_boundary(shared_handle->page_boundary, *page_config);
return !page->num_non_empty_words;
}
static pas_heap_summary compute_summary(pas_segregated_partial_view* view)
{
pas_segregated_size_directory* size_directory;
pas_segregated_directory* directory;
pas_segregated_shared_view* shared_view;
pas_segregated_page_config* page_config_ptr;
pas_segregated_page_config page_config;
pas_segregated_page* page;
unsigned* full_alloc_bits;
unsigned* alloc_bits;
uintptr_t page_boundary;
size_t object_size;
size_t index;
size_t begin_index;
size_t end_index;
pas_heap_summary result;
size_directory = pas_compact_segregated_size_directory_ptr_load_non_null(&view->directory);
directory = &size_directory->base;
page_config_ptr = pas_segregated_page_config_kind_get_config(directory->page_config_kind);
page_config = *page_config_ptr;
object_size = size_directory->object_size;
shared_view = pas_compact_segregated_shared_view_ptr_load_non_null(&view->shared_view);
full_alloc_bits = pas_compact_tagged_unsigned_ptr_load_non_null(&view->alloc_bits);
if (shared_view->is_owned) {
page_boundary = (uintptr_t)pas_shared_handle_or_page_boundary_get_page_boundary(
shared_view->shared_handle_or_page_boundary, page_config);
page = pas_segregated_page_for_boundary((void*)page_boundary, page_config);
alloc_bits = page->alloc_bits;
/* We rely on the fact that the ownership lock is the page lock. That has to be true for
partial views, since they do not do biasing. */
if (!pas_segregated_page_config_is_utility(page_config) && shared_view->is_owned)
PAS_ASSERT(page->lock_ptr == &shared_view->ownership_lock);
} else {
page = NULL;
alloc_bits = NULL;
page_boundary = 0;
}
/* This doesn't have to be optimized since this is just for internal introspection.
Note that this logic magically works even for */
begin_index = PAS_BITVECTOR_BIT_INDEX((unsigned)view->alloc_bits_offset);
end_index = PAS_BITVECTOR_BIT_INDEX((unsigned)view->alloc_bits_offset +
(unsigned)view->alloc_bits_size);
result = pas_heap_summary_create_empty();
for (index = begin_index; index < end_index; ++index) {
uintptr_t offset;
if (!pas_bitvector_get(full_alloc_bits, index))
continue;
if (!shared_view->is_owned) {
result.decommitted += object_size;
result.free_decommitted += object_size;
result.free += object_size;
continue;
}
offset = pas_page_base_object_offset_from_page_boundary_at_index(
(unsigned)index, page_config.base);
pas_segregated_page_add_commit_range(
page, &result, pas_range_create(offset, offset + object_size));
if (pas_bitvector_get(alloc_bits, index))
result.allocated += object_size;
else {
pas_page_base_add_free_range(
&page->base, &result,
pas_range_create(offset, offset + object_size),
pas_free_object_range);
}
}
return result;
}
pas_heap_summary pas_segregated_partial_view_compute_summary(
pas_segregated_partial_view* view)
{
pas_segregated_shared_view* shared_view;
pas_heap_summary result;
shared_view = pas_compact_segregated_shared_view_ptr_load_non_null(&view->shared_view);
pas_lock_lock(&shared_view->ownership_lock);
result = compute_summary(view);
pas_lock_unlock(&shared_view->ownership_lock);
return result;
}
bool pas_segregated_partial_view_is_eligible(pas_segregated_partial_view* view)
{
return PAS_SEGREGATED_DIRECTORY_GET_BIT(
&pas_compact_segregated_size_directory_ptr_load_non_null(&view->directory)->base,
view->index, eligible);
}
#endif /* LIBPAS_ENABLED */