GarbageCollector.cpp 12 KB

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