CachedCCWBase.h 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #pragma once
  2. #include "gc/GCHandle.h"
  3. #include "os/Atomic.h"
  4. #include "vm/CCWBase.h"
  5. #include "utils/Memory.h"
  6. #include "utils/TemplateUtils.h"
  7. #include "Baselib.h"
  8. #include "Cpp/Atomic.h"
  9. namespace il2cpp
  10. {
  11. namespace vm
  12. {
  13. // Alright, so the lifetime of this guy is pretty weird
  14. // For a single managed object, the IUnknown of its COM Callable Wrapper must always be the same
  15. // That means that we have to keep the same COM Callable Wrapper alive for an object once we create it
  16. // They are cached in il2cpp::vm::g_CCWCache, which is managed by il2cpp::vm::CCW class
  17. //
  18. // Here comes the tricky part: when a native object has a reference to the COM Callable Wrapper,
  19. // the managed object is not supposed to be garbage collected. However, when no native objects are referencing
  20. // it, it should not prevent the GC from collecting the managed object. We implement this by keeping a GC handle
  21. // on the managed object if our reference count is 1 or more. We acquire it when it gets increased from 0 (this
  22. // is safe because such AddRef can only come when this object is retrieved from CCW Cache) and release the GC
  23. // handle when our reference count gets decreased to 0. Here's a kicker: we don't destroy the COM Callable Wrapper
  24. // when the reference count reaches 0; we instead rely on GC finalizer of the managed object to both remove it from
  25. // CCW cache and also destroy it.
  26. template<typename TDerived>
  27. struct NOVTABLE CachedCCWBase : CCWBase
  28. {
  29. private:
  30. baselib::atomic<uint32_t> m_RefCount;
  31. uint32_t m_GCHandle;
  32. public:
  33. inline CachedCCWBase(Il2CppObject* obj) :
  34. CCWBase(obj),
  35. m_RefCount(0), // We do not hold any references upon its creation
  36. m_GCHandle(0)
  37. {
  38. Il2CppStaticAssert(utils::TemplateUtils::IsBaseOf<CachedCCWBase<TDerived>, TDerived>::value);
  39. }
  40. virtual uint32_t STDCALL AddRef() IL2CPP_OVERRIDE
  41. {
  42. return AddRefImpl();
  43. }
  44. virtual uint32_t STDCALL Release() IL2CPP_OVERRIDE
  45. {
  46. return ReleaseImpl();
  47. }
  48. // AddRef can be called at any time whatsoever, as it's called when
  49. // managed objects are passed to native code
  50. IL2CPP_NO_INLINE uint32_t AddRefImpl()
  51. {
  52. const uint32_t refCount = ++m_RefCount;
  53. if (refCount == 1)
  54. {
  55. // Since AddRef can be called at any time, it's possible that
  56. // at this point we're in middle of ReleaseImpl call just after
  57. // it decrements the gccount to 0 but hasn't released m_GCHandle
  58. // yet. We spin until it is released.
  59. uint32_t gcHandle = gc::GCHandle::New(GetManagedObjectInline(), false);
  60. while (os::Atomic::CompareExchange(&m_GCHandle, gcHandle, 0) != 0) {}
  61. }
  62. return refCount;
  63. }
  64. // Release can be called only if m_RefCount is greater than 0,
  65. // and the AddRef call that has increased the ref count above 0 has returned
  66. IL2CPP_NO_INLINE uint32_t ReleaseImpl()
  67. {
  68. const uint32_t count = --m_RefCount;
  69. if (count == 0)
  70. {
  71. // We decreased the ref count to 0, so we are responsible
  72. // for freeing the handle. Only one ReleaseImpl that reduced
  73. // ref count to 0 will ever be in flight at the same time
  74. // because AddRefImpl that takes us out of this state halts until
  75. // we set m_GCHandle to zero.
  76. uint32_t gcHandle = os::Atomic::Exchange(&m_GCHandle, 0);
  77. IL2CPP_ASSERT(gcHandle != 0);
  78. gc::GCHandle::Free(gcHandle);
  79. }
  80. return count;
  81. }
  82. virtual void STDCALL Destroy() IL2CPP_FINAL IL2CPP_OVERRIDE
  83. {
  84. IL2CPP_ASSERT(m_RefCount == 0);
  85. TDerived* instance = static_cast<TDerived*>(this);
  86. instance->~TDerived();
  87. utils::Memory::Free(instance);
  88. }
  89. };
  90. }
  91. }