vptr_manager.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. * Copyright 2020 ByteDance Games, Inc. All Rights Reserved.
  3. *
  4. * Author: Liuziming <liuziming.kelvin@bytedance.com>
  5. */
  6. #pragma once
  7. #include <stddef.h>
  8. #include <string.h>
  9. #include <string>
  10. #include <vector>
  11. #include <typeinfo>
  12. #include <unordered_map>
  13. #include "shm_helper.h"
  14. # if defined(__GNUC__)
  15. #include <tr2/type_traits>
  16. #include <typeinfo>
  17. #include <map>
  18. #ifdef OUTPUT_BASES
  19. extern std::map<std::string, std::vector<std::string>> g_bases_name;
  20. #endif
  21. #else
  22. #include "type_helper.h"
  23. #define REINTERPRET_CAST_TAG
  24. #endif
  25. namespace bg
  26. {
  27. namespace detail
  28. {
  29. struct BaseTypeInfo
  30. {
  31. TypeName type;
  32. ptrdiff_t offset;
  33. BaseTypeInfo(const char* type, ptrdiff_t offset) : type(type), offset(offset) {}
  34. };
  35. class VptrObjectContainerBase
  36. {
  37. public:
  38. VptrObjectContainerBase(const TypeName& type, bool in_shm);
  39. virtual ~VptrObjectContainerBase();
  40. virtual void FixObjects() = 0;
  41. virtual VptrObjectContainerBase* ShmNew() = 0;
  42. void FixVptr();
  43. const TypeName& GetTypeName() const { return m_type; }
  44. const BaseTypeInfo* GetBaseTypeInfo(const TypeName& base_type);
  45. void RecordObject(void* ptr);
  46. void RemoveObject(void* ptr);
  47. protected:
  48. bool FixSelf();
  49. void DoRemoveObject(size_t index);
  50. protected:
  51. static constexpr const double EXPANSION_FACTOR = 1.5;
  52. uint64_t m_version = 0;
  53. TypeName m_type;
  54. // As we have containers both in normal memory and shared
  55. // memory, so we make the pointer container as pointer here,
  56. // only initialize it when it's used in shared memory.
  57. std::vector<void*, ShmAllocator<void*>>* m_objects = nullptr;
  58. // The following field is initialized in derived class constructor.
  59. std::vector<BaseTypeInfo>* m_base_types = nullptr;
  60. };
  61. template<typename T>
  62. class VptrObjectContainer final : public VptrObjectContainerBase
  63. {
  64. public:
  65. static const T static_object;
  66. static const VptrObjectContainer<T> static_container;
  67. /*
  68. * NOTE: The 2nd arg to `TypeName(...)` MUST BE 0 here, because when
  69. * we delete a pointer of polymorphic type, we can only know it's real
  70. * type with `typeid(*ptr).name()`, but we cannot get the size of that
  71. * real type, so we have to abandon size here, to match the info we can
  72. * get at deletion. The same as following calls to this function.
  73. */
  74. VptrObjectContainer() : VptrObjectContainerBase(TypeName(typeid(T).name()), false)
  75. {
  76. SHM_DEBUG_ASSERT(this == &static_container);
  77. size_t count = GetBaseClassCount();
  78. if(count > 0)
  79. {
  80. m_base_types = new std::vector<BaseTypeInfo>();
  81. m_base_types->reserve(count);
  82. FillBaseTypeInfo(m_base_types, Index2Type<0>());
  83. }
  84. }
  85. VptrObjectContainer(const VptrObjectContainer& that) : VptrObjectContainerBase(that.m_type, true)
  86. {
  87. SHM_DEBUG_ASSERT(&that == &static_container);
  88. }
  89. ~VptrObjectContainer() final = default;
  90. void FixObjects() final
  91. {
  92. SHM_TRACE(">>>>> fixing vptr of objects in container(%p: %s)...", this, m_type.c_str());
  93. for(void* ptr : *m_objects)
  94. {
  95. DoFix(ptr, std::integral_constant<bool, GetBaseClassCount() != 0>(), Index2Type<0>());
  96. }
  97. SHM_TRACE("<<<<< vptr of objects in container(%p: %s) fixed.", this, m_type.c_str());
  98. }
  99. VptrObjectContainerBase* ShmNew() final
  100. {
  101. SHM_DEBUG_ASSERT(this == &static_container);
  102. ShmAllocator<VptrObjectContainer<T>> allocator;
  103. VptrObjectContainer<T>* ptr = allocator.allocate(1);
  104. if(SHM_LIKELY(ptr))
  105. {
  106. allocator.construct(ptr, *this);
  107. }
  108. return ptr;
  109. }
  110. private:
  111. template<typename... Types>
  112. struct List
  113. {
  114. static constexpr size_t count = sizeof...(Types);
  115. };
  116. template<typename NewType, typename List>
  117. struct PrependList;
  118. template<typename NewType, typename... Types>
  119. struct PrependList<NewType, List<Types...>>
  120. {
  121. using ListType = List<NewType, Types...>;
  122. };
  123. template<typename List>
  124. struct RemoveFirst;
  125. template<typename Head, typename... Rest>
  126. struct RemoveFirst<List<Head, Rest...>>
  127. {
  128. using RestList = List<Rest...>;
  129. };
  130. struct NullType {};
  131. template<typename T>
  132. struct bases
  133. {
  134. typedef __reflection_typelist<> type;
  135. };
  136. template<typename Type>
  137. # if defined(__GNUC__)
  138. using RefList = typename std::tr2::bases<Type>::type;
  139. #elif defined(_MSC_VER)
  140. using RefList = typename bases<Type>::type;
  141. #endif
  142. template<bool empty, typename RefList>
  143. struct DoMakeList;
  144. template<typename RefList>
  145. struct DoMakeList<true, RefList>
  146. {
  147. using ListType = List<NullType>;
  148. };
  149. template<typename RefList>
  150. struct DoMakeList<false, RefList>
  151. {
  152. using ListType =
  153. typename PrependList<
  154. typename RefList::first::type,
  155. typename DoMakeList<
  156. RefList::rest::type::empty::value,
  157. typename RefList::rest::type
  158. >::ListType
  159. >::ListType;
  160. };
  161. template<typename Type>
  162. struct MakeList
  163. {
  164. using Result =
  165. typename DoMakeList<
  166. RefList<Type>::empty::value,
  167. RefList<Type>
  168. >::ListType;
  169. };
  170. template<size_t N, typename BaseList>
  171. struct GetNthBaseClass;
  172. template<size_t N, typename Head, typename... Rest>
  173. struct GetNthBaseClass<N, List<Head, Rest...>>
  174. {
  175. using BaseType = typename GetNthBaseClass<N - 1, List<Rest...>>::BaseType;
  176. };
  177. template<typename Head, typename... Rest>
  178. struct GetNthBaseClass<0, List<Head, Rest...>>
  179. {
  180. using BaseType = Head;
  181. };
  182. # if defined(__GNUC__)
  183. template<typename Derived, typename Base>
  184. struct BaseClassOffset
  185. {
  186. static constexpr const Derived* derived = &VptrObjectContainer<T>::static_object;
  187. static constexpr const Base* base = static_cast<const Base*>(derived);
  188. static REINTERPRET_CAST_TAG ptrdiff_t value;
  189. static constexpr ptrdiff_t value =
  190. reinterpret_cast<intptr_t>(base) - reinterpret_cast<intptr_t>(derived);
  191. /*
  192. * This does not work with higher GCC version, due to C++ 11 standard specifies
  193. * that `reinterpret_cast` cannot be used inside constexpr expression. See this
  194. * link for detail:
  195. * https://stackoverflow.com/questions/24398102/constexpr-and-initialization-of-a-static-const-void-pointer-with-reinterpret-cas
  196. *
  197. * static constexpr ptrdiff_t value =
  198. * reinterpret_cast<intptr_t>(static_cast<Base*>(static_cast<Derived*>(0) + 1)) -
  199. * reinterpret_cast<intptr_t>(static_cast<Derived*>(0) + 1);
  200. */
  201. };
  202. #endif
  203. template<size_t N>
  204. # if defined(__GNUC__)
  205. static constexpr ptrdiff_t GetBaseClassOffset()
  206. {
  207. using BaseList = typename MakeList<T>::Result;
  208. using BaseType = typename GetNthBaseClass<N, BaseList>::BaseType;
  209. return BaseClassOffset<T, BaseType>::value;
  210. }
  211. #else
  212. static REINTERPRET_CAST_TAG ptrdiff_t GetBaseClassOffset()
  213. {
  214. using BaseList = typename MakeList<T>::Result;
  215. using BaseType = typename GetNthBaseClass<N, BaseList>::BaseType;
  216. static constexpr const T* derived = &VptrObjectContainer<T>::static_object;
  217. static constexpr const BaseType* base = static_cast<const BaseType*>(derived);
  218. return reinterpret_cast<intptr_t>(base) - reinterpret_cast<intptr_t>(derived);
  219. }
  220. #endif
  221. static constexpr size_t GetBaseClassCount()
  222. {
  223. // There is a NullType at the end of the list, so we compare with 1 here
  224. static_assert(MakeList<T>::Result::count >= 1, "Invalid base class count");
  225. return MakeList<T>::Result::count - 1;
  226. }
  227. /*
  228. * The BEST solution should be the following:
  229. * Generate an base class offset array at compile time, during fixing, iterate
  230. * over the array and fix each vptr at corresponding offset. However, there is
  231. * a bug in GCC(4.8), which leads to the following code cannot compile:
  232. *
  233. * internal compiler error: in convert_nontype_argument, at cp/pt.c:5623
  234. * Please submit a full bug report.
  235. * ...
  236. *
  237. * So, we have to use template function specialization instead to solve this
  238. * problem without a lot of ugly macro definitions.
  239. *
  240. *
  241. * template<ptrdiff_t... offsets>
  242. * struct OffsetHolder {
  243. * static constexpr ptrdiff_t base_offsets[sizeof...(offsets)] = { offsets... };
  244. * };
  245. *
  246. * template<size_t total, typename BaseList, ptrdiff_t... offsets>
  247. * struct DoGenerateOffsets {
  248. * using Result = typename DoGenerateOffsets<
  249. * total,
  250. * typename RemoveFirst<BaseList>::Rest,
  251. * // There is a NullType at the end of the list, we should make
  252. * // this consistent with value defined in GetBaseClassCount()
  253. * GetBaseClassOffset<total - (BaseList::count - 1)>(),
  254. * offsets...
  255. * >::Result;
  256. * };
  257. *
  258. * template<size_t total, ptrdiff_t... offsets>
  259. * struct DoGenerateOffsets<total, List<NullType>, offsets...> {
  260. * using Result = OffsetHolder<offsets...>;
  261. * };
  262. * struct GenerateOffsets {
  263. * using Result = typename DoGenerateOffsets<
  264. * GetBaseClassCount(), typename MakeList<T>::Result
  265. * >::Result;
  266. * };
  267. *
  268. * void DoFix(void *ptr) {
  269. * using OffsetArray = GenerateOffsets::Result;
  270. * for (size_t i = 0; i < ARRAY_SIZE(OffsetArray::base_offsets); ++i) {
  271. * const ptrdiff_t offset = OffsetArray::base_offsets[i];
  272. * // do fix vptr here
  273. * }
  274. * }
  275. *
  276. */
  277. template<size_t>
  278. struct Index2Type {};
  279. template<size_t index>
  280. void FillBaseTypeInfo(std::vector<BaseTypeInfo>* base_types, Index2Type<index>)
  281. {
  282. using BaseList = typename MakeList<T>::Result;
  283. using BaseType = typename GetNthBaseClass<index, BaseList>::BaseType;
  284. static constexpr const ptrdiff_t offset = GetBaseClassOffset<index>();
  285. static_assert(offset >= 0, "invalid offset");
  286. BaseTypeInfo info(typeid(BaseType).name(), offset);
  287. base_types->push_back(info);
  288. FillBaseTypeInfo(base_types, Index2Type<index + 1>());
  289. }
  290. void FillBaseTypeInfo(std::vector<BaseTypeInfo>*, Index2Type<GetBaseClassCount()>) {}
  291. // This function means there is no base class for class T.
  292. void DoFix(void* ptr, std::false_type, Index2Type<0>)
  293. {
  294. static constexpr const T* t = &static_object;
  295. const uintptr_t* new_vptr = reinterpret_cast<const uintptr_t*>(reinterpret_cast<const char*>(t));
  296. uintptr_t* old_vptr = reinterpret_cast<uintptr_t*>(reinterpret_cast<char*>(ptr));
  297. if(!HasVptr<T>::value)
  298. {
  299. SHM_ERROR("object(%p), type(%s), no base type, vptr not found(%#lx : %#lx), at least one virtual function is required.",
  300. ptr, m_type.c_str(), *old_vptr, *new_vptr);
  301. return;
  302. }
  303. SHM_DEBUG("object(%p), type(%s), no base type, old vptr(%#lx), new vptr(%#lx)", ptr, m_type.c_str(), *old_vptr, *new_vptr);
  304. if(*old_vptr != *new_vptr)
  305. {
  306. *old_vptr = *new_vptr;
  307. }
  308. }
  309. template<size_t index>
  310. void DoFix(void* ptr, std::true_type, Index2Type<index>)
  311. {
  312. using BaseList = typename MakeList<T>::Result;
  313. using BaseType = typename GetNthBaseClass<index, BaseList>::BaseType;
  314. static constexpr const ptrdiff_t offset = GetBaseClassOffset<index>();
  315. static constexpr const T* t = &static_object;
  316. const uintptr_t* new_vptr = reinterpret_cast<const uintptr_t*>(reinterpret_cast<const char*>(t) + offset);
  317. uintptr_t* old_vptr = reinterpret_cast<uintptr_t*>(reinterpret_cast<char*>(ptr) + offset);
  318. TypeName base_type(typeid(BaseType).name());
  319. if(!HasVptr<BaseType>::value)
  320. {
  321. SHM_ERROR("object(%p), type(%s), base type(%s), base index(%lu锛? offset(%#lx), vptr(%#lx : %#lx) not found, a virtual destructor is recommended.",
  322. ptr, m_type.c_str(), base_type.c_str(), index, offset, *old_vptr, *new_vptr);
  323. }
  324. else
  325. {
  326. SHM_DEBUG("object(%p), type(%s), base type(%s), base index(%lu锛? offset(%#lx), old vptr(%#lx), new vptr(%#lx)",
  327. ptr, m_type.c_str(), base_type.c_str(), index, offset, *old_vptr, *new_vptr);
  328. if(*old_vptr != *new_vptr)
  329. {
  330. *old_vptr = *new_vptr;
  331. }
  332. }
  333. DoFix(ptr, std::true_type(), Index2Type<index + 1>());
  334. }
  335. void DoFix(void*, std::true_type, Index2Type<GetBaseClassCount()>) {}
  336. };
  337. //template<typename T> const T VptrObjectContainer<T>::static_object = T();
  338. template<typename T> const T VptrObjectContainer<T>::static_object;
  339. template<typename T> const VptrObjectContainer<T> VptrObjectContainer<T>::static_container;
  340. class VptrManager
  341. {
  342. public:
  343. static bool Init();
  344. static void Fini();
  345. ~VptrManager();
  346. template<typename T, typename... Args>
  347. static T* NewVptrObject(Args&&... args)
  348. {
  349. return NewVptrObjectExtraSpace<T>(0, std::forward<Args>(args)...);
  350. }
  351. template<typename T, typename... Args>
  352. static T* NewVptrObjectExtraSpace(size_t extra_space, Args&&... args)
  353. {
  354. static_assert(HasVptr<T>::value, "type must be polymorphic or have vptr at least!");
  355. const TypeName& type = VptrObjectContainer<T>::static_container.GetTypeName();
  356. // Do not use static assertion here, sometimes it might not need a virtual destructor.
  357. // static_assert(std::has_virtual_destructor<T>::value, "polymorphic type must have a virtual destructor!");
  358. if(!std::has_virtual_destructor<T>::value)
  359. {
  360. SHM_ERROR("it's better to declare destructor virtual for polymorphic type(%s).", type.c_str());
  361. }
  362. void* ptr = NewSpace(sizeof(T) + extra_space);
  363. if(SHM_LIKELY(ptr))
  364. {
  365. new (ptr) T(std::forward<Args>(args)...);
  366. RecordObject(type, ptr);
  367. }
  368. return static_cast<T*>(ptr);
  369. }
  370. template<typename T>
  371. static void DelVptrObject(T* ptr)
  372. {
  373. static_assert(HasVptr<T>::value, "type must be polymorphic or have vptr at least!");
  374. if(SHM_LIKELY(ptr))
  375. {
  376. TypeName base_type(typeid(T).name());
  377. TypeName real_type(typeid(*ptr).name());
  378. void* real_ptr = GetRealPtr(base_type, real_type, ptr);
  379. RemoveObject(real_type, real_ptr);
  380. ptr->~T();
  381. FreeSpace(real_ptr);
  382. }
  383. }
  384. static void FixVptr();
  385. private:
  386. static void* NewSpace(size_t bytes);
  387. static void FreeSpace(void* ptr);
  388. static void* GetRealPtr(const TypeName& base_type, const TypeName& real_type, void* ptr);
  389. static void RecordObject(const TypeName& type, void* ptr);
  390. static void RemoveObject(const TypeName& type, void* ptr);
  391. private:
  392. template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Pred = std::equal_to<Key>>
  393. using Container = std::unordered_map<Key, Value, Hash, Pred, ShmAllocator<std::pair<const Key, Value>>>;
  394. Container<TypeName, VptrObjectContainerBase*, TypeNameHasher> m_type_to_containers;
  395. };
  396. } // namespace detail
  397. } // namespace bg