/** * Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. * * See file LICENSE for terms. */ #include extern "C" { #include } #include #include #include class test_mpool : public ucs::test { protected: static ucs_status_t test_alloc(ucs_mpool_t *mp, size_t *size_p, void **chunk_p) { *chunk_p = malloc(*size_p); return (*chunk_p == NULL) ? UCS_ERR_NO_MEMORY : UCS_OK; } static void test_free(ucs_mpool_t *mp, void *chunk) { free(chunk); } static void obj_str(ucs_mpool_t *mp, void *obj, ucs_string_buffer_t *strb) { ucs_string_buffer_appendf(strb, "test-obj-%p", obj); } static ucs_log_func_rc_t mpool_log_handler(const char *file, unsigned line, const char *function, ucs_log_level_t level, const ucs_log_component_config_t *comp_conf, const char *message, va_list ap) { // Ignore errors that invalid input parameters as it is expected if (level == UCS_LOG_LEVEL_ERROR) { std::string err_str = format_message(message, ap); std::string exp_str = "Invalid memory pool parameter(s)"; if (err_str == exp_str) { UCS_TEST_MESSAGE << err_str; return UCS_LOG_FUNC_RC_STOP; } } return UCS_LOG_FUNC_RC_CONTINUE; } static bool is_leak_str(const std::string &str) { return (str.find("not returned to mpool test") != std::string::npos) && (str.find("{test-obj-") != std::string::npos); } static ucs_log_func_rc_t mpool_log_leak_handler(const char *file, unsigned line, const char *function, ucs_log_level_t level, const ucs_log_component_config_t *comp_conf, const char *message, va_list ap) { if (level == UCS_LOG_LEVEL_WARN) { std::string msg = format_message(message, ap); if (is_leak_str(msg)) { UCS_TEST_MESSAGE << "< " << msg << " >"; ++leak_count; return UCS_LOG_FUNC_RC_STOP; } } return UCS_LOG_FUNC_RC_CONTINUE; } static const size_t header_size = 30; static const size_t data_size = 152; static const size_t align = 128; static size_t leak_count; }; size_t test_mpool::leak_count; UCS_TEST_F(test_mpool, no_allocs) { ucs_mpool_t mp; ucs_status_t status; ucs_mpool_ops_t ops = { ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, NULL }; status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 6, 18, &ops, "test"); ASSERT_UCS_OK(status); ucs_mpool_cleanup(&mp, 1); } UCS_TEST_F(test_mpool, wrong_ops) { ucs_mpool_t mp; ucs_status_t status; ucs_mpool_ops_t ops = { 0 }; scoped_log_handler log_handler(mpool_log_handler); status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 6, 18, &ops, "test"); EXPECT_TRUE(status == UCS_ERR_INVALID_PARAM); } UCS_TEST_F(test_mpool, basic) { ucs_status_t status; ucs_mpool_t mp; ucs_mpool_ops_t ops = { ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, NULL }; push_config(); for (int mpool_fifo = 0; mpool_fifo <= 1; ++mpool_fifo) { #if ENABLE_DEBUG_DATA modify_config("MPOOL_FIFO", ucs::to_string(mpool_fifo).c_str()); #else if (mpool_fifo == 1) { continue; } #endif status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 6, 18, &ops, "test"); ASSERT_UCS_OK(status); for (unsigned loop = 0; loop < 10; ++loop) { std::vector objs; for (unsigned i = 0; i < 18; ++i) { void *ptr = ucs_mpool_get(&mp); ASSERT_TRUE(ptr != NULL); ASSERT_EQ(0ul, ((uintptr_t)ptr + header_size) % align) << ptr; memset(ptr, 0xAA, header_size + data_size); objs.push_back(ptr); } ASSERT_TRUE(NULL == ucs_mpool_get(&mp)); for (std::vector::iterator iter = objs.begin(); iter != objs.end(); ++iter) { ucs_mpool_put(*iter); } } ucs_mpool_cleanup(&mp, 1); } pop_config(); } UCS_TEST_F(test_mpool, custom_alloc) { ucs_status_t status; ucs_mpool_t mp; ucs_mpool_ops_t ops = { test_alloc, test_free, NULL, NULL, NULL }; status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 5, 18, &ops, "test"); ASSERT_UCS_OK(status); void *obj = ucs_mpool_get(&mp); EXPECT_TRUE(obj != NULL); ucs_mpool_put(obj); ucs_mpool_cleanup(&mp, 1); } UCS_TEST_F(test_mpool, grow) { ucs_status_t status; ucs_mpool_t mp; ucs_mpool_ops_t ops = { ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, NULL }; status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 1000, 2000, &ops, "test"); ASSERT_UCS_OK(status); ucs_mpool_grow(&mp, 1); void *obj = ucs_mpool_get(&mp); EXPECT_TRUE(obj != NULL); ucs_mpool_put(obj); ucs_mpool_cleanup(&mp, 1); } UCS_TEST_F(test_mpool, infinite) { const unsigned NUM_ELEMS = 1000000 / ucs::test_time_multiplier(); ucs_status_t status; ucs_mpool_t mp; ucs_mpool_ops_t ops = { ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, NULL }; status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 10000, UINT_MAX, &ops, "test"); ASSERT_UCS_OK(status); std::queue q; for (unsigned i = 0; i < NUM_ELEMS; ++i) { void *obj = ucs_mpool_get(&mp); ASSERT_TRUE(obj != NULL); q.push(obj); } while (!q.empty()) { ucs_mpool_put(q.front()); q.pop(); } ucs_mpool_cleanup(&mp, 1); } UCS_TEST_F(test_mpool, leak_check) { ucs_mpool_t mp; ucs_status_t status; ucs_mpool_ops_t ops = { ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, obj_str }; status = ucs_mpool_init(&mp, 0, header_size + data_size, header_size, align, 6, 18, &ops, "test"); ASSERT_UCS_OK(status); for (int i = 0; i < 5; ++i) { void *obj = ucs_mpool_get(&mp); EXPECT_TRUE(obj != NULL); } // Do not release allocated objects leak_count = 0; scoped_log_handler log_handler(mpool_log_leak_handler); ucs_mpool_cleanup(&mp, 1); EXPECT_EQ(5u, leak_count); } UCS_TEST_SKIP_COND_F(test_mpool, alloc_4g, RUNNING_ON_VALGRIND) { const unsigned elems_per_chunk = 128; const size_t elem_size = 32 * UCS_MBYTE; ucs_mpool_ops_t mpool_ops = {ucs_mpool_chunk_malloc, ucs_mpool_chunk_free, NULL, NULL, NULL}; ucs_mpool_t mp; ucs_status_t status = ucs_mpool_init(&mp, 0, header_size + elem_size, header_size, align, elems_per_chunk, elems_per_chunk, &mpool_ops, "test"); ASSERT_UCS_OK(status); // Allocate objects per one chunk size std::vector objs; for (unsigned i = 0; i < elems_per_chunk; ++i) { void *obj = ucs_mpool_get(&mp); if (obj == NULL) { ADD_FAILURE() << "Could not allocate object [" << i << "] from mpool"; break; } objs.push_back(obj); } // Release allocated objects while (!objs.empty()) { ucs_mpool_put(objs.back()); objs.pop_back(); } ucs_mpool_cleanup(&mp, 1); }