blob: ca71dfe178514f6c32debf3e047245d911ccc7de [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.
*/
#ifndef RUNTIME_MM_STABLE_REF_REGISTRY_H
#define RUNTIME_MM_STABLE_REF_REGISTRY_H
#include "Memory.h"
#include "MultiSourceQueue.hpp"
#include "ThreadRegistry.hpp"
namespace kotlin {
namespace mm {
// Registry for all objects that have references outside of Kotlin.
class StableRefRegistry : Pinned {
using Mutex = SpinLock<MutexThreadStateHandling::kIgnore>;
public:
class ThreadQueue : public MultiSourceQueue<ObjHeader*, Mutex>::Producer {
public:
explicit ThreadQueue(StableRefRegistry& registry) : Producer(registry.stableRefs_) {}
// Do not add fields as this is just a wrapper and Producer does not have virtual destructor.
};
using Iterable = MultiSourceQueue<ObjHeader*, Mutex>::Iterable;
using Iterator = MultiSourceQueue<ObjHeader*, Mutex>::Iterator;
using Node = MultiSourceQueue<ObjHeader*, Mutex>::Node;
StableRefRegistry();
~StableRefRegistry();
static StableRefRegistry& Instance() noexcept;
Node* RegisterStableRef(mm::ThreadData* threadData, ObjHeader* object) noexcept;
void UnregisterStableRef(mm::ThreadData* threadData, Node* node) noexcept;
// Collect stable references from thread corresponding to `threadData`. Must be called by the thread
// when it's asked by GC to stop.
void ProcessThread(mm::ThreadData* threadData) noexcept;
// Lock registry and apply deletions. Should be called on GC thread after all threads have published, and before `LockForIter`.
void ProcessDeletions() noexcept;
// Lock registry for safe iteration.
// TODO: Iteration over `stableRefs_` will be slow, because it's `std_support::list` collected at different times from
// different threads, and so the nodes are all over the memory. Use metrics to understand how
// much of a problem is it.
Iterable LockForIter() noexcept { return stableRefs_.LockForIter(); }
void ClearForTests() noexcept { stableRefs_.ClearForTests(); }
private:
// Current approach optimizes for creating and disposing of stable refs:
// * creation just enqueues ref, disposing either queues or deletes the ref immediately (if it still resides in the current queue).
// * when thread is stopped, it'll scan through the local queue (to mark that refs no longer reside in it) and push creation and
// deletion queues to the global registry.
// * during marking GC will have to `ProcessDeletions` to actually delete the refs that were enqueued for deletion.
// So, we sacrifice memory (to keep deleted queues) and marking time (to process these queues) to improve creation and disposal times.
//
// Other alternatives:
// * Use a single global collection (e.g. lock free doubly linked list).
// * Sacrifice disposal time to try to delete as early as possible (e.g. post directly into owning producer, so it processes deletions
// before posting queue to the global registry)
//
// TODO: Measure to understand, if this approach is problematic.
MultiSourceQueue<ObjHeader*, Mutex> stableRefs_;
};
} // namespace mm
} // namespace kotlin
#endif // RUNTIME_MM_STABLE_REF_REGISTRY_H