[K/N] Remove legacy MM runtime modules ^KT-59121

This commit is contained in:
Alexander Shabalin
2023-08-10 11:58:40 +02:00
committed by Space Team
parent f6e9e9379d
commit 288163437d
11 changed files with 0 additions and 5161 deletions
-23
View File
@@ -206,20 +206,6 @@ bitcode {
onlyIf { target.supportsLibBacktrace() }
}
module("strict") {
headersDirs.from(files("src/main/cpp"))
sourceSets {
main {}
}
}
module("relaxed") {
headersDirs.from(files("src/main/cpp"))
sourceSets {
main {}
}
}
module("profileRuntime") {
srcRoot.set(layout.projectDirectory.dir("src/profile_runtime"))
sourceSets {
@@ -243,15 +229,6 @@ bitcode {
}
}
module("legacy_memory_manager") {
srcRoot.set(layout.projectDirectory.dir("src/legacymm"))
headersDirs.from(files("src/main/cpp"))
sourceSets {
main {}
testFixtures {}
}
}
module("experimental_memory_manager") {
srcRoot.set(layout.projectDirectory.dir("src/mm"))
headersDirs.from(files("src/gcScheduler/common/cpp", "src/gc/common/cpp", "src/main/cpp"))
@@ -1,534 +0,0 @@
/*
* Copyright 2010-2020 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 KONAN_NO_THREADS
#define WITH_WORKERS 1
#endif
#include "Atomic.h"
#include "KAssert.h"
#include "Memory.h"
#include "MemoryPrivate.hpp"
#include "Natives.h"
#include "Porting.h"
#include "Types.h"
#include "std_support/Deque.hpp"
#include "std_support/New.hpp"
#include "std_support/UnorderedMap.hpp"
#include "std_support/UnorderedSet.hpp"
#include "std_support/Vector.hpp"
#if WITH_WORKERS
#include <pthread.h>
#include "PthreadUtils.h"
#endif
#if WITH_WORKERS
// Define to 1 to print collector traces.
#define TRACE_COLLECTOR 0
#if TRACE_COLLECTOR
#define COLLECTOR_LOG(...) konan::consolePrintf(__VA_ARGS__);
#else
#define COLLECTOR_LOG(...)
#endif
using namespace kotlin;
/**
* Theory of operations:
*
* Kotlin/Native runtime has concurrent cyclic garbage collection for the shared mutable objects,
* such as `AtomicReference` and `FreezableAtomicReference` instances (further known as the atomic rootset).
* We perform such analysis by iterating over the transitive closure of the atomic rootset, and computing
* aggregated inner reference counter for rootset elements over this transitive closure.
* Collector runs in its own thread and is started by an explicit request or after certain time interval since last
* collection passes, thus its operation does not affect UI responsiveness in most cases.
* Atomic rootset is built by maintaining the set of all atomic and freezable atomic references objects.
* Elements whose transitive closure inner reference count matches the actual reference count are ones
* belonging to the garbage cycles and thus can be discarded.
* We ignore elements reachable from objects having external references (i.e. inner rc != real rc).
* If during computations of the aggregated RC there were modifications in the reference counts of
* elements of the atomic rootset:
* - if it is being increased, then someone already got an external reference to this element, thus we may not
* end up matching the inner reference count anyway
* - if it is being decreased and object become garbage, it will be collected next time
* If transitive closure of the atomic rootset mutates, it could only happen via changing the atomics references,
* as all elements of this closure are frozen.
* To handle such mutations we keep collector flag, which is cleared before analysis and set on every
* atomic reference value update. If flag's value changes - collector restarts its analysis.
* There are not so much of complications in this algorithm due to the delayed reference counting as if there's a
* stack reference to the shared object - it's reflected in the reference counter (see rememberNewContainer()).
* We release objects found by the collector on a rendezvouz callback, but not on the main thread,
* to keep UI responsive, as taking GC lock can take time, sometimes.
*/
namespace {
class Locker {
pthread_mutex_t* lock_;
public:
Locker(pthread_mutex_t* alock): lock_(alock) {
pthread_mutex_lock(lock_);
}
~Locker() {
pthread_mutex_unlock(lock_);
}
};
template <typename func>
inline void traverseObjectFields(ObjHeader* obj, func process) {
RuntimeAssert(obj != nullptr, "Must be non null");
const TypeInfo* typeInfo = obj->type_info();
if (typeInfo != theArrayTypeInfo) {
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
ObjHeader** location = reinterpret_cast<ObjHeader**>(
reinterpret_cast<uintptr_t>(obj) + typeInfo->objOffsets_[index]);
process(location);
}
} else {
ArrayHeader* array = obj->array();
for (uint32_t index = 0; index < array->count_; index++) {
process(ArrayAddressOfElementAt(array, index));
}
}
}
inline bool isAtomicReference(ObjHeader* obj) {
return (obj->type_info()->flags_ & TF_LEAK_DETECTOR_CANDIDATE) != 0;
}
#define CHECK_CALL(call, message) RuntimeCheck((call) == 0, message)
class CyclicCollector {
pthread_mutex_t lock_;
pthread_mutex_t timestampLock_;
pthread_cond_t cond_;
pthread_t gcThread_;
int currentAliveWorkers_;
int gcRunning_;
int mutatedAtomics_;
int pendingRelease_;
bool shallRunCollector_;
bool terminateCollector_;
int32_t currentTick_;
int32_t lastTick_;
int64_t lastTimestampUs_;
void* mainWorker_;
std_support::unordered_set<ObjHeader*> rootset_;
std_support::unordered_set<ObjHeader*> toRelease_;
public:
CyclicCollector() {
CHECK_CALL(pthread_mutex_init(&lock_, nullptr), "Cannot init collector mutex");
CHECK_CALL(pthread_mutex_init(&timestampLock_, nullptr), "Cannot init collector timestamp mutex");
CHECK_CALL(pthread_cond_init(&cond_, nullptr), "Cannot init collector condition");
CHECK_CALL(pthread_create(&gcThread_, nullptr, gcWorkerRoutine, this), "Cannot start collector thread");
}
void clear() {
Locker lock(&lock_);
rootset_.clear();
toRelease_.clear();
}
void terminate(bool enabled) {
{
Locker locker(&lock_);
terminateCollector_ = true;
if (enabled) shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
}
// TODO: improve waiting for collector termination.
while (atomicGet(&terminateCollector_)) {}
releasePendingUnlocked(nullptr);
}
~CyclicCollector() {
pthread_cond_destroy(&cond_);
pthread_mutex_destroy(&lock_);
pthread_mutex_destroy(&timestampLock_);
}
static void* gcWorkerRoutine(void* argument) {
CyclicCollector* thiz = reinterpret_cast<CyclicCollector*>(argument);
thiz->gcProcessor();
return nullptr;
}
void gcProcessor() {
{
Locker locker(&lock_);
std_support::deque<ObjHeader*> toVisit;
std_support::unordered_set<ObjHeader*> visited;
std_support::unordered_map<ObjHeader*, int> sideRefCounts;
int restartCount = 0;
while (!terminateCollector_) {
CHECK_CALL(pthread_cond_wait(&cond_, &lock_), "Cannot wait collector condition");
if (!shallRunCollector_) continue;
atomicSet(&gcRunning_, 1);
restartCount = 0;
restart:
COLLECTOR_LOG("start cycle GC\n");
if (restartCount > 10 && !terminateCollector_) {
COLLECTOR_LOG("wait for some time to avoid GC thrashing\n");
uint64_t nsDelta = 1000LL * 1000LL * (restartCount - 10);
WaitOnCondVar(&cond_, &lock_, nsDelta);
}
atomicSet(&mutatedAtomics_, 0);
visited.clear();
toVisit.clear();
sideRefCounts.clear();
for (auto* root: rootset_) {
// We only care about frozen values here, as only they could become part of shared cycles.
if (!containerFor(root)->frozen()) continue;
COLLECTOR_LOG("process root %p\n", root);
toVisit.push_back(root);
sideRefCounts[root] = 0;
}
while (toVisit.size() > 0) {
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during rootset visit\n")
restartCount++;
goto restart;
}
auto* obj = toVisit.front();
toVisit.pop_front();
COLLECTOR_LOG("visit %s%p\n", isAtomicReference(obj) ? "atomic " : "", obj);
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
RuntimeCheck(objContainer->shareable(), "Must be shareable");
if (visited.count(obj) == 0) {
visited.insert(obj);
traverseObjectFields(obj, [&toVisit, obj, &sideRefCounts](ObjHeader** location) {
ObjHeader* ref = *location;
if (ref != nullptr) {
COLLECTOR_LOG("object field %p in %p\n", ref, obj)
int increment;
// We shall not account for edges inside the same frozen container, unless it originates
// from an atomic reference.
if (isAtomicReference(obj) || (containerFor(obj) != containerFor(ref))) {
COLLECTOR_LOG("counting %p -> %p\n", obj, ref)
increment = 1;
} else {
COLLECTOR_LOG("not counting %p -> %p\n", obj, ref)
increment = 0;
}
sideRefCounts[ref] += increment;
toVisit.push_back(ref);
}
});
}
}
// Now find all elements with external references, and mark objects reachable from them as non suitable
// for collection by setting their side reference count to -1.
toVisit.clear();
for (auto it: sideRefCounts) {
auto* obj = it.first;
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
int refCount;
// If object is in aggregated container - sum up RC for all elements.
if (objContainer->objectCount() != 1) {
RuntimeAssert(objContainer->frozen(), "Must be frozen aggregate");
ContainerHeader** subContainer = reinterpret_cast<ContainerHeader**>(objContainer + 1);
refCount = 0;
for (uint32_t i = 0; i < objContainer->objectCount(); ++i) {
auto* componentObj = reinterpret_cast<ObjHeader*>((*subContainer) + 1);
refCount += sideRefCounts[componentObj];
subContainer++;
}
} else {
refCount = it.second;
}
RuntimeAssert(refCount <= objContainer->refCount(), "Must properly count inner refs");
if (refCount != objContainer->refCount()) {
COLLECTOR_LOG("for %p mismatched RC: %d vs %d, adding as possible root\n", obj, refCount, objContainer->refCount())
toVisit.push_back(it.first);
}
}
visited.clear();
while (toVisit.size() > 0) {
auto* obj = toVisit.front();
toVisit.pop_front();
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
RuntimeCheck(objContainer->shareable(), "Must be shareable");
sideRefCounts[obj] = -1;
visited.insert(obj);
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during reachable visit\n")
restartCount++;
goto restart;
}
traverseObjectFields(obj, [&toVisit, &visited](ObjHeader** location) {
ObjHeader* ref = *location;
if (ref != nullptr && (visited.count(ref) == 0)) {
toVisit.push_back(ref);
}
});
}
// Now release all atomic roots with matching reference counters, as only their destruction is controlled.
for (auto it: sideRefCounts) {
auto* obj = it.first;
// Only do that for atomic rootset elements. For them we also do not have sum up references from
// other elements of an aggregate, as atomic references are always in single object containers.
if (!isAtomicReference(obj)) {
continue;
}
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during matching check\n")
restartCount++;
goto restart;
}
auto* objContainer = containerFor(obj);
if (!objContainer->frozen()) continue;
RuntimeAssert(objContainer->objectCount() == 1, "Must be single object");
COLLECTOR_LOG("for %p inner %d actual %d\n", obj, it.second, objContainer->refCount());
// All references are inner. We compare the number of counted
// inner references with the number of non-stack references and per-thread ownership value
// (see rememberNewContainer()).
if (it.second == objContainer->refCount()) {
COLLECTOR_LOG("adding %p to release candidates\n", it.first);
toRelease_.insert(it.first);
}
}
if (toRelease_.size() > 0)
atomicSet(&pendingRelease_, 1);
atomicSet(&gcRunning_, 0);
shallRunCollector_ = false;
COLLECTOR_LOG("end cycle GC\n");
}
}
atomicSet(&terminateCollector_, false);
}
void addWorker(void* worker) {
suggestLockRelease();
Locker lock(&lock_);
currentAliveWorkers_++;
if (mainWorker_ == nullptr) mainWorker_ = worker;
}
void removeWorker(void* worker, bool enabled) {
suggestLockRelease();
Locker lock(&lock_);
// When exiting the worker - we shall collect the cyclic garbage here.
if (enabled) {
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
}
currentAliveWorkers_--;
}
void addRoot(ObjHeader* obj) {
COLLECTOR_LOG("add root %p\n", obj);
// TODO: we can only add root when collector is not processing, which looks like a limitation,
// instead we can add elements to the side buffer or have a separate lock for that.
suggestLockRelease();
Locker lock(&lock_);
rootset_.insert(obj);
}
void removeRoot(ObjHeader* obj) {
COLLECTOR_LOG("remove root %p\n", obj);
// Note that we can only remove root when the collector is not processing.
suggestLockRelease();
Locker lock(&lock_);
toRelease_.erase(obj);
rootset_.erase(obj);
}
void mutateRoot(ObjHeader* newValue) {
// TODO: consider optimization, when clearing value (setting to null) in atomic reference shall not lead
// to invalidation of the collector analysis state.
atomicSet(&mutatedAtomics_, 1);
}
void suggestLockRelease() {
atomicSet(&mutatedAtomics_, 1);
}
bool checkIfShallCollect() {
auto tick = atomicAdd(&currentTick_, 1);
auto delta = tick - atomicGet(&lastTick_);
if (delta > 10 || delta < 0) {
auto currentTimestampUs = konan::getTimeMicros();
#if KONAN_NO_64BIT_ATOMIC
if (currentTimestampUs - *(volatile int64_t*)&lastTimestampUs_ > 10000) {
#else
if (currentTimestampUs - atomicGet(&lastTimestampUs_) > 10000) {
#endif // KONAN_NO_64BIT_ATOMIC
// Do we care if this lock is not here?
Locker locker(&timestampLock_);
lastTick_ = currentTick_;
lastTimestampUs_ = currentTimestampUs;
return true;
}
}
return false;
}
void releasePendingUnlocked(void* worker) {
// We are not doing that on the UI thread, as taking lock is slow, unless
// it happens on deinit of the collector or if there are no other workers.
if ((atomicGet(&pendingRelease_) != 0) && ((worker != mainWorker_) || (currentAliveWorkers_ == 1))) {
std_support::vector<ObjHeader*> heapRefsToRelease;
{
suggestLockRelease();
Locker locker(&lock_);
COLLECTOR_LOG("clearing %d release candidates on %p\n", toRelease_.size(), worker);
for (auto* it: toRelease_) {
COLLECTOR_LOG("clear references in %p\n", it)
traverseObjectFields(it, [&heapRefsToRelease](ObjHeader** location) {
// Avoid using ZeroHeapRef here: it can provoke garbageCollect() which would then stuck on taking [lock_]
// (which is already taken above).
auto* value = *location;
if (reinterpret_cast<uintptr_t>(value) > 1) {
*location = nullptr;
heapRefsToRelease.push_back(value);
}
});
}
toRelease_.clear();
atomicSet(&pendingRelease_, 0);
}
for (auto* it: heapRefsToRelease) {
ReleaseHeapRef(it);
}
}
}
void collectorCallaback(void* worker) {
if (atomicGet(&gcRunning_) != 0) return;
releasePendingUnlocked(worker);
if (checkIfShallCollect()) {
Locker locker(&lock_);
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
}
}
void scheduleGarbageCollect() {
if (atomicGet(&gcRunning_) != 0) return;
Locker lock(&lock_);
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
}
void localGC() {
// We just need to take GC lock here, to avoid release of object we walk on.
// TODO: consider optimization without taking the lock and just notifying collector via an atomic.
suggestLockRelease();
Locker locker(&lock_);
}
};
CyclicCollector* cyclicCollector = nullptr;
} // namespace
#endif // WITH_WORKERS
void cyclicInit() {
#if WITH_WORKERS
RuntimeAssert(cyclicCollector == nullptr, "Must be not yet inited");
cyclicCollector = new (std_support::kalloc) CyclicCollector();
#endif
}
void cyclicDeinit(bool enabled) {
#if WITH_WORKERS
RuntimeAssert(cyclicCollector != nullptr, "Must be inited");
auto* local = cyclicCollector;
local->terminate(enabled);
cyclicCollector = nullptr;
// Workaround data race with threads non-atomically reading and then using [cyclicCollector].
// std_support::kdelete(local);
// Note: memory leaks here indeed, but usually it happens once per application.
// Make best effort to clean some memory:
local->clear();
#endif // WITH_WORKERS
}
void cyclicAddWorker(void* worker) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->addWorker(worker);
#endif // WITH_WORKERS
}
void cyclicRemoveWorker(void* worker, bool enabled) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->removeWorker(worker, enabled);
#endif // WITH_WORKERS
}
void cyclicCollectorCallback(void* worker) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->collectorCallaback(worker);
#endif // WITH_WORKERS
}
void cyclicScheduleGarbageCollect() {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->scheduleGarbageCollect();
#endif // WITH_WORKERS
}
void cyclicAddAtomicRoot(ObjHeader* obj) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->addRoot(obj);
#endif // WITH_WORKERS
}
void cyclicRemoveAtomicRoot(ObjHeader* obj) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->removeRoot(obj);
#endif // WITH_WORKERS
}
void cyclicMutateAtomicRoot(ObjHeader* newValue) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->mutateRoot(newValue);
#endif // WITH_WORKERS
}
void cyclicLocalGC() {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->localGC();
#endif // WITH_WORKERS
}
@@ -1,17 +0,0 @@
#ifndef RUNTIME_CYCLIC_COLLECTOR_H
#define RUNTIME_CYCLIC_COLLECTOR_H
struct ObjHeader;
void cyclicInit();
void cyclicDeinit(bool enabled);
void cyclicAddWorker(void* worker);
void cyclicRemoveWorker(void* worker, bool enabled);
void cyclicAddAtomicRoot(ObjHeader* obj);
void cyclicRemoveAtomicRoot(ObjHeader* obj);
void cyclicMutateAtomicRoot(ObjHeader* newValue);
void cyclicCollectorCallback(void* worker);
void cyclicLocalGC();
void cyclicScheduleGarbageCollect();
#endif // RUNTIME_CYCLIC_COLLECTOR_H
File diff suppressed because it is too large Load Diff
@@ -1,344 +0,0 @@
/*
* Copyright 2010-2017 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_MEMORYPRIVATE_HPP
#define RUNTIME_MEMORYPRIVATE_HPP
#include "Memory.h"
typedef enum {
// Those bit masks are applied to refCount_ field.
// Container is normal thread-local container.
CONTAINER_TAG_LOCAL = 0,
// Container is frozen, could only refer to other frozen objects.
// Refcounter update is atomics.
CONTAINER_TAG_FROZEN = 1 | 1, // shareable
// Stack container, no need to free, children cleanup still shall be there.
CONTAINER_TAG_STACK = 2,
// Atomic container, reference counter is atomically updated.
CONTAINER_TAG_SHARED = 3 | 1, // shareable
// Shift to get actual counter.
CONTAINER_TAG_SHIFT = 2,
// Actual value to increment/decrement container by. Tag is in lower bits.
CONTAINER_TAG_INCREMENT = 1 << CONTAINER_TAG_SHIFT,
// Mask for container type.
CONTAINER_TAG_MASK = CONTAINER_TAG_INCREMENT - 1,
// Shift to get actual object count, if has it.
CONTAINER_TAG_GC_SHIFT = 7,
CONTAINER_TAG_GC_MASK = (1 << CONTAINER_TAG_GC_SHIFT) - 1,
CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT,
// Color mask of a container.
CONTAINER_TAG_COLOR_SHIFT = 3,
CONTAINER_TAG_GC_COLOR_MASK = (1 << CONTAINER_TAG_COLOR_SHIFT) - 1,
// Colors.
// In use or free.
CONTAINER_TAG_GC_BLACK = 0,
// Possible member of garbage cycle.
CONTAINER_TAG_GC_GRAY = 1,
// Member of garbage cycle.
CONTAINER_TAG_GC_WHITE = 2,
// Possible root of cycle.
CONTAINER_TAG_GC_PURPLE = 3,
// Acyclic.
CONTAINER_TAG_GC_GREEN = 4,
// Orange and red are currently unused.
// Candidate cycle awaiting epoch.
CONTAINER_TAG_GC_ORANGE = 5,
// Candidate cycle awaiting sigma computation.
CONTAINER_TAG_GC_RED = 6,
// Individual state bits used during GC and freezing.
CONTAINER_TAG_GC_MARKED = 1 << CONTAINER_TAG_COLOR_SHIFT,
CONTAINER_TAG_GC_BUFFERED = 1 << (CONTAINER_TAG_COLOR_SHIFT + 1),
CONTAINER_TAG_GC_SEEN = 1 << (CONTAINER_TAG_COLOR_SHIFT + 2),
// If indeed has more that one object.
CONTAINER_TAG_GC_HAS_OBJECT_COUNT = 1 << (CONTAINER_TAG_COLOR_SHIFT + 3)
} ContainerTag;
// Header of all container objects. Contains reference counter.
struct ContainerHeader {
// Reference counter of container. Uses CONTAINER_TAG_SHIFT, lower bits of counter
// for container type (for polymorphism in ::Release()).
uint32_t refCount_;
// Number of objects in the container.
uint32_t objectCount_;
inline bool local() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_LOCAL;
}
inline bool frozen() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_FROZEN;
}
inline void freeze() {
refCount_ = (refCount_ & ~CONTAINER_TAG_MASK) | CONTAINER_TAG_FROZEN;
}
inline void makeShared() {
refCount_ = (refCount_ & ~CONTAINER_TAG_MASK) | CONTAINER_TAG_SHARED;
}
inline bool shared() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_SHARED;
}
inline bool shareable() const {
return (tag() & 1) != 0; // CONTAINER_TAG_FROZEN || CONTAINER_TAG_SHARED
}
inline bool stack() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_STACK;
}
inline int refCount() const {
return (int)refCount_ >> CONTAINER_TAG_SHIFT;
}
inline void setRefCount(unsigned refCount) {
refCount_ = tag() | (refCount << CONTAINER_TAG_SHIFT);
}
template <bool Atomic>
inline void incRefCount() {
#ifdef KONAN_NO_THREADS
refCount_ += CONTAINER_TAG_INCREMENT;
#else
if (Atomic)
__sync_add_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT);
else
refCount_ += CONTAINER_TAG_INCREMENT;
#endif
}
template <bool Atomic>
inline bool tryIncRefCount() {
if (Atomic) {
while (true) {
uint32_t currentRefCount_ = refCount_;
if (((int)currentRefCount_ >> CONTAINER_TAG_SHIFT) > 0) {
if (compareAndSet(&refCount_, currentRefCount_, currentRefCount_ + CONTAINER_TAG_INCREMENT)) {
return true;
}
} else {
return false;
}
}
} else {
// Note: tricky case here is doing this during cycle collection.
// This can actually happen due to deallocation hooks.
// Fortunately by this point reference counts have been made precise again.
if (refCount() > 0) {
incRefCount</* Atomic = */ false>();
return true;
} else {
return false;
}
}
}
template <bool Atomic>
inline int decRefCount() {
#ifdef KONAN_NO_THREADS
int value = refCount_ -= CONTAINER_TAG_INCREMENT;
#else
int value = Atomic ?
__sync_sub_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT) : refCount_ -= CONTAINER_TAG_INCREMENT;
#endif
return value >> CONTAINER_TAG_SHIFT;
}
inline int decRefCount() {
#ifdef KONAN_NO_THREADS
int value = refCount_ -= CONTAINER_TAG_INCREMENT;
#else
int value = shareable() ?
__sync_sub_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT) : refCount_ -= CONTAINER_TAG_INCREMENT;
#endif
return value >> CONTAINER_TAG_SHIFT;
}
inline unsigned tag() const {
return refCount_ & CONTAINER_TAG_MASK;
}
inline unsigned objectCount() const {
return (objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) != 0 ?
(objectCount_ >> CONTAINER_TAG_GC_SHIFT) : 1;
}
inline void incObjectCount() {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) != 0, "Must have object count");
objectCount_ += CONTAINER_TAG_GC_INCREMENT;
}
inline void setObjectCount(int count) {
if (count == 1) {
objectCount_ &= ~CONTAINER_TAG_GC_HAS_OBJECT_COUNT;
} else {
objectCount_ = (count << CONTAINER_TAG_GC_SHIFT) | CONTAINER_TAG_GC_HAS_OBJECT_COUNT;
}
}
inline unsigned containerSize() const {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0, "Must be single-object");
return (objectCount_ >> CONTAINER_TAG_GC_SHIFT);
}
inline void setContainerSize(unsigned size) {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0, "Must not have object count");
objectCount_ = (objectCount_ & CONTAINER_TAG_GC_MASK) | (size << CONTAINER_TAG_GC_SHIFT);
}
inline bool hasContainerSize() {
return (objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0;
}
inline unsigned color() const {
return objectCount_ & CONTAINER_TAG_GC_COLOR_MASK;
}
inline void setColorAssertIfGreen(unsigned color) {
RuntimeAssert(this->color() != CONTAINER_TAG_GC_GREEN, "Must not be green");
setColorEvenIfGreen(color);
}
inline void setColorEvenIfGreen(unsigned color) {
// TODO: do we need atomic color update?
objectCount_ = (objectCount_ & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
}
inline void setColorUnlessGreen(unsigned color) {
// TODO: do we need atomic color update?
unsigned objectCount = objectCount_;
if ((objectCount & CONTAINER_TAG_GC_COLOR_MASK) != CONTAINER_TAG_GC_GREEN)
objectCount_ = (objectCount & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
}
inline bool buffered() const {
return (objectCount_ & CONTAINER_TAG_GC_BUFFERED) != 0;
}
inline void setBuffered() {
objectCount_ |= CONTAINER_TAG_GC_BUFFERED;
}
inline void resetBuffered() {
objectCount_ &= ~CONTAINER_TAG_GC_BUFFERED;
}
inline bool marked() const {
return (objectCount_ & CONTAINER_TAG_GC_MARKED) != 0;
}
inline void mark() {
objectCount_ |= CONTAINER_TAG_GC_MARKED;
}
inline void unMark() {
objectCount_ &= ~CONTAINER_TAG_GC_MARKED;
}
inline bool seen() const {
return (objectCount_ & CONTAINER_TAG_GC_SEEN) != 0;
}
inline void setSeen() {
objectCount_ |= CONTAINER_TAG_GC_SEEN;
}
inline void resetSeen() {
objectCount_ &= ~CONTAINER_TAG_GC_SEEN;
}
// Following operations only work on freed container which is in finalization queue.
// We cannot use 'this' here, as it conflicts with aliasing analysis in clang.
inline void setNextLink(ContainerHeader* next) {
*reinterpret_cast<ContainerHeader**>(this + 1) = next;
}
inline ContainerHeader* nextLink() {
return *reinterpret_cast<ContainerHeader**>(this + 1);
}
};
ALWAYS_INLINE ContainerHeader* containerFor(const ObjHeader* obj);
// Header for the meta-object.
struct MetaObjHeader {
// Pointer to the type info. Must be first, to match ArrayHeader and ObjHeader layout.
const TypeInfo* typeInfo_;
// Container pointer.
ContainerHeader* container_;
#ifdef KONAN_OBJC_INTEROP
void* associatedObject_;
#endif
// Flags for the object state.
int32_t flags_;
struct {
// Strong reference to the counter object.
ObjHeader* counter_;
} WeakReference;
};
extern "C" {
#define MODEL_VARIANTS(returnType, name, ...) \
returnType name##Strict(__VA_ARGS__) RUNTIME_NOTHROW; \
returnType name##Relaxed(__VA_ARGS__) RUNTIME_NOTHROW;
OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* type_info, int32_t elements);
MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, ZeroStackRef, ObjHeader** location);
MODEL_VARIANTS(void, UpdateStackRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateHeapRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateHeapRefIfNull, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateReturnRef, ObjHeader** returnSlot, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateHeapRefsInsideOneArray, const ArrayHeader* array, int fromIndex, int toIndex,
int count);
MODEL_VARIANTS(void, EnterFrame, ObjHeader** start, int parameters, int count);
MODEL_VARIANTS(void, LeaveFrame, ObjHeader** start, int parameters, int count);
MODEL_VARIANTS(void, SetCurrentFrame, ObjHeader** start);
void ReleaseHeapRef(const ObjHeader* object) RUNTIME_NOTHROW;
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
MODEL_VARIANTS(void, ReleaseHeapRefNoCollect, const ObjHeader* object);
bool TryAddHeapRef(const ObjHeader* object);
void ReleaseHeapRefNoCollect(const 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);
} // extern "C"
#endif // RUNTIME_MEMORYPRIVATE_HPP
@@ -1,208 +0,0 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "MemorySharedRefs.hpp"
#include "Exceptions.h"
#include "MemoryPrivate.hpp"
#include "Runtime.h"
namespace {
inline bool isForeignRefAccessible(ObjHeader* object, ForeignRefContext context) {
// If runtime has not been initialized on this thread, then the object is either unowned or shared.
// In the former case initialized runtime is required to throw exceptions
// in the latter case -- to provide proper execution context for caller.
// TODO: this probably can't be called in uninitialized state in the new MM.
Kotlin_initRuntimeIfNeeded();
return IsForeignRefAccessible(object, context);
}
RUNTIME_NORETURN inline void throwIllegalSharingException(ObjHeader* object) {
// TODO: add some info about the context.
// Note: retrieving 'type_info()' is supposed to be correct even for unowned object.
ThrowIllegalObjectSharingException(object->type_info(), object);
}
RUNTIME_NORETURN inline void terminateWithIllegalSharingException(ObjHeader* object) {
#if KONAN_NO_EXCEPTIONS
// This will terminate.
throwIllegalSharingException(object);
#else
try {
throwIllegalSharingException(object);
} catch (...) {
// A trick to terminate with unhandled exception. This will print a stack trace
// and write to iOS crash log.
std::terminate();
}
#endif
}
template <ErrorPolicy errorPolicy>
bool ensureRefAccessible(ObjHeader* object, ForeignRefContext context) {
static_assert(errorPolicy != ErrorPolicy::kIgnore, "Must've been handled by specialization");
if (isForeignRefAccessible(object, context)) {
return true;
}
switch (errorPolicy) {
case ErrorPolicy::kDefaultValue:
return false;
case ErrorPolicy::kThrow:
throwIllegalSharingException(object);
case ErrorPolicy::kTerminate:
terminateWithIllegalSharingException(object);
}
}
template <>
bool ensureRefAccessible<ErrorPolicy::kIgnore>(ObjHeader* object, ForeignRefContext context) {
return true;
}
} // namespace
void KRefSharedHolder::initLocal(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
context_ = InitLocalForeignRef(obj);
obj_ = obj;
}
void KRefSharedHolder::init(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
context_ = InitForeignRef(obj);
obj_ = obj;
}
template <ErrorPolicy errorPolicy>
ObjHeader* KRefSharedHolder::ref() const {
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
return nullptr;
}
AdoptReferenceFromSharedVariable(obj_);
return obj_;
}
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kDefaultValue>() const;
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kThrow>() const;
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kTerminate>() const;
void KRefSharedHolder::dispose() {
if (obj_ == nullptr) {
// To handle the case when it is not initialized. See [KotlinMutableSet/Dictionary dealloc].
return;
}
DeinitForeignRef(obj_, context_);
}
void BackRefFromAssociatedObject::initForPermanentObject(ObjHeader* obj) {
initAndAddRef(obj);
}
void BackRefFromAssociatedObject::initAndAddRef(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
obj_ = obj;
// Generally a specialized addRef below:
context_ = InitForeignRef(obj);
refCount = 1;
}
template <ErrorPolicy errorPolicy>
void BackRefFromAssociatedObject::addRef() {
static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here");
// Can be called both from Native state (if ObjC or Swift code adds RC)
// and from Runnable state (Kotlin_ObjCExport_refToObjC).
if (atomicAdd(&refCount, 1) == 1) {
if (obj_ == nullptr) return; // E.g. after [detach].
// There are no references to the associated object itself, so Kotlin object is being passed from Kotlin,
// and it is owned therefore.
ensureRefAccessible<errorPolicy>(obj_, context_); // TODO: consider removing explicit verification.
// Foreign reference has already been deinitialized (see [releaseRef]).
// Create a new one:
context_ = InitForeignRef(obj_);
}
}
template void BackRefFromAssociatedObject::addRef<ErrorPolicy::kThrow>();
template void BackRefFromAssociatedObject::addRef<ErrorPolicy::kTerminate>();
template <ErrorPolicy errorPolicy>
bool BackRefFromAssociatedObject::tryAddRef() {
static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here");
if (obj_ == nullptr) return false; // E.g. after [detach].
// Suboptimal but simple:
ensureRefAccessible<errorPolicy>(obj_, context_);
ObjHeader* obj = obj_;
if (!TryAddHeapRef(obj)) return false;
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
// TODO: This is a very weird way to ask for "unsafe" addRef.
addRef<ErrorPolicy::kIgnore>();
ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef.
// TODO: consider optimizing for non-shared objects.
return true;
}
template bool BackRefFromAssociatedObject::tryAddRef<ErrorPolicy::kThrow>();
template bool BackRefFromAssociatedObject::tryAddRef<ErrorPolicy::kTerminate>();
void BackRefFromAssociatedObject::releaseRef() {
ForeignRefContext context = context_;
if (atomicAdd(&refCount, -1) == 0) {
if (obj_ == nullptr) return; // E.g. after [detach].
// Note: by this moment "subsequent" addRef may have already happened and patched context_.
// So use the value loaded before refCount update:
DeinitForeignRef(obj_, context);
// From this moment [context] is generally a dangling pointer.
// This is handled in [IsForeignRefAccessible] and [addRef].
// TODO: This probably isn't fine in new MM. Make sure it works.
}
}
void BackRefFromAssociatedObject::detach() {
RuntimeAssert(atomicGet(&refCount) == 0, "unexpected refCount");
obj_ = nullptr; // Handled in addRef/tryAddRef/releaseRef/ref.
}
void BackRefFromAssociatedObject::dealloc() {
RuntimeFail("New MM only");
}
template <ErrorPolicy errorPolicy>
ObjHeader* BackRefFromAssociatedObject::ref() const {
RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found");
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
return nullptr;
}
AdoptReferenceFromSharedVariable(obj_);
return obj_;
}
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kDefaultValue>() const;
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kThrow>() const;
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kTerminate>() const;
ObjHeader* BackRefFromAssociatedObject::refPermanent() const {
return ref<ErrorPolicy::kIgnore>();
}
@@ -1,14 +0,0 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "TestSupport.hpp"
extern "C" void Kotlin_TestSupport_AssertClearGlobalState() {
// Nothing to do. Supported for the new MM only.
}
void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {
DeinitMemory(memoryState, false);
}
@@ -1,110 +0,0 @@
/*
* 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.
*/
#include "Weak.h"
#include "Memory.h"
#include "Types.h"
namespace {
// TODO: an ugly hack with fixed layout.
struct WeakReferenceCounter {
ObjHeader header;
KRef referred;
KInt lock;
KInt cookie;
};
inline WeakReferenceCounter* asWeakReferenceCounter(ObjHeader* obj) {
return reinterpret_cast<WeakReferenceCounter*>(obj);
}
#if !KONAN_NO_THREADS
inline void lock(int32_t* address) {
RuntimeAssert(*address == 0 || *address == 1, "Incorrect lock state");
while (__sync_val_compare_and_swap(address, 0, 1) == 1);
}
inline void unlock(int32_t* address) {
int old = __sync_val_compare_and_swap(address, 1, 0);
RuntimeAssert(old == 1, "Incorrect lock state");
}
#endif
} // namespace
extern "C" {
OBJ_GETTER(makeWeakReferenceCounterLegacyMM, void*);
OBJ_GETTER(makeObjCWeakReferenceImpl, void*);
OBJ_GETTER(makePermanentWeakReferenceImpl, ObjHeader*);
// See Weak.kt for implementation details.
// Retrieve link on the counter object.
OBJ_GETTER(Konan_getWeakReferenceImpl, ObjHeader* referred) {
if (referred->permanent()) {
RETURN_RESULT_OF(makePermanentWeakReferenceImpl, referred);
}
#if KONAN_OBJC_INTEROP
if (IsInstanceInternal(referred, theObjCObjectWrapperTypeInfo)) {
RETURN_RESULT_OF(makeObjCWeakReferenceImpl, referred->GetAssociatedObject());
}
#endif // KONAN_OBJC_INTEROP
ObjHeader* weakCounter = referred->GetWeakCounter();
if (weakCounter == nullptr) {
ObjHolder counterHolder;
// Cast unneeded, just to emphasize we store an object reference as void*.
ObjHeader* counter = makeWeakReferenceCounterLegacyMM(reinterpret_cast<void*>(referred), counterHolder.slot());
weakCounter = referred->GetOrSetWeakCounter(counter);
}
RETURN_OBJ(weakCounter);
}
OBJ_GETTER(Konan_RegularWeakReferenceImpl_get, ObjHeader* counter) {
RuntimeFail("New MM only");
}
// Materialize a weak reference to either null or the real reference.
OBJ_GETTER(Konan_WeakReferenceCounterLegacyMM_get, ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
#if KONAN_NO_THREADS
RETURN_OBJ(*referredAddress);
#else
auto* weakCounter = asWeakReferenceCounter(counter);
RETURN_RESULT_OF(ReadHeapRefLocked, referredAddress, &weakCounter->lock, &weakCounter->cookie);
#endif
}
void WeakReferenceCounterClear(ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
// Note, that we don't do UpdateRef here, as reference is weak.
#if KONAN_NO_THREADS
*referredAddress = nullptr;
#else
int32_t* lockAddress = &asWeakReferenceCounter(counter)->lock;
// Spinlock.
lock(lockAddress);
*referredAddress = nullptr;
unlock(lockAddress);
#endif
}
} // extern "C"
@@ -1,18 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_WEAK_H
#define RUNTIME_WEAK_H
#include "Memory.h"
extern "C" {
// Atomically clears counter object reference.
void WeakReferenceCounterClear(ObjHeader* counter);
} // extern "C"
#endif // RUNTIME_WEAK_H
@@ -1,70 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Memory.h"
#include "../../legacymm/cpp/MemoryPrivate.hpp" // Fine, because this module is a part of legacy MM.
// Note that only C++ part of the runtime goes via those functions, Kotlin uses specialized versions.
extern "C" {
const MemoryModel CurrentMemoryModel = MemoryModel::kRelaxed;
RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
RETURN_RESULT_OF(AllocInstanceRelaxed, typeInfo);
}
OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements);
}
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
ReleaseHeapRefRelaxed(object);
}
RUNTIME_NOTHROW void ReleaseHeapRefNoCollect(const ObjHeader* object) {
ReleaseHeapRefNoCollectRelaxed(object);
}
RUNTIME_NOTHROW void ZeroStackRef(ObjHeader** location) {
ZeroStackRefRelaxed(location);
}
RUNTIME_NOTHROW void SetStackRef(ObjHeader** location, const ObjHeader* object) {
SetStackRefRelaxed(location, object);
}
RUNTIME_NOTHROW void SetHeapRef(ObjHeader** location, const ObjHeader* object) {
SetHeapRefRelaxed(location, object);
}
RUNTIME_NOTHROW void UpdateHeapRef(ObjHeader** location, const ObjHeader* object) {
UpdateHeapRefRelaxed(location, object);
}
RUNTIME_NOTHROW void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) {
UpdateReturnRefRelaxed(returnSlot, object);
}
RUNTIME_NOTHROW void EnterFrame(ObjHeader** start, int parameters, int count) {
EnterFrameRelaxed(start, parameters, count);
}
RUNTIME_NOTHROW void LeaveFrame(ObjHeader** start, int parameters, int count) {
LeaveFrameRelaxed(start, parameters, count);
}
RUNTIME_NOTHROW void SetCurrentFrame(ObjHeader** start) {
SetCurrentFrameRelaxed(start);
}
RUNTIME_NOTHROW void UpdateStackRef(ObjHeader** location, const ObjHeader* object) {
UpdateStackRefRelaxed(location, object);
}
RUNTIME_NOTHROW void UpdateHeapRefsInsideOneArray(const ArrayHeader* array, int fromIndex, int toIndex, int count) {
UpdateHeapRefsInsideOneArrayRelaxed(array, fromIndex, toIndex, count);
}
} // extern "C"
@@ -1,70 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Memory.h"
#include "../../legacymm/cpp/MemoryPrivate.hpp" // Fine, because this module is a part of legacy MM.
// Note that only C++ part of the runtime goes via those functions, Kotlin uses specialized versions.
extern "C" {
const MemoryModel CurrentMemoryModel = MemoryModel::kStrict;
RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
RETURN_RESULT_OF(AllocInstanceStrict, typeInfo);
}
OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements);
}
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
ReleaseHeapRefStrict(object);
}
RUNTIME_NOTHROW void ReleaseHeapRefNoCollect(const ObjHeader* object) {
ReleaseHeapRefNoCollectStrict(object);
}
RUNTIME_NOTHROW void SetStackRef(ObjHeader** location, const ObjHeader* object) {
SetStackRefStrict(location, object);
}
RUNTIME_NOTHROW void SetHeapRef(ObjHeader** location, const ObjHeader* object) {
SetHeapRefStrict(location, object);
}
RUNTIME_NOTHROW void ZeroStackRef(ObjHeader** location) {
ZeroStackRefStrict(location);
}
RUNTIME_NOTHROW void UpdateHeapRef(ObjHeader** location, const ObjHeader* object) {
UpdateHeapRefStrict(location, object);
}
RUNTIME_NOTHROW void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) {
UpdateReturnRefStrict(returnSlot, object);
}
RUNTIME_NOTHROW void EnterFrame(ObjHeader** start, int parameters, int count) {
EnterFrameStrict(start, parameters, count);
}
RUNTIME_NOTHROW void LeaveFrame(ObjHeader** start, int parameters, int count) {
LeaveFrameStrict(start, parameters, count);
}
RUNTIME_NOTHROW void SetCurrentFrame(ObjHeader** start) {
SetCurrentFrameStrict(start);
}
RUNTIME_NOTHROW void UpdateStackRef(ObjHeader** location, const ObjHeader* object) {
UpdateStackRefStrict(location, object);
}
RUNTIME_NOTHROW void UpdateHeapRefsInsideOneArray(const ArrayHeader* array, int fromIndex, int toIndex, int count) {
UpdateHeapRefsInsideOneArrayStrict(array, fromIndex, toIndex, count);
}
} // extern "C"