d3adfec2fb
Now it works with common logic for all static scope not with custom one.
567 lines
22 KiB
C++
567 lines
22 KiB
C++
/*
|
|
* Copyright 2010-2018 JetBrains s.r.o.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef RUNTIME_MEMORY_H
|
|
#define RUNTIME_MEMORY_H
|
|
|
|
#include <utility>
|
|
|
|
#include "KAssert.h"
|
|
#include "Common.h"
|
|
#include "TypeInfo.h"
|
|
#include "Atomic.h"
|
|
#include "PointerBits.h"
|
|
#include "Utils.hpp"
|
|
|
|
#if KONAN_NEED_SMALL_BINARY
|
|
// Currently, codegen places a lot of unnecessary calls to MM functions.
|
|
// By forcing NO_INLINE on these functions we keep binaries from growing too big.
|
|
#define CODEGEN_INLINE_POLICY NO_INLINE
|
|
#else
|
|
#define CODEGEN_INLINE_POLICY ALWAYS_INLINE
|
|
#endif
|
|
|
|
typedef enum {
|
|
// Must match to permTag() in Kotlin.
|
|
OBJECT_TAG_PERMANENT_CONTAINER = 1 << 0,
|
|
OBJECT_TAG_NONTRIVIAL_CONTAINER = 1 << 1,
|
|
// Keep in sync with immTypeInfoMask in Kotlin.
|
|
OBJECT_TAG_MASK = (1 << 2) - 1
|
|
} ObjectTag;
|
|
|
|
struct ArrayHeader;
|
|
struct MetaObjHeader;
|
|
|
|
// Header of every object.
|
|
struct ObjHeader {
|
|
TypeInfo* typeInfoOrMeta_;
|
|
|
|
// Returns `nullptr` if it's not a meta object.
|
|
static MetaObjHeader* AsMetaObject(TypeInfo* typeInfo) noexcept {
|
|
auto* typeInfoOrMeta = clearPointerBits(typeInfo, OBJECT_TAG_MASK);
|
|
if (typeInfoOrMeta != typeInfoOrMeta->typeInfo_) {
|
|
return reinterpret_cast<MetaObjHeader*>(typeInfoOrMeta);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
TypeInfo* typeInfoOrMetaRelaxed() const { return atomicGetRelaxed(&typeInfoOrMeta_);}
|
|
TypeInfo* typeInfoOrMetaAcquire() const { return atomicGetAcquire(&typeInfoOrMeta_);}
|
|
|
|
/**
|
|
* Formally, this code data races with installing ExtraObject. Even though, we are okey, with reading
|
|
* both typeInfo and meta-object pointer, llvm memory model doesn't guarantee, that if we are able to
|
|
* see metaObject, written by other thread, we would be able to see metaObject->typeInfo.
|
|
*
|
|
* To make this correct with llvm memory model we need to use [LLVMAtomicOrdering.LLVMAtomicOrderingAcquire] here.
|
|
* Unfortunately, this is dramatically harmful for performance on arm architecture. So, we are using
|
|
* [LLVMAtomicOrdering.LLVMAtomicOrderingMonotonic] for both this read and following load of metaObject->typeInfo.
|
|
* At this point, we have no data race, but llvm memory model allows uninitialized value to be read from metaObject->typeInfo.
|
|
*
|
|
* Hardware guaranties on many supported platforms doesn't allow this to happen.
|
|
*/
|
|
const TypeInfo* type_info() const {
|
|
#ifdef KONAN_TARGET_HAS_ADDRESS_DEPENDENCY
|
|
return atomicGetRelaxed(&clearPointerBits(typeInfoOrMetaRelaxed(), OBJECT_TAG_MASK)->typeInfo_);
|
|
#else
|
|
return atomicGetRelaxed(&clearPointerBits(typeInfoOrMetaAcquire(), OBJECT_TAG_MASK)->typeInfo_);
|
|
#endif
|
|
}
|
|
|
|
bool has_meta_object() const {
|
|
return meta_object_or_null() != nullptr;
|
|
}
|
|
|
|
MetaObjHeader* meta_object() {
|
|
if (auto* metaObject = AsMetaObject(typeInfoOrMetaAcquire())) {
|
|
return metaObject;
|
|
}
|
|
return createMetaObject(this);
|
|
}
|
|
|
|
MetaObjHeader* meta_object_or_null() const noexcept { return AsMetaObject(typeInfoOrMetaAcquire()); }
|
|
|
|
ALWAYS_INLINE ObjHeader* GetWeakCounter();
|
|
ALWAYS_INLINE ObjHeader* GetOrSetWeakCounter(ObjHeader* counter);
|
|
|
|
|
|
#ifdef KONAN_OBJC_INTEROP
|
|
ALWAYS_INLINE void* GetAssociatedObject() const;
|
|
ALWAYS_INLINE void SetAssociatedObject(void* obj);
|
|
ALWAYS_INLINE void* CasAssociatedObject(void* expectedObj, void* obj);
|
|
#endif
|
|
|
|
inline bool local() const {
|
|
unsigned bits = getPointerBits(typeInfoOrMetaRelaxed(), OBJECT_TAG_MASK);
|
|
return (bits & (OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER)) ==
|
|
(OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER);
|
|
}
|
|
|
|
// Unsafe cast to ArrayHeader. Use carefully!
|
|
// TODO: RuntimeAssert on type_info()->IsArray()?
|
|
ArrayHeader* array() { return reinterpret_cast<ArrayHeader*>(this); }
|
|
const ArrayHeader* array() const { return reinterpret_cast<const ArrayHeader*>(this); }
|
|
|
|
inline bool permanent() const {
|
|
return hasPointerBits(typeInfoOrMetaRelaxed(), OBJECT_TAG_PERMANENT_CONTAINER);
|
|
}
|
|
|
|
inline bool heap() const { return getPointerBits(typeInfoOrMetaRelaxed(), OBJECT_TAG_MASK) == 0; }
|
|
|
|
static MetaObjHeader* createMetaObject(ObjHeader* object);
|
|
static void destroyMetaObject(ObjHeader* object);
|
|
};
|
|
|
|
// Header of value type array objects. Keep layout in sync with that of object header.
|
|
struct ArrayHeader {
|
|
TypeInfo* typeInfoOrMeta_;
|
|
|
|
const TypeInfo* type_info() const {
|
|
return clearPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK)->typeInfo_;
|
|
}
|
|
|
|
ObjHeader* obj() { return reinterpret_cast<ObjHeader*>(this); }
|
|
const ObjHeader* obj() const { return reinterpret_cast<const ObjHeader*>(this); }
|
|
|
|
// Elements count. Element size is stored in instanceSize_ field of TypeInfo, negated.
|
|
uint32_t count_;
|
|
};
|
|
|
|
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj);
|
|
ALWAYS_INLINE bool isShareable(const ObjHeader* obj);
|
|
|
|
static inline ObjHeader* const kInitializingSingleton = reinterpret_cast<ObjHeader*>(1);
|
|
ALWAYS_INLINE inline bool isNullOrMarker(const ObjHeader* obj) noexcept {
|
|
return reinterpret_cast<uintptr_t>(obj) <= 1;
|
|
}
|
|
|
|
class ForeignRefManager;
|
|
struct FrameOverlay;
|
|
typedef ForeignRefManager* ForeignRefContext;
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#define OBJ_RESULT __result__
|
|
#define OBJ_GETTER0(name) ObjHeader* name(ObjHeader** OBJ_RESULT)
|
|
#define OBJ_GETTER(name, ...) ObjHeader* name(__VA_ARGS__, ObjHeader** OBJ_RESULT)
|
|
#define RETURN_OBJ(value) { ObjHeader* __obj = value; \
|
|
UpdateReturnRef(OBJ_RESULT, __obj); \
|
|
return __obj; }
|
|
#define RETURN_RESULT_OF0(name) { \
|
|
ObjHeader* __obj = name(OBJ_RESULT); \
|
|
return __obj; \
|
|
}
|
|
#define RETURN_RESULT_OF(name, ...) { \
|
|
ObjHeader* __result = name(__VA_ARGS__, OBJ_RESULT); \
|
|
return __result; \
|
|
}
|
|
|
|
struct MemoryState;
|
|
|
|
MemoryState* InitMemory(bool firstRuntime);
|
|
void DeinitMemory(MemoryState*, bool destroyRuntime);
|
|
void RestoreMemory(MemoryState*);
|
|
void ClearMemoryForTests(MemoryState*);
|
|
|
|
//
|
|
// Object allocation.
|
|
//
|
|
// Allocation can happen in either GLOBAL, FRAME or ARENA scope. Depending on that,
|
|
// Alloc* or ArenaAlloc* is called. Regular alloc means allocation happens in the heap,
|
|
// and each object gets its individual container. Otherwise, allocator uses aux slot in
|
|
// an implementation-defined manner, current behavior is to keep arena pointer there.
|
|
// Arena containers are not reference counted, and is explicitly freed when leaving
|
|
// its owner frame.
|
|
// Escape analysis algorithm is the provider of information for decision on exact aux slot
|
|
// selection, and comes from upper bound estimation of object lifetime.
|
|
//
|
|
OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
|
|
|
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements);
|
|
|
|
|
|
// `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already
|
|
// set by static initialization.
|
|
// TODO: When global initialization becomes lazy, this signature won't do.
|
|
void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) RUNTIME_NOTHROW;
|
|
|
|
//
|
|
// Object reference management.
|
|
//
|
|
// Reference management scheme we use assumes significant degree of flexibility, so that
|
|
// one could implement either pure reference counting scheme, or tracing collector without
|
|
// much ado.
|
|
// Most important primitive is Update*Ref() API, which modifies location to use new
|
|
// object reference. In pure reference counted scheme it will check old value,
|
|
// decrement reference, increment counter on the new value, and store it into the field.
|
|
// In tracing collector-like scheme, only field updates counts, and all other operations are
|
|
// essentially no-ops.
|
|
//
|
|
// On codegeneration phase we adopt following approaches:
|
|
// - every stack frame has several slots, holding object references (allRefs)
|
|
// - those are known by compiler (and shall be grouped together)
|
|
// - it keeps all locally allocated objects in such slot
|
|
// - all local variables keeping an object also allocate a slot
|
|
// - most manipulations on objects happens in SSA variables and do no affect slots
|
|
// - exception handlers knowns slot locations for every function, and can update references
|
|
// in intermediate frames when throwing
|
|
//
|
|
|
|
// NOTE: Must match `MemoryModel` in `Platform.kt`
|
|
enum class MemoryModel {
|
|
kStrict = 0,
|
|
kRelaxed = 1,
|
|
kExperimental = 2,
|
|
};
|
|
|
|
// Controls the current memory model, is compile-time constant.
|
|
extern const MemoryModel CurrentMemoryModel;
|
|
|
|
// Sets stack location.
|
|
void SetStackRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Sets heap location.
|
|
void SetHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Zeroes heap location.
|
|
void ZeroHeapRef(ObjHeader** location) RUNTIME_NOTHROW;
|
|
// Zeroes an array.
|
|
void ZeroArrayRefs(ArrayHeader* array) RUNTIME_NOTHROW;
|
|
// Zeroes stack location.
|
|
void ZeroStackRef(ObjHeader** location) RUNTIME_NOTHROW;
|
|
// Updates stack location.
|
|
void UpdateStackRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Updates heap/static data location.
|
|
void UpdateHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Updates heap/static data in one array.
|
|
void UpdateHeapRefsInsideOneArray(const ArrayHeader* array, int fromIndex, int toIndex, int count) RUNTIME_NOTHROW;
|
|
// Updates location if it is null, atomically.
|
|
void UpdateHeapRefIfNull(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Updates reference in return slot.
|
|
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) RUNTIME_NOTHROW;
|
|
// Compares and swaps reference with taken lock.
|
|
OBJ_GETTER(SwapHeapRefLocked,
|
|
ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue, int32_t* spinlock,
|
|
int32_t* cookie) RUNTIME_NOTHROW;
|
|
// Sets reference with taken lock.
|
|
void SetHeapRefLocked(ObjHeader** location, ObjHeader* newValue, int32_t* spinlock,
|
|
int32_t* cookie) RUNTIME_NOTHROW;
|
|
// Reads reference with taken lock.
|
|
OBJ_GETTER(ReadHeapRefLocked, ObjHeader** location, int32_t* spinlock, int32_t* cookie) RUNTIME_NOTHROW;
|
|
OBJ_GETTER(ReadHeapRefNoLock, ObjHeader* object, int32_t index);
|
|
// Called on frame enter, if it has object slots.
|
|
void EnterFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
|
|
// Called on frame leave, if it has object slots.
|
|
void LeaveFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
|
|
// Set current frame in case if exception caught.
|
|
void SetCurrentFrame(ObjHeader** start) RUNTIME_NOTHROW;
|
|
FrameOverlay* getCurrentFrame() RUNTIME_NOTHROW;
|
|
ALWAYS_INLINE void CheckCurrentFrame(ObjHeader** frame) RUNTIME_NOTHROW;
|
|
|
|
// Clears object subgraph references from memory subsystem, and optionally
|
|
// checks if subgraph referenced by given root is disjoint from the rest of
|
|
// object graph, i.e. no external references exists.
|
|
bool ClearSubgraphReferences(ObjHeader* root, bool checked) RUNTIME_NOTHROW;
|
|
// Creates a stable pointer out of the object.
|
|
void* CreateStablePointer(ObjHeader* obj) RUNTIME_NOTHROW;
|
|
// Disposes a stable pointer to the object.
|
|
void DisposeStablePointer(void* pointer) RUNTIME_NOTHROW;
|
|
// Disposes a stable pointer to the object.
|
|
// Accepts a MemoryState, thus can be called from deinitiliazation methods, when TLS is already deallocated.
|
|
void DisposeStablePointerFor(MemoryState* memoryState, void* pointer) RUNTIME_NOTHROW;
|
|
// Translate stable pointer to object reference.
|
|
OBJ_GETTER(DerefStablePointer, void*) RUNTIME_NOTHROW;
|
|
// Move stable pointer ownership.
|
|
OBJ_GETTER(AdoptStablePointer, void*) RUNTIME_NOTHROW;
|
|
// Check mutability state.
|
|
void MutationCheck(ObjHeader* obj);
|
|
void CheckLifetimesConstraint(ObjHeader* obj, ObjHeader* pointee) RUNTIME_NOTHROW;
|
|
// Freeze object subgraph.
|
|
void FreezeSubgraph(ObjHeader* obj);
|
|
// Ensure this object shall block freezing.
|
|
void EnsureNeverFrozen(ObjHeader* obj);
|
|
// Add TLS object storage, called by the generated code.
|
|
void AddTLSRecord(MemoryState* memory, void** key, int size) RUNTIME_NOTHROW;
|
|
// Allocate storage for TLS. `AddTLSRecord` cannot be called after this.
|
|
void CommitTLSStorage(MemoryState* memory) RUNTIME_NOTHROW;
|
|
// Clear TLS object storage.
|
|
void ClearTLS(MemoryState* memory) RUNTIME_NOTHROW;
|
|
// Lookup element in TLS object storage.
|
|
ObjHeader** LookupTLS(void** key, int index) RUNTIME_NOTHROW;
|
|
|
|
// APIs for the async GC.
|
|
void GC_RegisterWorker(void* worker) RUNTIME_NOTHROW;
|
|
void GC_UnregisterWorker(void* worker) RUNTIME_NOTHROW;
|
|
void GC_CollectorCallback(void* worker) RUNTIME_NOTHROW;
|
|
|
|
void Kotlin_native_internal_GC_collect(ObjHeader*);
|
|
void Kotlin_native_internal_GC_collectCyclic(ObjHeader*);
|
|
void Kotlin_native_internal_GC_suspend(ObjHeader*);
|
|
void Kotlin_native_internal_GC_resume(ObjHeader*);
|
|
void Kotlin_native_internal_GC_stop(ObjHeader*);
|
|
void Kotlin_native_internal_GC_start(ObjHeader*);
|
|
void Kotlin_native_internal_GC_setThreshold(ObjHeader*, int32_t value);
|
|
int32_t Kotlin_native_internal_GC_getThreshold(ObjHeader*);
|
|
void Kotlin_native_internal_GC_setCollectCyclesThreshold(ObjHeader*, int64_t value);
|
|
int64_t Kotlin_native_internal_GC_getCollectCyclesThreshold(ObjHeader*);
|
|
void Kotlin_native_internal_GC_setThresholdAllocations(ObjHeader*, int64_t value);
|
|
int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*);
|
|
void Kotlin_native_internal_GC_setTuneThreshold(ObjHeader*, bool value);
|
|
bool Kotlin_native_internal_GC_getTuneThreshold(ObjHeader*);
|
|
OBJ_GETTER(Kotlin_native_internal_GC_detectCycles, ObjHeader*);
|
|
OBJ_GETTER(Kotlin_native_internal_GC_findCycle, ObjHeader*, ObjHeader* root);
|
|
bool Kotlin_native_internal_GC_getCyclicCollector(ObjHeader* gc);
|
|
void Kotlin_native_internal_GC_setCyclicCollector(ObjHeader* gc, bool value);
|
|
|
|
bool Kotlin_Any_isShareable(ObjHeader* thiz);
|
|
void Kotlin_Any_share(ObjHeader* thiz);
|
|
void PerformFullGC(MemoryState* memory) RUNTIME_NOTHROW;
|
|
|
|
// Only for legacy
|
|
bool TryAddHeapRef(const ObjHeader* object);
|
|
void ReleaseHeapRefNoCollect(const ObjHeader* object) RUNTIME_NOTHROW;
|
|
|
|
// Only for experimental
|
|
OBJ_GETTER(TryRef, ObjHeader* object) RUNTIME_NOTHROW;
|
|
|
|
ForeignRefContext InitLocalForeignRef(ObjHeader* object);
|
|
|
|
ForeignRefContext InitForeignRef(ObjHeader* object);
|
|
void DeinitForeignRef(ObjHeader* object, ForeignRefContext context);
|
|
|
|
bool IsForeignRefAccessible(ObjHeader* object, ForeignRefContext context);
|
|
|
|
// Should be used when reference is read from a possibly shared variable,
|
|
// and there's nothing else keeping the object alive.
|
|
void AdoptReferenceFromSharedVariable(ObjHeader* object);
|
|
|
|
void CheckGlobalsAccessible();
|
|
|
|
// Sets state of the current thread to NATIVE (used by the new MM).
|
|
CODEGEN_INLINE_POLICY RUNTIME_NOTHROW void Kotlin_mm_switchThreadStateNative();
|
|
// Sets state of the current thread to RUNNABLE (used by the new MM).
|
|
CODEGEN_INLINE_POLICY RUNTIME_NOTHROW void Kotlin_mm_switchThreadStateRunnable();
|
|
|
|
// Safe point callbacks from Kotlin code generator.
|
|
CODEGEN_INLINE_POLICY void Kotlin_mm_safePointFunctionPrologue() RUNTIME_NOTHROW;
|
|
CODEGEN_INLINE_POLICY void Kotlin_mm_safePointWhileLoopBody() RUNTIME_NOTHROW;
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
struct FrameOverlay {
|
|
void* arena;
|
|
FrameOverlay* previous;
|
|
// As they go in pair, sizeof(FrameOverlay) % sizeof(void*) == 0 is always held.
|
|
int32_t parameters;
|
|
int32_t count;
|
|
};
|
|
|
|
// Class holding reference to an object, holding object during C++ scope.
|
|
class ObjHolder {
|
|
public:
|
|
ObjHolder() : obj_(nullptr) {
|
|
EnterFrame(frame(), 0, sizeof(*this)/sizeof(void*));
|
|
}
|
|
|
|
explicit ObjHolder(const ObjHeader* obj) {
|
|
EnterFrame(frame(), 0, sizeof(*this)/sizeof(void*));
|
|
::SetStackRef(slot(), obj);
|
|
}
|
|
|
|
~ObjHolder() {
|
|
LeaveFrame(frame(), 0, sizeof(*this)/sizeof(void*));
|
|
}
|
|
|
|
ObjHeader* obj() { return obj_; }
|
|
|
|
const ObjHeader* obj() const { return obj_; }
|
|
|
|
ObjHeader** slot() {
|
|
return &obj_;
|
|
}
|
|
|
|
void clear() { ::ZeroStackRef(&obj_); }
|
|
|
|
private:
|
|
ObjHeader** frame() { return reinterpret_cast<ObjHeader**>(&frame_); }
|
|
|
|
FrameOverlay frame_;
|
|
ObjHeader* obj_;
|
|
};
|
|
|
|
class ExceptionObjHolder {
|
|
public:
|
|
#if !KONAN_NO_EXCEPTIONS
|
|
static void Throw(ObjHeader* exception) RUNTIME_NORETURN;
|
|
|
|
ObjHeader* GetExceptionObject() noexcept;
|
|
#endif
|
|
|
|
// Exceptions are not on a hot path, so having virtual dispatch is fine.
|
|
virtual ~ExceptionObjHolder() = default;
|
|
};
|
|
|
|
namespace kotlin {
|
|
namespace mm {
|
|
|
|
// Returns the MemoryState for the current thread.
|
|
// For the new MM, the current thread must be attached to the runtime.
|
|
// For the legacy MM, returns nullptr if called on a thread that is not attached to the runtime.
|
|
// Try not to use it very often, as (1) thread local access can be slow on some platforms,
|
|
// (2) TLS gets deallocated before our thread destruction hooks run.
|
|
MemoryState* GetMemoryState() noexcept;
|
|
|
|
|
|
// TODO: Replace with direct access to ThreadRegistry when the legacy MM is gone.
|
|
// Checks if the current thread is attached to the runtime.
|
|
// This function accesses a TLS variable, so it must not be called from a thread destructor.
|
|
bool IsCurrentThreadRegistered() noexcept;
|
|
|
|
} // namespace mm
|
|
|
|
enum class ThreadState {
|
|
kRunnable, kNative
|
|
};
|
|
|
|
ThreadState GetThreadState(MemoryState* thread) noexcept;
|
|
|
|
inline ThreadState GetThreadState() noexcept {
|
|
return GetThreadState(mm::GetMemoryState());
|
|
}
|
|
|
|
// Switches the state of the given thread to `newState` and returns the previous thread state.
|
|
ALWAYS_INLINE ThreadState SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant = false) noexcept;
|
|
|
|
// Asserts that the given thread is in the given state.
|
|
ALWAYS_INLINE void AssertThreadState(MemoryState* thread, ThreadState expected) noexcept;
|
|
ALWAYS_INLINE void AssertThreadState(MemoryState* thread, std::initializer_list<ThreadState> expected) noexcept;
|
|
|
|
// Asserts that the current thread is in the the given state.
|
|
ALWAYS_INLINE inline void AssertThreadState(ThreadState expected) noexcept {
|
|
// Avoid redundant TLS access in GetMemoryState if runtime asserts are disabled.
|
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
|
AssertThreadState(mm::GetMemoryState(), expected);
|
|
}
|
|
}
|
|
|
|
ALWAYS_INLINE inline void AssertThreadState(std::initializer_list<ThreadState> expected) noexcept {
|
|
// Avoid redundant TLS access in GetMemoryState if runtime asserts are disabled.
|
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
|
AssertThreadState(mm::GetMemoryState(), expected);
|
|
}
|
|
}
|
|
|
|
// Scopely sets the given thread state for the given thread.
|
|
class ThreadStateGuard final : private MoveOnly {
|
|
public:
|
|
// Do not set any state. Useful to create a variable to move another guard into.
|
|
ThreadStateGuard() : thread_(nullptr), oldState_(ThreadState::kNative), reentrant_(false) {}
|
|
|
|
// Set the state for the given thread.
|
|
ThreadStateGuard(MemoryState* thread, ThreadState state, bool reentrant = false) noexcept : thread_(thread), reentrant_(reentrant) {
|
|
oldState_ = SwitchThreadState(thread_, state, reentrant_);
|
|
}
|
|
|
|
// Sets the state for the current thread.
|
|
explicit ThreadStateGuard(ThreadState state, bool reentrant = false) noexcept
|
|
: ThreadStateGuard(mm::GetMemoryState(), state, reentrant) {};
|
|
|
|
ThreadStateGuard(ThreadStateGuard&& other) noexcept
|
|
: thread_(other.thread_), oldState_(other.oldState_), reentrant_(other.reentrant_) {
|
|
other.thread_ = nullptr;
|
|
}
|
|
|
|
~ThreadStateGuard() noexcept {
|
|
if (thread_ != nullptr) {
|
|
SwitchThreadState(thread_, oldState_, reentrant_);
|
|
}
|
|
}
|
|
|
|
ThreadStateGuard& operator=(ThreadStateGuard&& other) noexcept {
|
|
thread_ = other.thread_;
|
|
oldState_ = other.oldState_;
|
|
reentrant_ = other.reentrant_;
|
|
other.thread_ = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
MemoryState* thread_;
|
|
ThreadState oldState_;
|
|
bool reentrant_;
|
|
};
|
|
|
|
// Scopely sets the kRunnable thread state for the current thread,
|
|
// and initializes runtime if needed for new MM.
|
|
// No-op for old GC.
|
|
class CalledFromNativeGuard final : private Pinned {
|
|
public:
|
|
ALWAYS_INLINE CalledFromNativeGuard(bool reentrant = false) noexcept;
|
|
|
|
~CalledFromNativeGuard() noexcept {
|
|
SwitchThreadState(thread_, oldState_, reentrant_);
|
|
}
|
|
private:
|
|
MemoryState* thread_;
|
|
ThreadState oldState_;
|
|
bool reentrant_;
|
|
};
|
|
|
|
class CurrentFrameGuard : Pinned {
|
|
public:
|
|
CurrentFrameGuard() : frame_(getCurrentFrame()) {}
|
|
~CurrentFrameGuard() { SetCurrentFrame(reinterpret_cast<ObjHeader**>(frame_)); }
|
|
private:
|
|
FrameOverlay* frame_;
|
|
};
|
|
|
|
template <ThreadState state, typename R, typename... Args>
|
|
ALWAYS_INLINE inline R CallWithThreadState(R(*function)(Args...), Args... args) {
|
|
ThreadStateGuard guard(state);
|
|
return function(std::forward<Args>(args)...);
|
|
}
|
|
|
|
class NativeOrUnregisteredThreadGuard final : private MoveOnly {
|
|
public:
|
|
explicit NativeOrUnregisteredThreadGuard(bool reentrant = false) noexcept {
|
|
// The default ctor of ThreadStateGuard doesn't set the state.
|
|
// So the actual state switching is performed only if the thread is registered.
|
|
if (kotlin::mm::IsCurrentThreadRegistered()) {
|
|
backingGuard_ = kotlin::ThreadStateGuard(kotlin::ThreadState::kNative, reentrant);
|
|
}
|
|
}
|
|
|
|
private:
|
|
ThreadStateGuard backingGuard_;
|
|
};
|
|
|
|
extern const bool kSupportsMultipleMutators;
|
|
|
|
void StartFinalizerThreadIfNeeded() noexcept;
|
|
bool FinalizersThreadIsRunning() noexcept;
|
|
|
|
} // namespace kotlin
|
|
|
|
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processObjectInMark(void* state, ObjHeader* object);
|
|
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processArrayInMark(void* state, ObjHeader* object);
|
|
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processFieldInMark(void* state, ObjHeader* field);
|
|
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processEmptyObjectInMark(void* state, ObjHeader* object);
|
|
|
|
#endif // RUNTIME_MEMORY_H
|