暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

GarbageCollector.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #include "il2cpp-config.h"
  2. #include "il2cpp-object-internals.h"
  3. #include "GarbageCollector.h"
  4. #include "os/Event.h"
  5. #include "os/Mutex.h"
  6. #include "os/Semaphore.h"
  7. #include "os/Thread.h"
  8. #include "utils/Il2CppHashMap.h"
  9. #include "utils/HashUtils.h"
  10. #include "Baselib.h"
  11. #include "Cpp/ReentrantLock.h"
  12. #include "vm/CCW.h"
  13. #include "vm/Class.h"
  14. #include "vm/Domain.h"
  15. #include "vm/Exception.h"
  16. #include "vm/RCW.h"
  17. #include "vm/Runtime.h"
  18. #include "vm/Thread.h"
  19. using namespace il2cpp::os;
  20. using namespace il2cpp::vm;
  21. namespace il2cpp
  22. {
  23. namespace gc
  24. {
  25. // So COM Callable Wrapper can be created for any kind of managed object,
  26. // whether it has finalizer or not. If it doesn't then it's an easy case:
  27. // when creating the CCW, we just register our cleanup method to be the
  28. // finalizer method. In case it does, then we need to be able to invoke
  29. // both our CCW cleanup method, and the finalizer in question.
  30. // We could chain them by registering CCW cleanup method over the finalizer
  31. // method and storing the previous finalizer in CCW cache, but it would
  32. // screw us over if for example the object is IDisposable and then it
  33. // calls `GC.SuppressFinalize` from C# - instead of the finalizer getting
  34. // unregistered, our CCW cleanup method gets unregistered, and we now have
  35. // a dangling pointer to the managed heap in the CCW cache. Not pretty.
  36. // Instead, we made GarbageCollector::RegisterFinalizer and GarbageCollector::SuppressFinalizer
  37. // to be CCW cache aware. Now, any managed object goes into 1 of these categories:
  38. //
  39. // 1. Object has no finalizer and it is not in CCW cache. It gets garbage
  40. // collected and no cleanup is needed.
  41. // 2. Object has a finalizer and it is not in CCW cache. GarbageCollector::RunFinalizer
  42. // gets registered with the GC for such object.
  43. // 3. Object has no finalizer and it is in CCW cache. CleanupCCW is
  44. // registered with the GC for such object. Once it is called, it removes
  45. // the object from the CCW cache.
  46. // 4. Object has a finalizer and it is in CCW cache. CleanupCCW is
  47. // registered with the GC for such object. Once it is called, it removes
  48. // the object from the CCW cache and then calls its finalizer.
  49. //
  50. // To know whether we have case 3 or 4, we have the "hasFinalizer" field in
  51. // the CachedCCW object.
  52. //
  53. // When GarbageCollector::RegisterFinalizer or GarbageCollector::SuppressFinalizer
  54. // is called, we have managed objects fitting in two buckets:
  55. //
  56. // 1. Those that do not exist in CCW cache. Finalizer is normally registered with
  57. // the GC.
  58. // 2. Those that are in the CCW cache. In such case, GC won't know about the call,
  59. // but instead we'll find the object in the CCW cache and RegisterFinalizer will
  60. // set "hasFinalizer" field to true, while SuppressFinalizer will set it to false
  61. //
  62. // Any methods that interact with s_CCWCache have to lock s_CCWCacheMutex.
  63. struct CachedCCW
  64. {
  65. Il2CppIManagedObjectHolder* managedObjectHolder;
  66. bool hasFinalizer;
  67. };
  68. typedef Il2CppHashMap<Il2CppObject*, CachedCCW, utils::PointerHash<Il2CppObject> > CCWCache;
  69. static baselib::ReentrantLock s_CCWCacheMutex;
  70. static CCWCache s_CCWCache;
  71. #if IL2CPP_SUPPORT_THREADS
  72. struct GarbageCollectorContext
  73. {
  74. GarbageCollectorContext() :
  75. m_StopFinalizer(false),
  76. m_FinalizerThread(nullptr),
  77. m_FinalizerThreadObject(nullptr),
  78. m_FinalizerSemaphore(0, 32767),
  79. m_FinalizersThreadStartedEvent(),
  80. m_FinalizersCompletedEvent(true, false)
  81. {
  82. }
  83. bool m_StopFinalizer;
  84. il2cpp::os::Thread* m_FinalizerThread;
  85. Il2CppThread* m_FinalizerThreadObject;
  86. Semaphore m_FinalizerSemaphore;
  87. Event m_FinalizersThreadStartedEvent;
  88. Event m_FinalizersCompletedEvent;
  89. };
  90. GarbageCollectorContext* s_GarbageCollectorContext = nullptr;
  91. static void FinalizerThread(void* arg)
  92. {
  93. s_GarbageCollectorContext->m_FinalizerThreadObject = il2cpp::vm::Thread::Attach(Domain::GetCurrent());
  94. s_GarbageCollectorContext->m_FinalizerThread->SetName("GC Finalizer");
  95. s_GarbageCollectorContext->m_FinalizersThreadStartedEvent.Set();
  96. while (!s_GarbageCollectorContext->m_StopFinalizer)
  97. {
  98. s_GarbageCollectorContext->m_FinalizerSemaphore.Wait();
  99. GarbageCollector::InvokeFinalizers();
  100. s_GarbageCollectorContext->m_FinalizersCompletedEvent.Set();
  101. }
  102. il2cpp::vm::Thread::Detach(s_GarbageCollectorContext->m_FinalizerThreadObject);
  103. }
  104. bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
  105. {
  106. return s_GarbageCollectorContext->m_FinalizerThreadObject == thread;
  107. }
  108. bool GarbageCollector::IsFinalizerInternalThread(Il2CppInternalThread *thread)
  109. {
  110. return s_GarbageCollectorContext->m_FinalizerThreadObject->GetInternalThread() == thread;
  111. }
  112. #else
  113. bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
  114. {
  115. return false;
  116. }
  117. bool GarbageCollector::IsFinalizerInternalThread(Il2CppInternalThread *thread)
  118. {
  119. return false;
  120. }
  121. #endif
  122. void GarbageCollector::InitializeFinalizer()
  123. {
  124. GarbageCollector::InvokeFinalizers();
  125. #if IL2CPP_SUPPORT_THREADS
  126. s_GarbageCollectorContext = new GarbageCollectorContext();
  127. s_GarbageCollectorContext->m_FinalizerThread = new il2cpp::os::Thread;
  128. s_GarbageCollectorContext->m_FinalizerThread->Run(&FinalizerThread, NULL);
  129. s_GarbageCollectorContext->m_FinalizersThreadStartedEvent.Wait();
  130. #endif
  131. }
  132. void GarbageCollector::UninitializeFinalizers()
  133. {
  134. #if IL2CPP_SUPPORT_THREADS
  135. s_GarbageCollectorContext->m_StopFinalizer = true;
  136. NotifyFinalizers();
  137. s_GarbageCollectorContext->m_FinalizerThread->Join();
  138. delete s_GarbageCollectorContext->m_FinalizerThread;
  139. s_GarbageCollectorContext->m_FinalizerThread = NULL;
  140. s_GarbageCollectorContext->m_StopFinalizer = false;
  141. s_GarbageCollectorContext->m_FinalizerThreadObject = NULL;
  142. delete s_GarbageCollectorContext;
  143. s_GarbageCollectorContext = nullptr;
  144. #endif
  145. }
  146. void GarbageCollector::NotifyFinalizers()
  147. {
  148. #if IL2CPP_SUPPORT_THREADS
  149. s_GarbageCollectorContext->m_FinalizerSemaphore.Post(1, NULL);
  150. #endif
  151. }
  152. void GarbageCollector::RunFinalizer(void *obj, void *data)
  153. {
  154. IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(GarbageCollector::RunFinalizer, "Compare to mono implementation special cases");
  155. Il2CppException *exc = NULL;
  156. Il2CppObject *o;
  157. const MethodInfo* finalizer = NULL;
  158. o = (Il2CppObject*)obj;
  159. finalizer = Class::GetFinalizer(o->klass);
  160. Runtime::Invoke(finalizer, o, NULL, &exc);
  161. if (exc)
  162. Runtime::UnhandledException(exc);
  163. }
  164. void GarbageCollector::RegisterFinalizerForNewObject(Il2CppObject* obj)
  165. {
  166. // Fast path
  167. // No need to check CCW cache since it's guaranteed to not be in it for a new object
  168. RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
  169. }
  170. void GarbageCollector::RegisterFinalizer(Il2CppObject* obj)
  171. {
  172. // Slow path
  173. // Check in CCW cache first
  174. os::FastAutoLock lock(&s_CCWCacheMutex);
  175. CCWCache::iterator it = s_CCWCache.find(obj);
  176. if (it != s_CCWCache.end())
  177. {
  178. it->second.hasFinalizer = true;
  179. }
  180. else
  181. {
  182. RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
  183. }
  184. }
  185. void GarbageCollector::SuppressFinalizer(Il2CppObject* obj)
  186. {
  187. // Slow path
  188. // Check in CCW cache first
  189. os::FastAutoLock lock(&s_CCWCacheMutex);
  190. CCWCache::iterator it = s_CCWCache.find(obj);
  191. if (it != s_CCWCache.end())
  192. {
  193. it->second.hasFinalizer = false;
  194. }
  195. else
  196. {
  197. RegisterFinalizerWithCallback(obj, NULL);
  198. }
  199. }
  200. void GarbageCollector::WaitForPendingFinalizers()
  201. {
  202. if (!GarbageCollector::HasPendingFinalizers())
  203. return;
  204. #if IL2CPP_SUPPORT_THREADS
  205. /* Avoid deadlocks */
  206. if (vm::Thread::Current() == s_GarbageCollectorContext->m_FinalizerThreadObject)
  207. return;
  208. s_GarbageCollectorContext->m_FinalizersCompletedEvent.Reset();
  209. NotifyFinalizers();
  210. s_GarbageCollectorContext->m_FinalizersCompletedEvent.Wait();
  211. #else
  212. GarbageCollector::InvokeFinalizers();
  213. #endif
  214. }
  215. static void CleanupCCW(void* obj, void* data)
  216. {
  217. bool hasFinalizer;
  218. // We have to destroy CCW before invoking the finalizer, because we cannot know whether the finalizer will revive the object
  219. // 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
  220. // rather than returning the one that we're about to destroy here
  221. {
  222. os::FastAutoLock lock(&s_CCWCacheMutex);
  223. CCWCache::iterator it = s_CCWCache.find(static_cast<Il2CppObject*>(obj));
  224. IL2CPP_ASSERT(it != s_CCWCache.end());
  225. Il2CppIManagedObjectHolder* managedObjectHolder = it->second.managedObjectHolder;
  226. hasFinalizer = it->second.hasFinalizer;
  227. s_CCWCache.erase(it);
  228. managedObjectHolder->Destroy();
  229. }
  230. if (hasFinalizer)
  231. GarbageCollector::RunFinalizer(obj, data);
  232. }
  233. // When creating COM Callable Wrappers for classes that project to other Windows Runtime classes
  234. // for instance, System.Uri to Windows.Foundation.Uri, we cannot actually create a wrapper against managed object
  235. // as the native side actually wants an instance of a real Windows.Foundation.Uri class. So do that, our createCCW
  236. // instead of creating a COM Callable Wrapper, will create an instance of that windows runtime class. In that case,
  237. // we do not (AND CANNOT!) put it into a CCW cache because it's not a CCW.
  238. static bool ShouldInsertIntoCCWCache(Il2CppObject* obj)
  239. {
  240. if (obj->klass == il2cpp_defaults.system_uri_class && il2cpp_defaults.windows_foundation_uri_class != NULL)
  241. return false;
  242. /* TODO - Add the rest of the class projections:
  243. System.Collections.Specialized.NotifyCollectionChangedEventArgs <-> Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs
  244. System.ComponentModel.PropertyChangedEventArgs <-> Windows.UI.Xaml.Data.PropertyChangedEventArgs
  245. */
  246. return true;
  247. }
  248. Il2CppIUnknown* GarbageCollector::GetOrCreateCCW(Il2CppObject* obj, const Il2CppGuid& iid)
  249. {
  250. if (obj == NULL)
  251. return NULL;
  252. // check for rcw object. COM interface can be extracted from it and there's no need to create ccw
  253. if (obj->klass->is_import_or_windows_runtime)
  254. {
  255. Il2CppIUnknown* result = RCW::QueryInterfaceNoAddRef<true>(static_cast<Il2CppComObject*>(obj), iid);
  256. result->AddRef();
  257. return result;
  258. }
  259. os::FastAutoLock lock(&s_CCWCacheMutex);
  260. CCWCache::iterator it = s_CCWCache.find(obj);
  261. Il2CppIUnknown* comCallableWrapper;
  262. if (it == s_CCWCache.end())
  263. {
  264. comCallableWrapper = CCW::CreateCCW(obj);
  265. if (ShouldInsertIntoCCWCache(obj))
  266. {
  267. #if IL2CPP_DEBUG
  268. // Assert that CCW::CreateCCW actually returns upcasted Il2CppIManagedObjectHolder
  269. Il2CppIManagedObjectHolder* managedObjectHolder = NULL;
  270. comCallableWrapper->QueryInterface(Il2CppIManagedObjectHolder::IID, reinterpret_cast<void**>(&managedObjectHolder));
  271. IL2CPP_ASSERT(static_cast<void*>(comCallableWrapper) == static_cast<void*>(managedObjectHolder));
  272. managedObjectHolder->Release();
  273. #endif
  274. CachedCCW ccw =
  275. {
  276. static_cast<Il2CppIManagedObjectHolder*>(comCallableWrapper),
  277. RegisterFinalizerWithCallback(obj, &CleanupCCW) != NULL
  278. };
  279. s_CCWCache.insert(std::make_pair(obj, ccw));
  280. }
  281. }
  282. else
  283. {
  284. comCallableWrapper = it->second.managedObjectHolder;
  285. }
  286. Il2CppIUnknown* result;
  287. il2cpp_hresult_t hr = comCallableWrapper->QueryInterface(iid, reinterpret_cast<void**>(&result));
  288. vm::Exception::RaiseIfFailed(hr, true);
  289. return result;
  290. }
  291. int32_t GarbageCollector::GetGeneration(void* addr)
  292. {
  293. return 0;
  294. }
  295. void GarbageCollector::AddMemoryPressure(int64_t value)
  296. {
  297. }
  298. #if IL2CPP_ENABLE_WRITE_BARRIERS
  299. void il2cpp::gc::GarbageCollector::SetWriteBarrier(void **ptr, size_t size)
  300. {
  301. #if IL2CPP_ENABLE_STRICT_WRITE_BARRIERS
  302. for (size_t i = 0; i < size / sizeof(void**); i++)
  303. SetWriteBarrier(ptr + i);
  304. #else
  305. SetWriteBarrier(ptr);
  306. #endif
  307. }
  308. #endif
  309. void il2cpp::gc::GarbageCollector::SetSkipThread(bool skip)
  310. {
  311. }
  312. } // namespace gc
  313. } // namespace il2cpp