/** * Copyright (c) 2021 OceanBase * OceanBase Database Proxy(ODP) is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #ifndef OB_LIB_CONCURRENCY_OBJPOOL_H_ #define OB_LIB_CONCURRENCY_OBJPOOL_H_ #include #include "lib/allocator/page_arena.h" #include "lib/utility/ob_print_utils.h" #include "lib/list/ob_atomic_list.h" #include "lib/list/ob_intrusive_list.h" #include "lib/lock/ob_mutex.h" #include "lib/container/ob_vector.h" DEFINE_HAS_MEMBER(OP_LOCAL_NUM); namespace oceanbase { namespace common { struct ObThreadCache; class ObObjFreeList; class ObObjFreeListList; class ObFixedMemAllocator; enum ObMemCacheType { OP_GLOBAL, OP_TC, OP_RECLAIM }; enum ObReclaimOptType { ENABLE_RECLAIM, STEAL_THREAD_COUNT, RECLAIM_FACTOR, MAX_OVERAGE, DEBUG_FILTER }; struct ObReclaimOpt { ObReclaimOpt() : enable_reclaim_(true), steal_thread_count_(0), reclaim_factor_(30), max_overage_(10), debug_filter_(0) { } ~ObReclaimOpt() { } bool enable_reclaim_; int64_t steal_thread_count_; // when steal memory from neighbor threads, max traverse thread cache count int64_t reclaim_factor_; // percentage, * 100 int64_t max_overage_; // Debug filter bit mask: // bit 0: reclaim in thread_cache_free, // reclaim memory from all caches in the same thread // bit 1: reclaim in thread_cache_alloc, print // current object thread cache reclaim info int64_t debug_filter_; }; struct ObChunkInfo { ObChunkInfo() { memset(this, 0, sizeof(ObChunkInfo)); } ~ObChunkInfo() { } pthread_t tid_; int64_t type_size_; int64_t obj_count_; int64_t allocated_; int64_t length_; // inner free list will only be // accessed by creator-thread void *inner_free_list_; void *head_; ObThreadCache *thread_cache_; LINK(ObChunkInfo, link_); #ifdef DOUBLE_FREE_CHECK // magic code for each item, // it's used to check double-free issue. uint8_t item_magic_[0]; #endif }; struct ObThreadCache { ObThreadCache() { memset(this, 0, sizeof(ObThreadCache)); } ~ObThreadCache() { } // the address must be aligned with 16 bytes if it uses cas 128 // // outer free list will be accessed by: // - creator-thread, as a producer-thread // - consumer-thread // - neighbor-thread ObAtomicList outer_free_list_; ObObjFreeList *f_; // using for memory reclaim algorithm double nr_average_; int64_t nr_total_; int64_t nr_free_; int64_t nr_overage_; int64_t nr_malloc_; // represent the status(state) of allocator: Malloc-ing(0) or Free-ing(1), // I use it as an simple state machine - calculating the minimum of free // memory only when the status change from Malloc-ing to Free-ing. volatile int64_t status_; int64_t nr_free_chunks_; DLL free_chunk_list_; ObThreadCache *prev_; ObThreadCache *next_; }; class ObObjFreeList { friend class ObObjFreeListList; friend struct ObThreadCache; public: ObObjFreeList(); ~ObObjFreeList() { (void)mutex_destroy(&lock_); } // alignment must be a power of 2 int init(const char *name, const int64_t type_size, const int64_t obj_count, const int64_t alignment = 16, const ObMemCacheType cache_type = OP_RECLAIM); void *alloc(); void free(void *item); ObReclaimOpt &get_reclaim_opt() { return reclaim_opt_; } const char *get_name() const { return name_; } int64_t get_allocated() const { return allocated_; } int64_t get_allocated_base() const { return allocated_base_; } int64_t get_type_size() const { return type_size_; } int64_t get_used() const { return used_; } int64_t get_used_base() const { return used_base_; } int64_t get_chunk_count() const { return chunk_count_; } int64_t get_chunk_byte_size() const { return chunk_byte_size_; } int64_t get_base_obj_size() const { return type_size_base_; } private: void show_info(const char *file, const int line, const char *tag, ObThreadCache *thread_cache); ObChunkInfo *&get_chunk_info(void *item); #ifdef DOUBLE_FREE_CHECK int64_t get_chunk_item_magic_idx(void *item, ObChunkInfo **chunk_info, const bool do_check = false); #endif void set_chunk_item_magic(ObChunkInfo *chunk_info, void *item); void clear_chunk_item_magic(ObChunkInfo *chunk_info, void *item); ObChunkInfo *chunk_create(ObThreadCache *thread_cache); void chunk_delete(ObThreadCache *thread_cache, ObChunkInfo *chunk_info); void *malloc_whole_chunk(ObThreadCache *thread_cache, ObChunkInfo *chunk_info); void *malloc_from_chunk(ObThreadCache *thread_cache, ObChunkInfo *chunk_info); void free_to_chunk(ObThreadCache *thread_cache, void *item); void *malloc_from_cache(ObThreadCache *thread_cache, const int64_t nr); void free_to_cache(ObThreadCache *thread_cache, void *item, const int64_t nr); void refresh_average_info(ObThreadCache *thread_cache); bool need_to_reclaim(ObThreadCache *thread_cache); void *global_alloc(); void global_free(void *item); ObThreadCache *init_thread_cache(ObChunkInfo *&chunk_info); void update_used(); private: ObAtomicList obj_free_list_; SLINK(ObObjFreeList, link_); ObAtomicSLL *freelists_; // global free list list ObFixedMemAllocator *tc_allocator_; ObReclaimOpt reclaim_opt_; bool is_inited_; bool only_global_; int64_t mod_id_; int64_t thread_cache_idx_; const char *name_; // actual size of each object, each object in one chunk, each chunk includes // an extra void* to store instance of ObChunkInfo. int64_t type_size_; // user specified size of object int64_t type_size_base_; int64_t alignment_; // user specified number of elements int64_t obj_count_base_; // number of elements of each chunk int64_t obj_count_per_chunk_; // byte size of one block allocated from ob_malloc() int64_t chunk_byte_size_; // memory block count, blocks are allocated from ob_malloc() int64_t chunk_count_; int64_t used_; int64_t allocated_; int64_t allocated_base_; int64_t used_base_; // number of thread cache in one object freelist int64_t nr_thread_cache_; ObThreadCache *thread_cache_; ObMutex lock_; DISALLOW_COPY_AND_ASSIGN(ObObjFreeList); }; // Allocator for fixed size memory blocks. class ObFixedMemAllocator { public: ObFixedMemAllocator() { fl_ = NULL; } virtual ~ObFixedMemAllocator() { } // Creates a new global allocator. // @param name identification tag used for mem tracking . // @param obj_size size of memory blocks to be allocated. // @param obj_count number of units to be allocated if free pool is // empty. // @param alignment of objects must be a power of 2. // initialize the parameters of the allocator. int init(ObObjFreeListList &fll, const char *name, const int64_t obj_size, const int64_t obj_count, const int64_t alignment, const ObMemCacheType cache_type = OP_RECLAIM, const bool is_meta = false); int init(const char *name, const int64_t obj_size, const int64_t obj_count, const int64_t alignment, const ObMemCacheType cache_type = OP_RECLAIM, const bool is_meta = false); // Allocate a block of memory (size specified during construction // of Allocator. virtual void *alloc_void() { void *ret = NULL; if (OB_LIKELY(NULL != fl_)) { ret = fl_->alloc(); } return ret; } // Deallocate a block of memory allocated by the Allocator. virtual void free_void(void *ptr) { if (OB_LIKELY(NULL != fl_) && OB_LIKELY(NULL != ptr)) { fl_->free(ptr); ptr = NULL; } } void set_reclaim_opt(const ObReclaimOptType opt_type, const int64_t value) { if (OB_LIKELY(NULL != fl_)) { switch (opt_type) { case ENABLE_RECLAIM: fl_->get_reclaim_opt().enable_reclaim_ = (0 != value); break; case STEAL_THREAD_COUNT: fl_->get_reclaim_opt().steal_thread_count_ = (value > 0) ? value : 0; break; case RECLAIM_FACTOR: fl_->get_reclaim_opt().reclaim_factor_ = (value >= 0 && value <= 100) ? value : 30; break; case MAX_OVERAGE: fl_->get_reclaim_opt().max_overage_ = (value >= 0) ? value : 10; break; case DEBUG_FILTER: fl_->get_reclaim_opt().debug_filter_ = (value >= 0 && value <= 3) ? value : 0; break; default: break; } } } int64_t get_obj_size() { return ((NULL == fl_) ? 0 : fl_->get_base_obj_size()); } protected: ObObjFreeList *fl_; DISALLOW_COPY_AND_ASSIGN(ObFixedMemAllocator); }; #if defined(GCC_52) template struct __is_default_constructible__; template <> struct __is_default_constructible__ { protected: // Put base typedefs here to avoid pollution struct twoc { char a, b; }; template struct test { typedef char type; }; public: static bool const value = false; }; template <> struct __is_default_constructible__<>::test { typedef twoc type; }; template struct __is_default_constructible__ : __is_default_constructible__<> { private: template static typename test::type sfinae(U*); template static char sfinae(...); public: static bool const value = sizeof(sfinae(0)) > 1; }; #else template struct __is_default_constructible__ { typedef char yes[1]; typedef char no [2]; template static yes &chk(__typeof__(&Type())); template static no &chk(...); static bool const value = sizeof(chk(0)) == sizeof(yes); }; #endif template struct ObClassConstructor { template struct Constructor { Type *operator ()(void *ptr) { return new (ptr) Type(); } }; template struct Constructor { Type *operator ()(void *ptr) { return reinterpret_cast(ptr); } }; T *operator ()(void *ptr) { Constructor::value> construct; return construct(ptr); } }; #define RND16(x) (((x) + 15) & ~15) // Allocator for Class objects. It uses a prototype object to do // fast initialization. Prototype of the template class is created // when the fast allocator is created. This is instantiated with // default (no argument) constructor. Constructor is not called for // the allocated objects. Instead, the prototype is just memory // copied onto the new objects. This is done for performance reasons. template class ObClassAllocator : public ObFixedMemAllocator { public: ObClassAllocator() { fl_ = NULL; } virtual ~ObClassAllocator() { } // Create a new global class specific ClassAllocator. // @param name some identifying name, used for mem tracking purposes. // @param obj_count number of units to be allocated if free pool is // empty. // @param alignment of objects must be a power of 2. // @param is_meta is allocator for meta static ObClassAllocator *get(const int64_t obj_count = 64, const ObMemCacheType cache_type = OP_RECLAIM, const int64_t alignment = 16, const bool is_meta = false) { ObClassAllocator *instance = NULL; while (OB_UNLIKELY(once_ < 2)) { if (ATOMIC_BCAS(&once_, 0, 1)) { instance = new (std::nothrow) ObClassAllocator(); if (OB_LIKELY(NULL != instance)) { if (common::OB_SUCCESS != instance->init(typeid(T).name(), RND16(sizeof(T)), obj_count, RND16(alignment), cache_type, is_meta)) { _OB_LOG(ERROR, "failed to init class allocator %s", typeid(T).name()); delete instance; instance = NULL; ATOMIC_BCAS(&once_, 1, 0); } else { if (instance_ != NULL) { delete instance_; } instance_ = instance; (void)ATOMIC_BCAS(&once_, 1, 2); } } else { (void)ATOMIC_BCAS(&once_, 1, 0); } } } return (ObClassAllocator *)instance_; } // Allocates objects of the templated type. virtual T *alloc() { T *ret = NULL; void *ptr = NULL; if (OB_LIKELY(NULL != fl_) && OB_LIKELY(NULL != (ptr = fl_->alloc()))) { ObClassConstructor construct; ret = construct(ptr); } return ret; } // Deallocates objects of the templated type. // @param ptr pointer to be freed. virtual void free(T *ptr) { if (OB_LIKELY(NULL != fl_) && OB_LIKELY(NULL != ptr)) { ptr->~T(); fl_->free(ptr); ptr = NULL; } } // Allocate objects of the templated type via the inherited interface // using void pointers. virtual void *alloc_void() { return reinterpret_cast(alloc()); } // Deallocate objects of the templated type via the inherited // interface using void pointers. // @param ptr pointer to be freed. virtual void free_void(void *ptr) { free(reinterpret_cast(ptr)); } protected: static volatile int64_t once_; // for creating singleton instance static volatile ObClassAllocator *instance_; private: DISALLOW_COPY_AND_ASSIGN(ObClassAllocator); }; // Allocator for meta of ObObjFreeList, ObChunkInfo, ObThreadCache. The other // objects need created object pools with these three objects. But these three // objects are also allocated form their object pools. The object pool is // implied with these three objects. So the object pools of these three objects // need an especial allocator to allocate themself. template class ObMetaAllocator : public ObFixedMemAllocator { public: // Create a new class specific ClassAllocator. // @param mod allocate mode. explicit ObMetaAllocator(const int64_t mod) { free_list_ = NULL; allocator_.set_mod_id(mod); (void)mutex_init(&allocator_lock_); } virtual ~ObMetaAllocator() { (void)mutex_destroy(&allocator_lock_); } // Allocates objects of the templated type. T *alloc() { int ret = OB_SUCCESS; T *obj = NULL; void *ptr = NULL; if (OB_SUCC(mutex_acquire(&allocator_lock_))) { if (OB_LIKELY(NULL != free_list_)) { ptr = free_list_; free_list_ = *(reinterpret_cast(ptr)); } else { ptr = allocator_.alloc_aligned(sizeof(T), 16); } mutex_release(&allocator_lock_); } if (OB_LIKELY(NULL != ptr)) { obj = new (ptr) T(); } return obj; } // Deallocates objects of the templated type. // @param ptr pointer to be freed. void free(T *ptr) { int ret = OB_SUCCESS; if (OB_LIKELY(NULL != ptr)) { ptr->~T(); if (OB_SUCC(mutex_acquire(&allocator_lock_))) { *(reinterpret_cast(ptr)) = free_list_; free_list_ = ptr; mutex_release(&allocator_lock_); } } } // Allocate objects of the templated type via the inherited interface // using void pointers. virtual void *alloc_void() { return reinterpret_cast(alloc()); } // Deallocate objects of the templated type via the inherited // interface using void pointers. // @param ptr pointer to be freed. virtual void free_void(void *ptr) { free(reinterpret_cast(ptr)); } private: void *free_list_; PageArena allocator_; ObMutex allocator_lock_; DISALLOW_COPY_AND_ASSIGN(ObMetaAllocator); }; // Allocator for space class, a class with a lot of uninitialized // space/members. It uses an instantiate fucntion do initialization // of objects. This is particulary useful if most of the space in // the objects does not need to be intialized. The inifunction passed // can be used to intialize a few fields selectively. Using // ObClassAllocator for space objects would unnecessarily initialized // all of the members. template class ObSparseClassAllocator : public ObClassAllocator { public: ObSparseClassAllocator() : instantiate_(NULL), proto_() { } virtual ~ObSparseClassAllocator() { } // Create a new class specific ObSparseClassAllocator. // @param name some identifying name, used for mem tracking purposes. // @param obj_count number of units to be allocated if free pool is empty. // @param alignment of objects must be a power of 2. // @param instantiate_func static ObSparseClassAllocator *get(void (*instantiate_func) (T &proto, T &instance) = NULL, const int64_t obj_count = 64) { ObSparseClassAllocator *instance = NULL; while (OB_UNLIKELY(once_ < 2)) { if (ATOMIC_BCAS(&once_, 0, 1)) { instance = new (std::nothrow) ObSparseClassAllocator(); if (OB_LIKELY(NULL != instance)) { instance->instantiate_ = instantiate_func; if (common::OB_SUCCESS != instance->init(typeid(T).name(), RND16(sizeof(T)), obj_count, 16, OP_RECLAIM, false)) { _OB_LOG(ERROR, "failed to init class sparse allocator %s", typeid(T).name()); delete instance; instance = NULL; ATOMIC_BCAS(&once_, 1, 0); } else { instance_ = instance; (void)ATOMIC_BCAS(&once_, 1, 2); } } else { (void)ATOMIC_BCAS(&once_, 1, 0); } } } return (ObSparseClassAllocator *)instance_; } // Allocates objects of the templated type. virtual T *alloc() { T *ret = NULL; void *ptr = NULL; if (OB_LIKELY(NULL != this->fl_) && OB_LIKELY(NULL != (ptr = this->fl_->alloc()))) { if (OB_UNLIKELY(NULL == instantiate_)) { ret = new (ptr) T(); } else { (*instantiate_) (proto_.type_object_, *reinterpret_cast(ptr)); ret = reinterpret_cast(ptr); } } return ret; } void (*instantiate_) (T &proto, T &instance); struct { T type_object_; int64_t space_holder_; } proto_; private: static volatile int64_t once_; // for creating singleton instance static volatile ObSparseClassAllocator *instance_; DISALLOW_COPY_AND_ASSIGN(ObSparseClassAllocator); }; class ObObjFreeListList { friend class ObObjFreeList; public: static const int64_t MAX_NUM_FREELIST; ~ObObjFreeListList() { } int create_freelist(ObObjFreeList *&free_list, const char *name, const int64_t obj_size, const int64_t obj_count, const int64_t alignment = 16, const ObMemCacheType cache_type = OP_RECLAIM, const bool is_meta = false); static ObObjFreeListList &get_freelists() { static ObObjFreeListList g_freelists; g_freelists.once_init(); return g_freelists; } int64_t get_next_free_slot() { return ATOMIC_FAA(&nr_freelists_, 1); } int get_info(ObVector &flls); void dump(); void dump_baselinerel(); void snap_baseline(); int64_t to_string(char *buf, const int64_t len) const; private: void once_init() { int ret = OB_SUCCESS; while (OB_UNLIKELY(once_ < 2) && OB_SUCC(ret)) { if (ATOMIC_BCAS(&once_, 0, 1)) { if (OB_FAIL(fl_allocator_.init(*this, "ObjFreeList", sizeof(ObObjFreeList), 64, 16, OP_GLOBAL, true))) { OB_LOG(WARN, "failed to init ObjFreeList allocator", K(ret)); ATOMIC_BCAS(&once_, 1, 0); } else if (OB_FAIL(tc_allocator_.init(*this, "ObThreadCache", sizeof(ObThreadCache), 64, 16, OP_RECLAIM, true))) { OB_LOG(WARN, "failed to init ObThreadCache allocator", K(ret)); ATOMIC_BCAS(&once_, 1, 0); } else { is_inited_ = true; (void)ATOMIC_BCAS(&once_, 1, 2); } } } } private: static volatile int64_t once_; // for creating singleton instance ObObjFreeListList(); ObAtomicSLL freelists_; // global free list list volatile int64_t nr_freelists_; volatile int64_t mem_total_; bool is_inited_; ObMetaAllocator tc_meta_allocator_; ObClassAllocator fl_allocator_; ObClassAllocator tc_allocator_; DISALLOW_COPY_AND_ASSIGN(ObObjFreeListList); }; inline int ObFixedMemAllocator::init( ObObjFreeListList &fll, const char *name, const int64_t obj_size, const int64_t obj_count, const int64_t alignment, const ObMemCacheType cache_type /* = OP_RECLAIM */, const bool is_meta /* = false */) { int ret = common::OB_SUCCESS; if (OB_FAIL(fll.create_freelist(fl_, name, obj_size, obj_count, alignment, cache_type, is_meta))) { // do nothing } else if (OB_ISNULL(fl_)) { ret = OB_ERR_UNEXPECTED; } return ret; } inline int ObFixedMemAllocator::init( const char *name, const int64_t obj_size, const int64_t obj_count, const int64_t alignment, const ObMemCacheType cache_type /* = OP_RECLAIM */, const bool is_meta /* = false */) { return init(ObObjFreeListList::get_freelists(), name, obj_size, obj_count, alignment, cache_type, is_meta); } struct ObThreadFreeList { ObThreadFreeList() : reserved_size_(64), allocated_(0), freelist_(NULL) { } ~ObThreadFreeList() { } int64_t reserved_size_; int64_t allocated_; void *freelist_; }; template volatile int64_t ObClassAllocator::once_ = 0; template volatile ObClassAllocator *ObClassAllocator::instance_ = NULL; template volatile int64_t ObSparseClassAllocator::once_ = 0; template volatile ObSparseClassAllocator *ObSparseClassAllocator::instance_ = NULL; template struct OPNum { template struct GetOPLocalNum { static const int64_t v = Type::OP_LOCAL_NUM; }; template struct GetOPLocalNum { static const int64_t v = 64; }; static const int64_t LOCAL_NUM = GetOPLocalNum::v; }; // object pool user interface // Warning: 1. the cached memory can't be freed even through the thread is dead. // 2. double free is very terriable. // 3. because object pool uses singleton, please only use one of global, // tc or reclaim interfaces for each object type in the whole procject. // global pool allocator interface #define op_alloc_args(type, args...) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM, common::OP_GLOBAL); \ if (OB_LIKELY(NULL != instance)) { \ type *tmp = instance->alloc(); \ if (OB_LIKELY(NULL != tmp)) { \ ret = new (tmp) type(args); \ } \ } \ ret; \ }) #define op_alloc(type) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM, common::OP_GLOBAL); \ if (OB_LIKELY(NULL != instance)) { \ ret = instance->alloc(); \ } \ ret; \ }) #define op_free(ptr) \ ({ \ common::ObClassAllocator<__typeof__(*ptr)> *instance = common::ObClassAllocator<__typeof__(*ptr)>::get(common::OPNum<__typeof__(*ptr)>::LOCAL_NUM, common::OP_GLOBAL); \ if (OB_LIKELY(NULL != instance)) { \ instance->free(ptr); \ } \ }) // thread cache pool allocator interface #define op_tc_alloc_args(type, args...) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM, common::OP_TC); \ if (OB_LIKELY(NULL != instance)) { \ type *tmp = instance->alloc(); \ if (OB_LIKELY(NULL != tmp)) { \ ret = new (tmp) type(args); \ } \ } \ ret; \ }) #define op_tc_alloc(type) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM, common::OP_TC); \ if (OB_LIKELY(NULL != instance)) { \ ret = instance->alloc(); \ } \ ret; \ }) #define op_tc_free(ptr) \ ({ \ common::ObClassAllocator<__typeof__(*ptr)> *instance = common::ObClassAllocator<__typeof__(*ptr)>::get(common::OPNum<__typeof__(*ptr)>::LOCAL_NUM, common::OP_TC); \ if (OB_LIKELY(NULL != instance)) { \ instance->free(ptr); \ } \ }) // thread cache pool and reclaim allocator interface #define op_reclaim_alloc_args(type, args...) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ type *tmp = instance->alloc(); \ if (OB_LIKELY(NULL != tmp)) { \ ret = new (tmp) type(args); \ } \ } \ ret; \ }) #define op_reclaim_alloc(type) \ ({ \ type *ret = NULL; \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ ret = instance->alloc(); \ } \ ret; \ }) #define op_reclaim_free(ptr) \ ({ \ common::ObClassAllocator<__typeof__(*ptr)> *instance = common::ObClassAllocator<__typeof__(*ptr)>::get(common::OPNum<__typeof__(*ptr)>::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ instance->free(ptr); \ } \ }) #define op_reclaim_opt(type, reclaim_type, value) \ ({ \ common::ObClassAllocator *instance = common::ObClassAllocator::get(common::OPNum::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ instance->set_reclaim_opt(reclaim_type, value); \ } \ }) // thread cache pool and reclaim sparse allocator interface #define op_reclaim_sparse_alloc(type, init_func) \ ({ \ type *ret = NULL; \ common::ObSparseClassAllocator *instance = common::ObSparseClassAllocator::get(init_func, common::OPNum::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ ret = instance->alloc(); \ } \ ret; \ }) #define op_reclaim_sparse_free(ptr) \ ({ \ common::ObSparseClassAllocator<__typeof__(*ptr)> *instance = common::ObSparseClassAllocator<__typeof__(*ptr)>::get(NULL, common::OPNum<__typeof__(*ptr)>::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ instance->free(ptr); \ } \ }) #define op_reclaim_sparse_opt(type, init_func, reclaim_type, value) \ ({ \ common::ObSparseClassAllocator *instance = common::ObSparseClassAllocator::get(init_func, common::OPNum::LOCAL_NUM); \ if (OB_LIKELY(NULL != instance)) { \ instance->set_reclaim_opt(reclaim_type, value); \ } \ }) } // end of namespace common } // end of namespace oceanbase #endif // OB_LIB_CONCURRENCY_OBJPOOL_H_