blob: 12ece81f88e70f2617536b5dd3dceb623439a515 [file]
/*
* 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.
*/
#include "Cleaner.h"
#include "std_support/Atomic.hpp"
#include "Memory.h"
#include "Runtime.h"
#include "Worker.h"
// Defined in Cleaner.kt
extern "C" void Kotlin_CleanerImpl_shutdownCleanerWorker(KInt, bool);
extern "C" KInt Kotlin_CleanerImpl_createCleanerWorker();
namespace {
struct CleanerImpl {
ObjHeader header;
KNativePtr cleanerStablePtr;
};
constexpr KInt kCleanerWorkerUninitialized = 0;
constexpr KInt kCleanerWorkerInitializing = -1;
constexpr KInt kCleanerWorkerShutdown = -2;
std::atomic globalCleanerWorker = kCleanerWorkerUninitialized;
void disposeCleaner(CleanerImpl* thiz) {
auto worker = globalCleanerWorker.load();
RuntimeAssert(
worker != kCleanerWorkerUninitialized && worker != kCleanerWorkerInitializing,
"Cleaner worker must've been initialized by now");
if (worker == kCleanerWorkerShutdown) {
if (Kotlin_cleanersLeakCheckerEnabled()) {
konan::consoleErrorf(
"Cleaner %p was disposed during program exit\n"
"Use `Platform.isCleanersLeakCheckerActive = false` to avoid this check.\n",
thiz);
RuntimeCheck(false, "Terminating now");
}
return;
}
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
bool result = WorkerSchedule(worker, thiz->cleanerStablePtr);
RuntimeAssert(result, "Couldn't find Cleaner worker");
}
} // namespace
RUNTIME_NOTHROW void DisposeCleaner(KRef thiz) {
try {
disposeCleaner(reinterpret_cast<CleanerImpl*>(thiz));
} catch (...) {
// A trick to terminate with unhandled exception. This will print a stack trace
// and write to iOS crash log.
std::terminate();
}
}
void ShutdownCleaners(bool executeScheduledCleaners) {
KInt worker = 0;
{
// This loop may spin waiting for a proper worker state. Switch to the native thread state.
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
do {
worker = globalCleanerWorker.load();
RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not be shutdown twice");
if (worker == kCleanerWorkerUninitialized) {
if (!globalCleanerWorker.compare_exchange_weak(worker, kCleanerWorkerShutdown)) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// worker was never initialized. Just return.
return;
}
if (worker == kCleanerWorkerInitializing) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// Worker is in some proper state.
break;
} while (true);
}
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
globalCleanerWorker.store(kCleanerWorkerShutdown);
Kotlin_CleanerImpl_shutdownCleanerWorker(worker, executeScheduledCleaners);
WaitNativeWorkerTermination(worker);
}
extern "C" KInt Kotlin_CleanerImpl_getCleanerWorker() {
KInt worker = 0;
// This loop may spin waiting for a proper worker state. Switch to the native thread state.
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
do {
worker = globalCleanerWorker.load();
RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not have been shutdown");
if (worker == kCleanerWorkerUninitialized) {
if (!globalCleanerWorker.compare_exchange_weak(worker, kCleanerWorkerInitializing)) {
// Someone else is trying to initialize the worker. Try again.
continue;
}
worker = kotlin::CallWithThreadState<kotlin::ThreadState::kRunnable>(Kotlin_CleanerImpl_createCleanerWorker);
if (!kotlin::std_support::atomic_compare_exchange_strong(globalCleanerWorker, kCleanerWorkerInitializing, worker)) {
RuntimeCheck(false, "Someone interrupted worker initializing");
}
// Worker is initialized.
break;
}
if (worker == kCleanerWorkerInitializing) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// Worker is in some proper state.
break;
} while (true);
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
return worker;
}
void ResetCleanerWorkerForTests() {
globalCleanerWorker.store(kCleanerWorkerUninitialized);
}