291 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) 2021 OceanBase
 | 
						|
 * OceanBase CE 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <gtest/gtest.h>
 | 
						|
#include "lib/oblog/ob_log.h"
 | 
						|
#include "lib/allocator/ob_memfrag_recycle_allocator.h"
 | 
						|
#include "lib/random/ob_random.h"
 | 
						|
 | 
						|
using namespace oceanbase::common;
 | 
						|
 | 
						|
namespace oceanbase {
 | 
						|
namespace common {
 | 
						|
 | 
						|
class TestObMetaImage : public ObIRewritable, public lib::ThreadPool {
 | 
						|
public:
 | 
						|
  TestObMetaImage();
 | 
						|
  virtual ~TestObMetaImage();
 | 
						|
  int init(const int64_t expire_duration);
 | 
						|
  void destroy();
 | 
						|
  virtual int rewrite_switch();
 | 
						|
  void run1() final;
 | 
						|
 | 
						|
private:
 | 
						|
  static const int64_t MAX_META_COUNT = 1024L * 1024L * 2L;
 | 
						|
  struct TestObMeta {
 | 
						|
    int64_t size_;
 | 
						|
    char data_[0];
 | 
						|
  };
 | 
						|
  int do_work();
 | 
						|
  inline void* alloc(const int64_t size)
 | 
						|
  {
 | 
						|
    return allocator_.alloc(size);
 | 
						|
  }
 | 
						|
  inline void free(void* ptr)
 | 
						|
  {
 | 
						|
    allocator_.free(ptr);
 | 
						|
  }
 | 
						|
  inline int need_rewrite(void* ptr, bool& is_need)
 | 
						|
  {
 | 
						|
    return allocator_.need_rewrite(ptr, is_need);
 | 
						|
  }
 | 
						|
  ObMemfragRecycleAllocator allocator_;
 | 
						|
  TestObMeta** meta_array_;
 | 
						|
};
 | 
						|
 | 
						|
TestObMetaImage::TestObMetaImage()
 | 
						|
{
 | 
						|
  meta_array_ = (TestObMeta**)ob_malloc(sizeof(TestObMeta*) * MAX_META_COUNT);
 | 
						|
  memset(meta_array_, 0, sizeof(TestObMeta*) * MAX_META_COUNT);
 | 
						|
}
 | 
						|
 | 
						|
TestObMetaImage::~TestObMetaImage()
 | 
						|
{
 | 
						|
  ob_free(meta_array_);
 | 
						|
}
 | 
						|
 | 
						|
int TestObMetaImage::init(const int64_t expire_duration)
 | 
						|
{
 | 
						|
  return allocator_.init(ObModIds::OB_MACRO_BLOCK_META, this, expire_duration);
 | 
						|
}
 | 
						|
 | 
						|
void TestObMetaImage::destroy()
 | 
						|
{
 | 
						|
  for (int64_t i = 0; i < MAX_META_COUNT; i++) {
 | 
						|
    if (NULL != meta_array_[i]) {
 | 
						|
      free(meta_array_[i]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  allocator_.destroy();
 | 
						|
}
 | 
						|
 | 
						|
void TestObMetaImage::run1()
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (OB_SUCCESS != (ret = do_work())) {
 | 
						|
    LIB_ALLOC_LOG(WARN, "do work failed, ", "ret", ret);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int TestObMetaImage::do_work()
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int64_t loc = 0;
 | 
						|
  int64_t size = 0;
 | 
						|
  TestObMeta* old_meta = NULL;
 | 
						|
  TestObMeta* new_meta = NULL;
 | 
						|
 | 
						|
  while (!has_set_stop()) {
 | 
						|
    loc = ObRandom::rand(0, MAX_META_COUNT - 1);
 | 
						|
    size = ObRandom::rand(128, 1000);
 | 
						|
    new_meta = (TestObMeta*)alloc(size);
 | 
						|
    if (NULL == new_meta) {
 | 
						|
      ret = OB_ALLOCATE_MEMORY_FAILED;
 | 
						|
      break;
 | 
						|
    } else {
 | 
						|
      new_meta->size_ = size;
 | 
						|
    }
 | 
						|
 | 
						|
    old_meta = (TestObMeta*)ATOMIC_TAS(reinterpret_cast<volatile uint64_t*>(&meta_array_[loc]), (uint64_t)new_meta);
 | 
						|
    if (NULL != old_meta) {
 | 
						|
      free(old_meta);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int TestObMetaImage::rewrite_switch()
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  TestObMeta* old_meta = NULL;
 | 
						|
  TestObMeta* new_meta = NULL;
 | 
						|
  TestObMeta* tmp_meta = NULL;
 | 
						|
  bool is_need = false;
 | 
						|
 | 
						|
  for (int64_t i = 0; OB_SUCC(ret) && i < MAX_META_COUNT; i++) {
 | 
						|
    old_meta = meta_array_[i];
 | 
						|
    if (OB_SUCCESS != (ret = need_rewrite(old_meta, is_need))) {
 | 
						|
      LIB_ALLOC_LOG(WARN, "fail to decide if need rewrite, ", "ret", ret);
 | 
						|
    } else if (is_need) {
 | 
						|
      new_meta = (TestObMeta*)alloc(old_meta->size_);
 | 
						|
      if (NULL == new_meta) {
 | 
						|
        ret = OB_ALLOCATE_MEMORY_FAILED;
 | 
						|
        break;
 | 
						|
      } else {
 | 
						|
        new_meta->size_ = old_meta->size_;
 | 
						|
 | 
						|
        tmp_meta = (TestObMeta*)ATOMIC_VCAS(
 | 
						|
            reinterpret_cast<volatile uint64_t*>(&meta_array_[i]), (uint64_t)old_meta, (uint64_t)new_meta);
 | 
						|
        if (old_meta == tmp_meta) {
 | 
						|
          free(old_meta);
 | 
						|
        } else {
 | 
						|
          // retry
 | 
						|
          free(new_meta);
 | 
						|
          i--;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
TEST(ObMRAllocatorRecycler, test_recycle)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ObMemfragRecycleAllocator allocator[100];
 | 
						|
 | 
						|
  // test null argument
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().register_allocator(NULL);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test normal register
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().register_allocator(&allocator[0]);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test repeatly register
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().register_allocator(&allocator[0]);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test normal deregister
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().deregister_allocator(&allocator[0]);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test repeatly deregister
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().deregister_allocator(&allocator[0]);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test invalid_deregister
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().deregister_allocator(NULL);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test invalid_deregister
 | 
						|
  ret = ObMRAllocatorRecycler::get_instance().deregister_allocator(&allocator[1]);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test register limit count
 | 
						|
  ret = OB_SUCCESS;
 | 
						|
  for (int64_t i = 0; OB_SUCC(ret) && i < 100; i++) {
 | 
						|
    ret = ObMRAllocatorRecycler::get_instance().register_allocator(&allocator[i]);
 | 
						|
  }
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  ret = OB_SUCCESS;
 | 
						|
  for (int64_t i = 0; OB_SUCC(ret) && i < 100; i++) {
 | 
						|
    ret = ObMRAllocatorRecycler::get_instance().deregister_allocator(&allocator[i]);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
TEST(ObMemfragRecycleAllocator, test_allocator)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ObMemfragRecycleAllocator allocator;
 | 
						|
  TestObMetaImage image1;
 | 
						|
  TestObMetaImage image2;
 | 
						|
  void* data = NULL;
 | 
						|
 | 
						|
  // test invalid argument
 | 
						|
  ret = allocator.init(-1, NULL, -1);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test invalid alloc, free ...
 | 
						|
  data = allocator.alloc(100);
 | 
						|
  EXPECT_TRUE(NULL == data);
 | 
						|
  allocator.free(data);
 | 
						|
 | 
						|
  // test invalid switch
 | 
						|
  ret = allocator.switch_state();
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test thread unsafe init
 | 
						|
  ret = allocator.init(ObModIds::OB_MACRO_BLOCK_META, NULL, 0);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
  // test invalid switch
 | 
						|
  ret = allocator.switch_state();
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
  // test destroy
 | 
						|
  allocator.destroy();
 | 
						|
 | 
						|
  // test normal init
 | 
						|
  ret = allocator.init(ObModIds::OB_MACRO_BLOCK_META, &image1, 0);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test repeatly init
 | 
						|
  ret = allocator.init(ObModIds::OB_MACRO_BLOCK_META, &image2, 0);
 | 
						|
  EXPECT_NE(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  // test normal alloc
 | 
						|
  data = allocator.alloc(100);
 | 
						|
  EXPECT_TRUE(data != NULL);
 | 
						|
 | 
						|
  // test normal free
 | 
						|
  allocator.free(data);
 | 
						|
 | 
						|
  // test invalid free
 | 
						|
  allocator.free(&image1);
 | 
						|
 | 
						|
  // test invalid need_rewrite
 | 
						|
  bool is_need = false;
 | 
						|
  ret = allocator.need_rewrite(NULL, is_need);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
  EXPECT_TRUE(!is_need);
 | 
						|
 | 
						|
  // test normal need_rewrite
 | 
						|
  data = allocator.alloc(100);
 | 
						|
  ret = allocator.need_rewrite(data, is_need);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
  EXPECT_TRUE(!is_need);
 | 
						|
 | 
						|
  allocator.destroy();
 | 
						|
}
 | 
						|
 | 
						|
TEST(ObMetaImage, test_image)
 | 
						|
{
 | 
						|
  const int64_t THREAD_COUNT = 9;
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  TestObMetaImage image;
 | 
						|
 | 
						|
  ret = image.init(1000 * 1000);
 | 
						|
  EXPECT_EQ(OB_SUCCESS, ret);
 | 
						|
 | 
						|
  ObMRAllocatorRecycler::get_instance().set_schedule_interval_us(1000 * 1000);
 | 
						|
 | 
						|
  image.set_thread_count(THREAD_COUNT);
 | 
						|
  image.start();
 | 
						|
  sleep(60);
 | 
						|
  image.stop();
 | 
						|
  image.wait();
 | 
						|
 | 
						|
  image.destroy();
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace common
 | 
						|
}  // namespace oceanbase
 | 
						|
 | 
						|
int main(int argc, char** argv)
 | 
						|
{
 | 
						|
  oceanbase::common::ObLogger::get_logger().set_log_level("DEBUG");
 | 
						|
  testing::InitGoogleTest(&argc, argv);
 | 
						|
  return RUN_ALL_TESTS();
 | 
						|
}
 |