123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- #include "il2cpp-config.h"
- #include "il2cpp-object-internals.h"
- #include "GarbageCollector.h"
- #include "os/Event.h"
- #include "os/Mutex.h"
- #include "os/Semaphore.h"
- #include "os/Thread.h"
- #include "utils/Il2CppHashMap.h"
- #include "utils/HashUtils.h"
-
- #include "Baselib.h"
- #include "Cpp/ReentrantLock.h"
-
- #include "vm/CCW.h"
- #include "vm/Class.h"
- #include "vm/Domain.h"
- #include "vm/Exception.h"
- #include "vm/RCW.h"
- #include "vm/Runtime.h"
- #include "vm/Thread.h"
-
- using namespace il2cpp::os;
- using namespace il2cpp::vm;
-
- namespace il2cpp
- {
- namespace gc
- {
- // So COM Callable Wrapper can be created for any kind of managed object,
- // whether it has finalizer or not. If it doesn't then it's an easy case:
- // when creating the CCW, we just register our cleanup method to be the
- // finalizer method. In case it does, then we need to be able to invoke
- // both our CCW cleanup method, and the finalizer in question.
- // We could chain them by registering CCW cleanup method over the finalizer
- // method and storing the previous finalizer in CCW cache, but it would
- // screw us over if for example the object is IDisposable and then it
- // calls `GC.SuppressFinalize` from C# - instead of the finalizer getting
- // unregistered, our CCW cleanup method gets unregistered, and we now have
- // a dangling pointer to the managed heap in the CCW cache. Not pretty.
- // Instead, we made GarbageCollector::RegisterFinalizer and GarbageCollector::SuppressFinalizer
- // to be CCW cache aware. Now, any managed object goes into 1 of these categories:
- //
- // 1. Object has no finalizer and it is not in CCW cache. It gets garbage
- // collected and no cleanup is needed.
- // 2. Object has a finalizer and it is not in CCW cache. GarbageCollector::RunFinalizer
- // gets registered with the GC for such object.
- // 3. Object has no finalizer and it is in CCW cache. CleanupCCW is
- // registered with the GC for such object. Once it is called, it removes
- // the object from the CCW cache.
- // 4. Object has a finalizer and it is in CCW cache. CleanupCCW is
- // registered with the GC for such object. Once it is called, it removes
- // the object from the CCW cache and then calls its finalizer.
- //
- // To know whether we have case 3 or 4, we have the "hasFinalizer" field in
- // the CachedCCW object.
- //
- // When GarbageCollector::RegisterFinalizer or GarbageCollector::SuppressFinalizer
- // is called, we have managed objects fitting in two buckets:
- //
- // 1. Those that do not exist in CCW cache. Finalizer is normally registered with
- // the GC.
- // 2. Those that are in the CCW cache. In such case, GC won't know about the call,
- // but instead we'll find the object in the CCW cache and RegisterFinalizer will
- // set "hasFinalizer" field to true, while SuppressFinalizer will set it to false
- //
- // Any methods that interact with s_CCWCache have to lock s_CCWCacheMutex.
- struct CachedCCW
- {
- Il2CppIManagedObjectHolder* managedObjectHolder;
- bool hasFinalizer;
- };
-
- typedef Il2CppHashMap<Il2CppObject*, CachedCCW, utils::PointerHash<Il2CppObject> > CCWCache;
-
- static baselib::ReentrantLock s_CCWCacheMutex;
- static CCWCache s_CCWCache;
-
- #if IL2CPP_SUPPORT_THREADS
-
- struct GarbageCollectorContext
- {
- GarbageCollectorContext() :
- m_StopFinalizer(false),
- m_FinalizerThread(nullptr),
- m_FinalizerThreadObject(nullptr),
- m_FinalizerSemaphore(0, 32767),
- m_FinalizersThreadStartedEvent(),
- m_FinalizersCompletedEvent(true, false)
- {
- }
-
- bool m_StopFinalizer;
- il2cpp::os::Thread* m_FinalizerThread;
- Il2CppThread* m_FinalizerThreadObject;
- Semaphore m_FinalizerSemaphore;
- Event m_FinalizersThreadStartedEvent;
- Event m_FinalizersCompletedEvent;
- };
-
- GarbageCollectorContext* s_GarbageCollectorContext = nullptr;
-
- static void FinalizerThread(void* arg)
- {
- s_GarbageCollectorContext->m_FinalizerThreadObject = il2cpp::vm::Thread::Attach(Domain::GetCurrent());
- s_GarbageCollectorContext->m_FinalizerThread->SetName("GC Finalizer");
-
- s_GarbageCollectorContext->m_FinalizersThreadStartedEvent.Set();
-
- while (!s_GarbageCollectorContext->m_StopFinalizer)
- {
- s_GarbageCollectorContext->m_FinalizerSemaphore.Wait();
-
- GarbageCollector::InvokeFinalizers();
-
- s_GarbageCollectorContext->m_FinalizersCompletedEvent.Set();
- }
-
- il2cpp::vm::Thread::Detach(s_GarbageCollectorContext->m_FinalizerThreadObject);
- }
-
- bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
- {
- return s_GarbageCollectorContext->m_FinalizerThreadObject == thread;
- }
-
- bool GarbageCollector::IsFinalizerInternalThread(Il2CppInternalThread *thread)
- {
- return s_GarbageCollectorContext->m_FinalizerThreadObject->GetInternalThread() == thread;
- }
-
- #else
-
- bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
- {
- return false;
- }
-
- bool GarbageCollector::IsFinalizerInternalThread(Il2CppInternalThread *thread)
- {
- return false;
- }
-
- #endif
-
- void GarbageCollector::InitializeFinalizer()
- {
- GarbageCollector::InvokeFinalizers();
- #if IL2CPP_SUPPORT_THREADS
- s_GarbageCollectorContext = new GarbageCollectorContext();
-
- s_GarbageCollectorContext->m_FinalizerThread = new il2cpp::os::Thread;
- s_GarbageCollectorContext->m_FinalizerThread->Run(&FinalizerThread, NULL);
- s_GarbageCollectorContext->m_FinalizersThreadStartedEvent.Wait();
- #endif
- }
-
- void GarbageCollector::UninitializeFinalizers()
- {
- #if IL2CPP_SUPPORT_THREADS
- s_GarbageCollectorContext->m_StopFinalizer = true;
- NotifyFinalizers();
- s_GarbageCollectorContext->m_FinalizerThread->Join();
- delete s_GarbageCollectorContext->m_FinalizerThread;
- s_GarbageCollectorContext->m_FinalizerThread = NULL;
- s_GarbageCollectorContext->m_StopFinalizer = false;
- s_GarbageCollectorContext->m_FinalizerThreadObject = NULL;
-
- delete s_GarbageCollectorContext;
- s_GarbageCollectorContext = nullptr;
- #endif
- }
-
- void GarbageCollector::NotifyFinalizers()
- {
- #if IL2CPP_SUPPORT_THREADS
- s_GarbageCollectorContext->m_FinalizerSemaphore.Post(1, NULL);
- #endif
- }
-
- void GarbageCollector::RunFinalizer(void *obj, void *data)
- {
- IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(GarbageCollector::RunFinalizer, "Compare to mono implementation special cases");
-
- Il2CppException *exc = NULL;
- Il2CppObject *o;
- const MethodInfo* finalizer = NULL;
-
- o = (Il2CppObject*)obj;
-
- finalizer = Class::GetFinalizer(o->klass);
-
- Runtime::Invoke(finalizer, o, NULL, &exc);
-
- if (exc)
- Runtime::UnhandledException(exc);
- }
-
- void GarbageCollector::RegisterFinalizerForNewObject(Il2CppObject* obj)
- {
- // Fast path
- // No need to check CCW cache since it's guaranteed to not be in it for a new object
- RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
- }
-
- void GarbageCollector::RegisterFinalizer(Il2CppObject* obj)
- {
- // Slow path
- // Check in CCW cache first
- os::FastAutoLock lock(&s_CCWCacheMutex);
- CCWCache::iterator it = s_CCWCache.find(obj);
-
- if (it != s_CCWCache.end())
- {
- it->second.hasFinalizer = true;
- }
- else
- {
- RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
- }
- }
-
- void GarbageCollector::SuppressFinalizer(Il2CppObject* obj)
- {
- // Slow path
- // Check in CCW cache first
- os::FastAutoLock lock(&s_CCWCacheMutex);
- CCWCache::iterator it = s_CCWCache.find(obj);
-
- if (it != s_CCWCache.end())
- {
- it->second.hasFinalizer = false;
- }
- else
- {
- RegisterFinalizerWithCallback(obj, NULL);
- }
- }
-
- void GarbageCollector::WaitForPendingFinalizers()
- {
- if (!GarbageCollector::HasPendingFinalizers())
- return;
-
- #if IL2CPP_SUPPORT_THREADS
- /* Avoid deadlocks */
- if (vm::Thread::Current() == s_GarbageCollectorContext->m_FinalizerThreadObject)
- return;
-
- s_GarbageCollectorContext->m_FinalizersCompletedEvent.Reset();
- NotifyFinalizers();
- s_GarbageCollectorContext->m_FinalizersCompletedEvent.Wait();
- #else
- GarbageCollector::InvokeFinalizers();
- #endif
- }
-
- static void CleanupCCW(void* obj, void* data)
- {
- bool hasFinalizer;
-
- // We have to destroy CCW before invoking the finalizer, because we cannot know whether the finalizer will revive the object
- // In cases it does revive it, it's also possible for it to hit CCW cache, and in that case we'd want to create a new CCW object
- // rather than returning the one that we're about to destroy here
- {
- os::FastAutoLock lock(&s_CCWCacheMutex);
- CCWCache::iterator it = s_CCWCache.find(static_cast<Il2CppObject*>(obj));
- IL2CPP_ASSERT(it != s_CCWCache.end());
-
- Il2CppIManagedObjectHolder* managedObjectHolder = it->second.managedObjectHolder;
- hasFinalizer = it->second.hasFinalizer;
- s_CCWCache.erase(it);
-
- managedObjectHolder->Destroy();
- }
-
- if (hasFinalizer)
- GarbageCollector::RunFinalizer(obj, data);
- }
-
- // When creating COM Callable Wrappers for classes that project to other Windows Runtime classes
- // for instance, System.Uri to Windows.Foundation.Uri, we cannot actually create a wrapper against managed object
- // as the native side actually wants an instance of a real Windows.Foundation.Uri class. So do that, our createCCW
- // instead of creating a COM Callable Wrapper, will create an instance of that windows runtime class. In that case,
- // we do not (AND CANNOT!) put it into a CCW cache because it's not a CCW.
- static bool ShouldInsertIntoCCWCache(Il2CppObject* obj)
- {
- if (obj->klass == il2cpp_defaults.system_uri_class && il2cpp_defaults.windows_foundation_uri_class != NULL)
- return false;
-
- /* TODO - Add the rest of the class projections:
- System.Collections.Specialized.NotifyCollectionChangedEventArgs <-> Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs
- System.ComponentModel.PropertyChangedEventArgs <-> Windows.UI.Xaml.Data.PropertyChangedEventArgs
- */
-
- return true;
- }
-
- Il2CppIUnknown* GarbageCollector::GetOrCreateCCW(Il2CppObject* obj, const Il2CppGuid& iid)
- {
- if (obj == NULL)
- return NULL;
-
- // check for rcw object. COM interface can be extracted from it and there's no need to create ccw
- if (obj->klass->is_import_or_windows_runtime)
- {
- Il2CppIUnknown* result = RCW::QueryInterfaceNoAddRef<true>(static_cast<Il2CppComObject*>(obj), iid);
- result->AddRef();
- return result;
- }
-
- os::FastAutoLock lock(&s_CCWCacheMutex);
- CCWCache::iterator it = s_CCWCache.find(obj);
- Il2CppIUnknown* comCallableWrapper;
-
- if (it == s_CCWCache.end())
- {
- comCallableWrapper = CCW::CreateCCW(obj);
-
- if (ShouldInsertIntoCCWCache(obj))
- {
- #if IL2CPP_DEBUG
- // Assert that CCW::CreateCCW actually returns upcasted Il2CppIManagedObjectHolder
- Il2CppIManagedObjectHolder* managedObjectHolder = NULL;
- comCallableWrapper->QueryInterface(Il2CppIManagedObjectHolder::IID, reinterpret_cast<void**>(&managedObjectHolder));
- IL2CPP_ASSERT(static_cast<void*>(comCallableWrapper) == static_cast<void*>(managedObjectHolder));
- managedObjectHolder->Release();
- #endif
- CachedCCW ccw =
- {
- static_cast<Il2CppIManagedObjectHolder*>(comCallableWrapper),
- RegisterFinalizerWithCallback(obj, &CleanupCCW) != NULL
- };
-
- s_CCWCache.insert(std::make_pair(obj, ccw));
- }
- }
- else
- {
- comCallableWrapper = it->second.managedObjectHolder;
- }
-
- Il2CppIUnknown* result;
- il2cpp_hresult_t hr = comCallableWrapper->QueryInterface(iid, reinterpret_cast<void**>(&result));
- vm::Exception::RaiseIfFailed(hr, true);
- return result;
- }
-
- int32_t GarbageCollector::GetGeneration(void* addr)
- {
- return 0;
- }
-
- void GarbageCollector::AddMemoryPressure(int64_t value)
- {
- }
-
- #if IL2CPP_ENABLE_WRITE_BARRIERS
- void il2cpp::gc::GarbageCollector::SetWriteBarrier(void **ptr, size_t size)
- {
- #if IL2CPP_ENABLE_STRICT_WRITE_BARRIERS
- for (size_t i = 0; i < size / sizeof(void**); i++)
- SetWriteBarrier(ptr + i);
- #else
- SetWriteBarrier(ptr);
- #endif
- }
-
- #endif
-
- void il2cpp::gc::GarbageCollector::SetSkipThread(bool skip)
- {
- }
- } // namespace gc
- } // namespace il2cpp
|