123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- /*
- * Copyright 2020 ByteDance Games, Inc. All Rights Reserved.
- *
- * Author: Liuziming <liuziming.kelvin@bytedance.com>
- */
- #pragma once
- #include <string>
- #include <vector>
- #include <list>
- #include <set>
- #include <map>
- #include <unordered_set>
- #include <unordered_map>
- #include "vptr_manager.h"
- #include "shm_helper.h"
- #include <stdint.h>
- // Containers redefined for shm from `std` namespace.
- namespace bg
- {
- using ShmString = std::basic_string<char, std::char_traits<char>, detail::ShmAllocator<char>>;
- template<typename T>
- using ShmVector = std::vector<T, detail::ShmAllocator<T>>;
- template<typename T>
- using ShmList = std::list<T, detail::ShmAllocator<T>>;
- template<typename Key, typename Compare = std::less<Key>>
- using ShmSet = std::set<Key, Compare, detail::ShmAllocator<Key>>;
- template<typename Key, typename Value, typename Compare = std::less<Key>>
- using ShmMap = std::map<Key, Value, Compare, detail::ShmAllocator<std::pair<const Key, Value>>>;
- template<typename Key, typename Hash = std::hash<Key>, typename Pred = std::equal_to<Key>>
- using ShmUnorderedSet = std::unordered_set<Key, Hash, Pred, detail::ShmAllocator<Key>>;
- template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Pred = std::equal_to<Key>>
- using ShmUnorderedMap = std::unordered_map<Key, Value, Hash, Pred, detail::ShmAllocator<std::pair<const Key, Value>>>;
- } // namespace bg
- // C-style memory related functions for shm.
- namespace bg
- {
- /**
- * 分配一块共享内存块
- * @param bytes 要分配的共享内存块的大小
- * @return 分配的共享内存块的首地址,如果分配失败,返回NULL
- */
- void* ShmMalloc(size_t bytes);
- /**
- * 调整已分配的共享内存块的大小
- * @param old_ptr 要调整的共享内存块的首地址
- * @param new_bytes 要调整的新的大小
- * @return 调整后的共享内存块的首地址
- * @note 调用本函数分以下几种情况:
- * 1. 如果old_ptr为NULL,则该调用等同于ShmMalloc(new_bytes)
- * 2. 如果new_bytes为0,则该调用等同于ShmFree(old_ptr),并返回NULL
- * 3. 其它情况,视new_bytes大小决定:
- * - 原共享内存块大小足够并且不触发内存缩减操作时,直接返回原共享内存块
- * - 原共享内存块大小不足或者new_bytes远小于原共享内存块大小时,调用ShmMalloc分配新共享内
- * 存块,将原内存的内容使用memcpy拷贝到新内存,然后调用ShmFree释放原共享内存块并返回新共
- * 享内存块。如果ShmMalloc分配失败,则不释放原内存并返回NULL。注意,如果new_bytes小于原内
- * 存大小,则在调用memcpy时会产生内容截断。
- */
- void* ShmRealloc(void* old_ptr, size_t new_bytes);
- /**
- * 分配n块连续的共享内存块并填充0
- * @param n 要分配的共享内存块的数量
- * @param bytes 要分配的共享内存块的大小
- * @return 分配的已填充0的连续的共享内存块的首地址,如果分配失败,返回NULL
- */
- void* ShmCalloc(size_t n, size_t bytes);
- /**
- * 释放传入的共享内存块
- * @param ptr 要释放的共享内存块的首地址
- */
- void ShmFree(void* ptr);
- } // namespace bg
- // Helper functions.
- namespace bg
- {
- namespace detail
- {
- template<typename T, typename... Args>
- T* ShmNew(std::true_type /* objects with vptr(s) */, Args&& ... args)
- {
- return VptrManager::NewVptrObject<T>(std::forward<Args>(args)...);
- }
- template<typename T, typename... Args>
- T* ShmNewExtraSpace(std::true_type /* objects with vptr(s) */, size_t extra_space, Args&& ... args)
- {
- return VptrManager::NewVptrObjectExtraSpace<T>(extra_space, std::forward<Args>(args)...);
- }
- template<typename T, typename... Args>
- T* ShmNew(std::false_type /* objects without vptr(s) */, Args&& ... args)
- {
- void* ptr = ShmMalloc(sizeof(T));
- if(SHM_LIKELY(ptr))
- {
- new(ptr) T(std::forward<Args>(args)...);
- }
- return static_cast<T*>(ptr);
- }
- template<typename T, typename... Args>
- T* ShmNewExtraSpace(std::false_type /* objects without vptr(s) */, size_t extra_space, Args&& ... args)
- {
- void* ptr = ShmMalloc(sizeof(T) + extra_space);
- if(SHM_LIKELY(ptr))
- {
- new(ptr) T(std::forward<Args>(args)...);
- }
- return static_cast<T*>(ptr);
- }
- template<typename T>
- void ShmDelete(std::true_type /* objects with vptr(s) */, T* ptr)
- {
- VptrManager::DelVptrObject(ptr);
- }
- template<typename T>
- void ShmDelete(std::false_type /* objects without vptr(s) */, T* ptr)
- {
- if(SHM_LIKELY(ptr))
- {
- ptr->~T();
- ShmFree(ptr);
- }
- }
- } // namespace detail
- } // namespace bg
- // C++-style memory related functions for shm.
- namespace bg
- {
- /**
- * 构造一个保存在共享内存中的类的实例
- * @tparam T 要保存在共享内存中的类的类型
- * @tparam Args T类型的构造函数的参数类型
- * @param args T类型的构造函数的参数
- * @return 指向T类型实例的指针,如果分配失败,返回NULL
- */
- template<typename T, typename... Args>
- T* ShmNew(Args&&... args)
- {
- return detail::ShmNew<T>(detail::HasVptr<T>(), std::forward<Args>(args)...);
- }
- /**
- * 构造一个保存在共享内存中的类的实例,并且分配额外的内存(额外的内存紧临该实例)
- * @tparam T 要保存在共享内存中的类的类型
- * @tparam Args T类型的构造函数的参数类型
- * @param extra_space 要额外分配的内存大小
- * @param args T类型的构造函数的参数
- * @return 指向T类型实例的指针,如果分配失败,返回NULL
- */
- template<typename T, typename... Args>
- T* ShmNewExtraSpace(size_t extra_space, Args&&... args)
- {
- return detail::ShmNewExtraSpace<T>(detail::HasVptr<T>(), extra_space, std::forward<Args>(args)...);
- }
- /**
- * 析构一个保存在共享内存中的类的实例
- * @tparam T 保存在共享内存中的类的类型
- * @param ptr T类型实例的指针
- */
- template<typename T>
- void ShmDelete(T* ptr)
- {
- detail::ShmDelete(detail::HasVptr<T>(), ptr);
- }
- } // namespace bg
- // Singleton related functions.
- namespace bg
- {
- /**
- * 获得一个保存在共享内存中的单例
- * @tparam T 要获取的单例类型
- * @tparam Args T类型的构造函数的参数类型
- * @param args T类型的构造函数的参数
- * @return 指向T类型单例的指针,如果分配失败,返回NULL
- * @note T类型不能是多态类型,否则编译失败
- */
- template<typename T, typename... Args>
- T* ShmGetSingleton(Args&& ... args)
- {
- static_assert(!detail::HasVptr<T>::value, "singleton does not support polymorphic types.");
- detail::TypeName type(typeid(T).name(), sizeof(T));
- bool first_call = false;
- void* ptr = detail::ShmGetSingleton(type, sizeof(T), &first_call);
- if(ptr && first_call)
- {
- new(ptr) T(std::forward<Args>(args)...);
- }
- return static_cast<T*>(ptr);
- }
- /**
- * 销毁一个保存在共享内存中的单例
- * @tparam T 单例类的类型
- * @note T类型不能是多态类型,否则编译失败
- */
- template<typename T>
- void ShmDeleteSingleton()
- {
- static_assert(!detail::HasVptr<T>::value, "singleton does not support polymorphic types.");
- detail::TypeName type(typeid(T).name(), sizeof(T));
- if(!detail::ShmHasSingleton(type))
- {
- return;
- }
- void* ptr = detail::ShmGetSingleton(type, sizeof(T), nullptr);
- SHM_ASSERT_RETURN_VOID(ptr);
- static_cast<T*>(ptr)->~T();
- detail::ShmFreeSingleton(type);
- }
- } // namespace bg
- // Initialize/Finalize functions.
- namespace bg
- {
- /**
- * 初始化整个Shm模块时的一些可配置选项
- */
- struct ShmOptions
- {
- // 是否以resume模式初始化Shm模块
- bool resume;
- // 是否要替换系统内存分配器,目前没有实现,但是依然强烈不建议打开此选项
- bool replace_sys_alloc;
- // 是否在ShmInit里面去修复虚表指针,默认值为true,如果有dlopen打开so,并且so
- // 里面有虚表指针需要修复的话,建议此选项为false,同时,此时需要调用者自己在
- // dlopen之后去调用修复虚表指针的函数
- bool fix_vptr_on_init;
- // Shm模块的日志输出函数,如果为NULL,则所有日志默认输出到stdout
- ShmLogger* logger;
- // Shm模块管理器挂载的内存地址,默认值为0x600000000000,通常情况下不需要自定
- // 义。如果要自定义该值,请避开进程中已映射的区域(可查看/proc/<pid>/maps),并
- // 尽量选用较空的位置,不然整个Shm模块将无法工作
- uintptr_t main_address;
- // 每块原始共享内存的映射大小,也是每块原始共享内存的最大大小,默认值为4GB,通
- // 常情况下不需要自定义
- size_t shm_block_mmap_size;
- // 在原始共享内存不足时增长的大小,默认值是32MB,通常情况下不需要自定义
- size_t shm_block_grow_size;
- // 原始共享内存可挂载的内存地址的数量,默认值为16(即fixed_addresses的大小)
- size_t fixed_address_count;
- // Shm模块的标识符,由于共享内存全系统可见,为防止冲突,请务必保证每个进程有
- // 独一无二的标识符,并且在进程重启后保证标识符不变
- char identifier[64];
- // 原始共享内存可挂载的内存地址列表,默认有16个地址,通常情况下这些地址不需要
- // 自定义。如果要自定义,请参考main_address的自定义方式,并且保证从每个地址开
- // 始,到加上shm_block_grow_size结束的区域不会和别的内存映射区域重叠
- uintptr_t fixed_addresses[16];
- /**
- * 配置项构造函数,设置传入的选项,并将其它选项设置为默认值
- * @param resume 设置是否是resume模式
- * @param identifier 设置模块标识符,不能超过64字节
- * @param logger 设置日志输出函数,可以为NULL
- */
- ShmOptions(bool resume, const char* identifier, ShmLogger* logger);
- bool AddFixedAddress(uintptr_t addr);
- bool PopFixedAddress(uintptr_t* addr);
- static size_t FillDefaultFixedAddresses(uintptr_t* result, size_t max_count);
- };
- /**
- * 初始化整个Shm模块
- * @param options 用于初始化Shm模块的一些配置项
- * @return 成功返回true,失败返回false,并输出相应的错误日志
- * @note 在使用任何本模块的内存操作函数前,必须调用本函数进程初始化
- */
- bool ShmInit(const ShmOptions& options);
- /**
- * 销毁整个Shm模块
- * @note 本函数会销毁整个Shm模块,以及共享内存中的数据,因此,只有在
- * 正常停服时才需要调用本函数,进行热更新或者进程崩溃等需要重启进
- * 程并使用原来共享内存的情况下,请务必*不要*调用本函数
- */
- void ShmFini();
- /**
- * 修复在共享内存中实例的虚表指针
- * @note 通常情况下,虚表指针会在ShmInit中进行修复,但有些程序会用
- * dlopen打开so,而如果dlopen发生在ShmInit之后,则ShmInit没办法修
- * 复so中的虚表指针,此时,需要调用者在dlopen之后显式调用该函数进
- * 行虚表指针修复
- */
- void ShmFixVptr();
- } // namespace bg
- // A dirty hack on `std::hash` for `bg::ShmString`.
- namespace std
- {
- template<>
- struct hash<bg::ShmString>
- {
- size_t operator()(const bg::ShmString& str) const noexcept
- {
- return std::_Fnv1a_append_value(str.length(), str.data());
- }
- };
- } // namespace std
|