blob: 025057f29a2e92e1c118082925f35c04c4a9a05b [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_THREAD_LOCAL_CACHE_H
#define PAS_THREAD_LOCAL_CACHE_H
#include "pas_allocator_index.h"
#include "pas_allocator_scavenge_action.h"
#include "pas_deallocator_scavenge_action.h"
#include "pas_fast_tls.h"
#include "pas_internal_config.h"
#include "pas_local_allocator.h"
#include "pas_local_allocator_result.h"
#include "pas_utils.h"
#include <pthread.h>
#if defined(__has_include) && __has_include(<pthread/private.h>)
#include <pthread/private.h>
#define PAS_HAVE_PTHREAD_PRIVATE 1
#else
#define PAS_HAVE_PTHREAD_PRIVATE 0
#endif
PAS_BEGIN_EXTERN_C;
struct pas_magazine;
struct pas_thread_local_cache;
struct pas_thread_local_cache_node;
typedef struct pas_magazine pas_magazine;
typedef struct pas_thread_local_cache pas_thread_local_cache;
typedef struct pas_thread_local_cache_node pas_thread_local_cache_node;
struct pas_thread_local_cache {
uintptr_t deallocation_log[PAS_DEALLOCATION_LOG_SIZE];
unsigned deallocation_log_index;
/* A dirty allocation log is one that has been flushed by the thread recently. A clean one is
one that hasn't been touched by the thread (except maybe by appending more things to it). */
bool deallocation_log_dirty;
size_t num_logged_bytes; /* This undercounts since when we append small objects we don't
touch this. */
/* This part of the TLC is allocated separately so that it does not move. */
pas_thread_local_cache_node* node;
unsigned* should_stop_bitvector;
pthread_t thread;
bool should_stop_some;
unsigned allocator_index_upper_bound;
unsigned allocator_index_capacity;
uint64_t local_allocators[1]; /* This is variable-length. */
};
PAS_API extern pas_fast_tls pas_thread_local_cache_fast_tls;
#define PAS_THREAD_LOCAL_KEY __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4
static inline pas_thread_local_cache* pas_thread_local_cache_try_get(void)
{
return (pas_thread_local_cache*)PAS_FAST_TLS_GET(
PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls);
}
#if PAS_HAVE_PTHREAD_PRIVATE
#define PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT \
__builtin_available(macos 10.16, ios 14.0, tvos 14.0, watchos 7.0, *)
static inline bool pas_thread_local_cache_is_guaranteed_to_destruct(void)
{
if (PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT)
return true;
return false;
}
static inline bool pas_thread_local_cache_can_set(void)
{
if (PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT)
return !pthread_self_is_exiting_np();
return true;
}
#else /* PAS_HAVE_PTHREAD_PRIVATE -> so !PAS_HAVE_PTHREAD_PRIVATE */
static inline bool pas_thread_local_cache_is_guaranteed_to_destruct(void)
{
return false;
}
static inline bool pas_thread_local_cache_can_set(void)
{
return true;
}
#endif /* PAS_HAVE_PTHREAD_PRIVATE -> so end of !PAS_HAVE_PTHREAD_PRIVATE */
static inline void pas_thread_local_cache_set_impl(pas_thread_local_cache* thread_local_cache)
{
PAS_ASSERT(pas_thread_local_cache_can_set() || pas_thread_local_cache_try_get());
PAS_FAST_TLS_SET(PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls, thread_local_cache);
}
PAS_API size_t pas_thread_local_cache_size_for_allocator_index_capacity(unsigned allocator_index_capacity);
PAS_API pas_thread_local_cache* pas_thread_local_cache_create(void);
PAS_API void pas_thread_local_cache_destroy(pas_lock_hold_mode heap_lock_hold_mode);
PAS_API pas_thread_local_cache* pas_thread_local_cache_get_slow(
pas_heap_config* config, pas_lock_hold_mode heap_lock_hold_mode);
static inline pas_thread_local_cache* pas_thread_local_cache_get_already_initialized(void)
{
pas_thread_local_cache* result;
result = pas_thread_local_cache_try_get();
PAS_ASSERT(result);
return result;
}
static inline pas_thread_local_cache*
pas_thread_local_cache_get_with_heap_lock_hold_mode(pas_heap_config* config,
pas_lock_hold_mode heap_lock_hold_mode)
{
pas_thread_local_cache* result;
result = pas_thread_local_cache_try_get();
if (PAS_LIKELY(result))
return result;
return pas_thread_local_cache_get_slow(config, heap_lock_hold_mode);
}
static inline pas_thread_local_cache* pas_thread_local_cache_get(pas_heap_config* config)
{
return pas_thread_local_cache_get_with_heap_lock_hold_mode(config, pas_lock_is_not_held);
}
static inline pas_thread_local_cache* pas_thread_local_cache_get_holding_heap_lock(
pas_heap_config* config)
{
return pas_thread_local_cache_get_with_heap_lock_hold_mode(config, pas_lock_is_held);
}
PAS_API pas_local_allocator_result pas_thread_local_cache_get_local_allocator_slow(
pas_thread_local_cache* thread_local_cache,
unsigned allocator_index,
pas_lock_hold_mode heap_lock_hold_mode);
static PAS_ALWAYS_INLINE void*
pas_thread_local_cache_get_local_allocator_impl(
pas_thread_local_cache* thread_local_cache,
uintptr_t allocator_index)
{
return (void*)(thread_local_cache->local_allocators + allocator_index);
}
static PAS_ALWAYS_INLINE pas_local_allocator_result
pas_thread_local_cache_get_local_allocator(
pas_thread_local_cache* thread_local_cache,
unsigned allocator_index,
pas_lock_hold_mode heap_lock_hold_mode)
{
if (PAS_UNLIKELY(allocator_index >= thread_local_cache->allocator_index_upper_bound)) {
/* It could be that we just don't have an allocator index yet. */
if (allocator_index >= (pas_allocator_index)UINT_MAX)
return pas_local_allocator_result_create_failure();
return pas_thread_local_cache_get_local_allocator_slow(
thread_local_cache, allocator_index, heap_lock_hold_mode);
}
return pas_local_allocator_result_create_success(
pas_thread_local_cache_get_local_allocator_impl(thread_local_cache, allocator_index));
}
static PAS_ALWAYS_INLINE pas_local_allocator_result
pas_thread_local_cache_try_get_local_allocator(
pas_thread_local_cache* thread_local_cache,
unsigned allocator_index)
{
if (PAS_UNLIKELY(allocator_index >= thread_local_cache->allocator_index_upper_bound))
return pas_local_allocator_result_create_failure();
return pas_local_allocator_result_create_success(
pas_thread_local_cache_get_local_allocator_impl(thread_local_cache, allocator_index));
}
PAS_API pas_local_allocator_result
pas_thread_local_cache_get_local_allocator_if_can_set_cache_slow(unsigned allocator_index,
pas_heap_config* heap_config);
static PAS_ALWAYS_INLINE pas_local_allocator_result
pas_thread_local_cache_get_local_allocator_if_can_set_cache(unsigned allocator_index,
pas_heap_config* heap_config)
{
pas_thread_local_cache* cache;
cache = pas_thread_local_cache_try_get();
if (PAS_UNLIKELY(!cache)) {
return pas_thread_local_cache_get_local_allocator_if_can_set_cache_slow(
allocator_index, heap_config);
}
return pas_thread_local_cache_get_local_allocator(cache, allocator_index, pas_lock_is_not_held);
}
PAS_API unsigned pas_thread_local_cache_get_num_allocators(pas_thread_local_cache* cache);
PAS_API void pas_thread_local_cache_stop_local_allocators(
pas_thread_local_cache* thread_local_cache,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_API void pas_thread_local_cache_stop_local_allocators_if_necessary(
pas_thread_local_cache* thread_local_cache,
pas_local_allocator* requesting_allocator,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_API void pas_thread_local_cache_flush_deallocation_log(
pas_thread_local_cache* thread_local_cache,
pas_lock_hold_mode heap_lock_hold_mode);
/* Returns true if it's possible that we'll be able to return more memory if this was called
again. */
PAS_API bool pas_thread_local_cache_for_all(pas_allocator_scavenge_action allocator_action,
pas_deallocator_scavenge_action deallocator_action,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_API void pas_thread_local_cache_append_deallocation_slow(
pas_thread_local_cache* thread_local_cache,
uintptr_t begin,
pas_segregated_page_config_kind kind);
static PAS_ALWAYS_INLINE uintptr_t pas_thread_local_cache_encode_object(
uintptr_t begin,
pas_segregated_page_config_kind kind)
{
return (begin << PAS_SEGREGATED_PAGE_CONFIG_KIND_NUM_BITS) | (uintptr_t)kind;
}
static PAS_ALWAYS_INLINE void pas_thread_local_cache_append_deallocation(
pas_thread_local_cache* thread_local_cache,
uintptr_t begin,
pas_segregated_page_config_kind kind)
{
unsigned index;
index = thread_local_cache->deallocation_log_index;
if (PAS_UNLIKELY(index >= PAS_DEALLOCATION_LOG_SIZE - 1)) {
pas_thread_local_cache_append_deallocation_slow(thread_local_cache, begin, kind);
return;
}
thread_local_cache->deallocation_log[index++] = pas_thread_local_cache_encode_object(begin, kind);
thread_local_cache->deallocation_log_index = index;
}
static PAS_ALWAYS_INLINE void pas_thread_local_cache_append_deallocation_with_size(
pas_thread_local_cache* thread_local_cache,
uintptr_t begin, size_t object_size,
pas_segregated_page_config_kind kind)
{
size_t new_num_logged_bytes;
new_num_logged_bytes = thread_local_cache->num_logged_bytes + object_size;
if (new_num_logged_bytes > PAS_DEALLOCATION_LOG_MAX_BYTES) {
pas_thread_local_cache_append_deallocation_slow(thread_local_cache, begin, kind);
return;
}
thread_local_cache->num_logged_bytes = new_num_logged_bytes;
pas_thread_local_cache_append_deallocation(thread_local_cache, begin, kind);
}
PAS_API void pas_thread_local_cache_shrink(pas_thread_local_cache* thread_local_cache,
pas_lock_hold_mode heap_lock_hold_mode);
PAS_END_EXTERN_C;
#endif /* PAS_THREAD_LOCAL_CACHE_H */