CachedCCWBase.h 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #pragma once
  2. #include "gc/GCHandle.h"
  3. #include "vm/ComObjectBase.h"
  4. #include "utils/TemplateUtils.h"
  5. namespace il2cpp
  6. {
  7. namespace vm
  8. {
  9. // Alright, so the lifetime of this guy is pretty weird
  10. // For a single managed object, the IUnknown of its COM Callable Wrapper must always be the same
  11. // That means that we have to keep the same COM Callable Wrapper alive for an object once we create it
  12. // They are cached in il2cpp::vm::g_CCWCache, which is managed by il2cpp::vm::CCW class
  13. //
  14. // Here comes the tricky part: when a native object has a reference to the COM Callable Wrapper,
  15. // the managed object is not supposed to be garbage collected. However, when no native objects are referencing
  16. // it, it should not prevent the GC from collecting the managed object. We implement this by keeping a GC handle
  17. // on the managed object if our reference count is 1 or more. We acquire it when it gets increased from 0 (this
  18. // is safe because such AddRef can only come when this object is retrieved from CCW Cache) and release the GC
  19. // handle when our reference count gets decreased to 0. Here's a kicker: we don't destroy the COM Callable Wrapper
  20. // when the reference count reaches 0; we instead rely on GC finalizer of the managed object to both remove it from
  21. // CCW cache and also destroy it.
  22. template<typename TDerived>
  23. struct NOVTABLE CachedCCWBase : ComObjectBase
  24. {
  25. private:
  26. volatile uint32_t m_RefCount;
  27. uint32_t m_GCHandle;
  28. public:
  29. inline CachedCCWBase(Il2CppObject* obj) :
  30. ComObjectBase(obj),
  31. m_RefCount(0), // We do not hold any references upon its creation
  32. m_GCHandle(0)
  33. {
  34. Il2CppStaticAssert(utils::TemplateUtils::IsBaseOf<CachedCCWBase<TDerived>, TDerived>::value);
  35. }
  36. virtual uint32_t STDCALL AddRef() IL2CPP_OVERRIDE
  37. {
  38. return AddRefImpl();
  39. }
  40. virtual uint32_t STDCALL Release() IL2CPP_OVERRIDE
  41. {
  42. return ReleaseImpl();
  43. }
  44. FORCE_INLINE uint32_t AddRefImpl()
  45. {
  46. const uint32_t refCount = Atomic::Increment(&m_RefCount);
  47. if (refCount == 1)
  48. {
  49. IL2CPP_ASSERT(m_GCHandle == 0);
  50. m_GCHandle = gc::GCHandle::New(GetManagedObjectInline(), false);
  51. }
  52. return refCount;
  53. }
  54. FORCE_INLINE uint32_t ReleaseImpl()
  55. {
  56. const uint32_t count = Atomic::Decrement(&m_RefCount);
  57. if (count == 0)
  58. {
  59. IL2CPP_ASSERT(m_GCHandle != 0);
  60. gc::GCHandle::Free(m_GCHandle);
  61. m_GCHandle = 0;
  62. }
  63. return count;
  64. }
  65. virtual void STDCALL Destroy() IL2CPP_FINAL IL2CPP_OVERRIDE
  66. {
  67. IL2CPP_ASSERT(m_RefCount == 0);
  68. TDerived* instance = static_cast<TDerived*>(this);
  69. instance->~TDerived();
  70. utils::Memory::Free(instance);
  71. }
  72. };
  73. }
  74. }