[K/N] Rework object and enum classes initialization
Now it works with common logic for all static scope not with custom one.
This commit is contained in:
committed by
Space Team
parent
7006eb938d
commit
d3adfec2fb
@@ -31,8 +31,6 @@ touchType(KRefSharedHolder)
|
||||
|
||||
touchFunction(AllocInstance)
|
||||
touchFunction(AllocArrayInstance)
|
||||
touchFunction(InitThreadLocalSingleton)
|
||||
touchFunction(InitSingleton)
|
||||
touchFunction(InitAndRegisterGlobal)
|
||||
touchFunction(UpdateHeapRef)
|
||||
touchFunction(UpdateStackRef)
|
||||
|
||||
@@ -2373,104 +2373,6 @@ OBJ_GETTER(allocArrayInstance, const TypeInfo* type_info, int32_t elements) {
|
||||
RETURN_OBJ(container.GetPlace()->obj());
|
||||
}
|
||||
|
||||
template <bool Strict>
|
||||
OBJ_GETTER(initThreadLocalSingleton,
|
||||
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
ObjHeader* value = *location;
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = allocInstance<Strict>(typeInfo, OBJ_RESULT);
|
||||
updateHeapRef<Strict>(location, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
ZeroHeapRef(location);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool Strict>
|
||||
OBJ_GETTER(initSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
#if KONAN_NO_THREADS
|
||||
ObjHeader* value = *location;
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
UpdateHeapRef(location, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
ZeroHeapRef(location);
|
||||
throw;
|
||||
}
|
||||
#endif // KONAN_NO_EXCEPTIONS
|
||||
#else // KONAN_NO_THREADS
|
||||
// Search from the top of the stack.
|
||||
for (auto it = memoryState->initializingSingletons.rbegin(); it != memoryState->initializingSingletons.rend(); ++it) {
|
||||
if (it->first == location) {
|
||||
RETURN_OBJ(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
ObjHeader* initializing = kInitializingSingleton;
|
||||
|
||||
// Spin lock.
|
||||
ObjHeader* value = nullptr;
|
||||
while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing);
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
memoryState->initializingSingletons.push_back(std::make_pair(location, object));
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
UpdateHeapRef(location, object);
|
||||
synchronize();
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
return object;
|
||||
#else // KONAN_NO_EXCEPTIONS
|
||||
try {
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
UpdateHeapRef(location, object);
|
||||
synchronize();
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
zeroHeapRef(location);
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
synchronize();
|
||||
throw;
|
||||
}
|
||||
#endif // KONAN_NO_EXCEPTIONS
|
||||
#endif // KONAN_NO_THREADS
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep thread affinity and reference value based cookie in the atomic references, so that
|
||||
* repeating read operation of the same value do not lead to the repeating rememberNewContainer() operation.
|
||||
@@ -3398,20 +3300,6 @@ OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* typeInfo, int32_t elements
|
||||
RETURN_RESULT_OF(allocArrayInstance<false>, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initThreadLocalSingleton<true>, location, typeInfo, ctor);
|
||||
}
|
||||
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initThreadLocalSingleton<false>, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initSingleton<true>, location, typeInfo, ctor);
|
||||
}
|
||||
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initSingleton<false>, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||
RuntimeCheck(false, "Global registration is impossible in legacy MM");
|
||||
}
|
||||
|
||||
@@ -309,11 +309,6 @@ 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);
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object);
|
||||
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
||||
|
||||
@@ -25,8 +25,6 @@ void ensureUsed(Ret (*f)(Args...)) {
|
||||
void EnsureDeclarationsEmitted() {
|
||||
ensureUsed(AllocInstance);
|
||||
ensureUsed(AllocArrayInstance);
|
||||
ensureUsed(InitThreadLocalSingleton);
|
||||
ensureUsed(InitSingleton);
|
||||
ensureUsed(InitAndRegisterGlobal);
|
||||
ensureUsed(UpdateHeapRef);
|
||||
ensureUsed(UpdateStackRef);
|
||||
|
||||
@@ -83,6 +83,7 @@ void VLog(Level level, std::initializer_list<const char*> tags, const char* form
|
||||
|
||||
inline constexpr const char* kTagGC = "gc";
|
||||
inline constexpr const char* kTagMM = "mm";
|
||||
inline constexpr const char* kTagTLS = "tls";
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
|
||||
@@ -195,9 +195,6 @@ OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||
|
||||
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements);
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
// `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already
|
||||
// set by static initialization.
|
||||
|
||||
@@ -53,6 +53,7 @@ class IntrinsicType {
|
||||
const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB"
|
||||
const val INIT_INSTANCE = "INIT_INSTANCE"
|
||||
const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM"
|
||||
const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE"
|
||||
|
||||
// Enums
|
||||
const val ENUM_VALUES = "ENUM_VALUES"
|
||||
|
||||
@@ -30,3 +30,6 @@ import kotlin.native.internal.IntrinsicType
|
||||
|
||||
// Reinterprets this value from T to R having the same binary representation (e.g. to unwrap inline class).
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY) @PublishedApi external internal fun <T, R> T.reinterpret(): R
|
||||
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.THE_UNIT_INSTANCE) @ExportForCompiler external internal fun theUnitInstance(): Unit
|
||||
@@ -1,80 +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 "InitializationScheme.hpp"
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectOps.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
OBJ_GETTER(mm::InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
if (auto* value = *location) {
|
||||
// Initialized by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
auto* value = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT);
|
||||
mm::SetHeapRef(location, value);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(value);
|
||||
#else
|
||||
try {
|
||||
ctor(value);
|
||||
} catch (...) {
|
||||
mm::SetStackRef(OBJ_RESULT, nullptr);
|
||||
mm::SetHeapRef(location, nullptr);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
|
||||
OBJ_GETTER(mm::InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
auto& initializingSingletons = threadData->initializingSingletons();
|
||||
|
||||
// Search from the top of the stack.
|
||||
for (auto it = initializingSingletons.rbegin(); it != initializingSingletons.rend(); ++it) {
|
||||
if (it->first == location) {
|
||||
RETURN_OBJ(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
ObjHeader* initializing = kInitializingSingleton;
|
||||
|
||||
// Spin lock.
|
||||
ObjHeader* value = nullptr;
|
||||
{
|
||||
ThreadStateGuard guard(ThreadState::kNative);
|
||||
while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing) {
|
||||
}
|
||||
}
|
||||
if (value != nullptr) {
|
||||
// Initialized by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
auto* object = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT);
|
||||
initializingSingletons.push_back(std::make_pair(location, object));
|
||||
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
} catch (...) {
|
||||
mm::SetStackRef(OBJ_RESULT, nullptr);
|
||||
mm::SetHeapRefAtomic(location, nullptr);
|
||||
initializingSingletons.pop_back();
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location);
|
||||
mm::SetHeapRefAtomic(location, object);
|
||||
initializingSingletons.pop_back();
|
||||
return object;
|
||||
}
|
||||
@@ -1,22 +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.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
#define RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
namespace kotlin {
|
||||
namespace mm {
|
||||
|
||||
class ThreadData;
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
} // namespace mm
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
@@ -1,270 +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 "InitializationScheme.hpp"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "Types.h"
|
||||
#include "std_support/Vector.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
struct EmptyPayload {
|
||||
using Field = ObjHeader* EmptyPayload::*;
|
||||
static constexpr std::array<Field, 0> kFields{};
|
||||
};
|
||||
|
||||
class InitSingletonTest : public testing::Test {
|
||||
public:
|
||||
InitSingletonTest() {
|
||||
globalConstructor_ = &constructor_;
|
||||
}
|
||||
|
||||
~InitSingletonTest() {
|
||||
globalConstructor_ = nullptr;
|
||||
mm::GlobalData::Instance().gc().ClearForTests();
|
||||
mm::GlobalData::Instance().globalsRegistry().ClearForTests();
|
||||
}
|
||||
|
||||
testing::MockFunction<void(ObjHeader*)>& constructor() { return constructor_; }
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, mm::ThreadData& threadData) {
|
||||
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, &threadData, location, type_.typeInfo(), constructorImpl);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, mm::ThreadData& threadData) {
|
||||
RETURN_RESULT_OF(mm::InitSingleton, &threadData, location, type_.typeInfo(), constructorImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> constructor_;
|
||||
test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
|
||||
|
||||
static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
|
||||
|
||||
static void constructorImpl(ObjHeader* object) { globalConstructor_->Call(object); }
|
||||
};
|
||||
|
||||
// static
|
||||
testing::MockFunction<void(ObjHeader*)>* InitSingletonTest::globalConstructor_ = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingleton) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
ObjHeader* valueAtConstructor = nullptr;
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce(
|
||||
[&location, &stackLocation, &valueAtConstructor](ObjHeader* value) {
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
valueAtConstructor = value;
|
||||
});
|
||||
ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(valueAtConstructor, location);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingletonTwice) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader previousValue;
|
||||
ObjHeader* location = &previousValue;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(0);
|
||||
ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(value, &previousValue);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingletonFail) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
constexpr int kException = 42;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; });
|
||||
try {
|
||||
InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
EXPECT_THAT(stackLocation, nullptr);
|
||||
EXPECT_THAT(location, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingleton) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
ObjHeader* valueAtConstructor = nullptr;
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce(
|
||||
[&location, &stackLocation, &valueAtConstructor](ObjHeader* value) {
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(location, kInitializingSingleton);
|
||||
valueAtConstructor = value;
|
||||
});
|
||||
ObjHeader* value = InitSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(valueAtConstructor, location);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonTwice) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader previousValue;
|
||||
ObjHeader* location = &previousValue;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(0);
|
||||
ObjHeader* value = InitSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(value, &previousValue);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonFail) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
constexpr int kException = 42;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; });
|
||||
try {
|
||||
InitSingleton(&location, threadData, &stackLocation);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
EXPECT_THAT(stackLocation, nullptr);
|
||||
EXPECT_THAT(location, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonRecursive) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
// The first singleton. Its constructor depends on the second singleton.
|
||||
ObjHeader* location1 = nullptr;
|
||||
ObjHeader* stackLocation1 = nullptr;
|
||||
// The second singleton. Its constructor depends on the first singleton.
|
||||
ObjHeader* location2 = nullptr;
|
||||
ObjHeader* stackLocation2 = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_))
|
||||
.Times(2) // called only once for each singleton.
|
||||
.WillRepeatedly([this, &location1, &stackLocation1, &location2, &stackLocation2, &threadData](ObjHeader* value) {
|
||||
if (value == stackLocation1) {
|
||||
ObjHeader* result = InitSingleton(&location2, threadData, &stackLocation2);
|
||||
EXPECT_THAT(result, stackLocation2);
|
||||
EXPECT_THAT(result, location2);
|
||||
EXPECT_THAT(result, testing::Not(testing::Truly(isNullOrMarker)));
|
||||
} else {
|
||||
ObjHeader* result = InitSingleton(&location1, threadData, &stackLocation1);
|
||||
EXPECT_THAT(result, stackLocation1);
|
||||
EXPECT_THAT(result, testing::Ne(location1));
|
||||
EXPECT_THAT(location1, kInitializingSingleton);
|
||||
}
|
||||
});
|
||||
ObjHeader* value = InitSingleton(&location1, threadData, &stackLocation1);
|
||||
EXPECT_THAT(value, stackLocation1);
|
||||
EXPECT_THAT(value, location1);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonConcurrent) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
std_support::vector<ScopedThread> threads;
|
||||
ObjHeader* location = nullptr;
|
||||
std_support::vector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
std_support::vector<ObjHeader*> actual(kThreadCount, nullptr);
|
||||
|
||||
for (size_t i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([this, i, &location, &stackLocations, &actual, &readyCount, &canStart]() {
|
||||
ScopedMemoryInit init;
|
||||
auto* threadData = init.memoryState()->GetThreadData();
|
||||
++readyCount;
|
||||
while (!canStart) {
|
||||
}
|
||||
actual[i] = InitSingleton(&location, *threadData, &stackLocations[i]);
|
||||
threadData->Publish();
|
||||
});
|
||||
}
|
||||
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
// Constructor is called exactly once.
|
||||
EXPECT_CALL(constructor(), Call(_));
|
||||
canStart = true;
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, testing::Not(testing::Truly(isNullOrMarker)));
|
||||
EXPECT_THAT(stackLocations, testing::Each(location));
|
||||
EXPECT_THAT(actual, testing::Each(location));
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
std_support::vector<ScopedThread> threads;
|
||||
constexpr int kException = 42;
|
||||
ObjHeader* location = nullptr;
|
||||
std_support::vector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
|
||||
for (size_t i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([this, i, &location, &stackLocations, &readyCount, &canStart]() {
|
||||
ScopedMemoryInit init;
|
||||
auto* threadData = init.memoryState()->GetThreadData();
|
||||
++readyCount;
|
||||
while (!canStart) {
|
||||
}
|
||||
try {
|
||||
InitSingleton(&location, *threadData, &stackLocations[i]);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
threadData->Publish();
|
||||
});
|
||||
}
|
||||
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
// Constructor is called exactly `kThreadCount` times.
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(kThreadCount).WillRepeatedly([]() { throw kException; });
|
||||
canStart = true;
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, nullptr);
|
||||
EXPECT_THAT(stackLocations, testing::Each(nullptr));
|
||||
}
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "Freezing.hpp"
|
||||
#include "GC.hpp"
|
||||
#include "GlobalsRegistry.hpp"
|
||||
#include "InitializationScheme.hpp"
|
||||
#include "KAssert.h"
|
||||
#include "Natives.h"
|
||||
#include "ObjectOps.hpp"
|
||||
@@ -146,18 +145,6 @@ extern "C" OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elem
|
||||
RETURN_RESULT_OF(mm::AllocateArray, threadData, typeInfo, static_cast<uint32_t>(elements));
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
|
||||
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadData, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
|
||||
RETURN_RESULT_OF(mm::InitSingleton, threadData, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
*/
|
||||
|
||||
#include "ThreadLocalStorage.hpp"
|
||||
#include "Logging.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||
RuntimeAssert(size >= 0, "Size cannot be negative");
|
||||
RuntimeLogDebug({kTagTLS}, "Add record key = %p, size = %d\n", key, size);
|
||||
auto it = map_.find(key);
|
||||
if (it != map_.end()) {
|
||||
RuntimeAssert(it->second.size == size, "Attempt to add TLS record with the same key, but different size");
|
||||
@@ -20,12 +22,14 @@ void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
||||
}
|
||||
|
||||
void mm::ThreadLocalStorage::Commit() noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Committed");
|
||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||
storage_.resize(size_);
|
||||
state_ = State::kCommitted;
|
||||
}
|
||||
|
||||
void mm::ThreadLocalStorage::Clear() noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Cleared");
|
||||
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||
// Just free the storage.
|
||||
storage_.clear();
|
||||
@@ -33,6 +37,7 @@ void mm::ThreadLocalStorage::Clear() noexcept {
|
||||
}
|
||||
|
||||
ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Lookup key = %p, index = %d", key, index);
|
||||
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||
if (lastKeyAndEntry_.first == key) {
|
||||
return Lookup(lastKeyAndEntry_.second, index);
|
||||
@@ -44,6 +49,7 @@ ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept {
|
||||
}
|
||||
|
||||
ObjHeader** mm::ThreadLocalStorage::Lookup(Entry entry, int index) noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Lookup entry = {%d, %d}, index = %d", entry.offset, entry.size, index);
|
||||
RuntimeAssert(index < entry.size, "Out of bounds TLS access");
|
||||
return &storage_[entry.offset + index];
|
||||
}
|
||||
|
||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitThreadLocalSingletonRelaxed, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSingletonRelaxed, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||
ReleaseHeapRefRelaxed(object);
|
||||
}
|
||||
|
||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitThreadLocalSingletonStrict, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSingletonStrict, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||
ReleaseHeapRefStrict(object);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user