diff --git a/src/redis.cpp b/src/redis.cpp index 5277b2e..ad61573 100644 --- a/src/redis.cpp +++ b/src/redis.cpp @@ -45,22 +45,34 @@ void module::redis_pool::close() std::shared_ptr module::redis_pool::get() { - if (m_closed) - { - throw ylib::exception("connection pool is closed"); - } redisContext* ctx = nullptr; - if (m_queue.pop(ctx)) { + std::unique_lock uni(m_mutex); + if (m_closed) + { + throw ylib::exception("connection pool is closed"); + } + if (m_queue.pop(ctx)) + { + m_pop_size++; + return std::make_shared(ctx, this); + } + if (m_pop_size >= m_max_size) + throw ylib::exception("connection pool releases more connections than the maximum number"); + // Reserve a slot before unlocking for the blocking connect. m_pop_size++; - return std::make_shared(ctx, this); } - if (m_pop_size >= m_max_size) - throw ylib::exception("connection pool releases more connections than the maximum number"); - ctx = reget(nullptr); - - m_pop_size++; + try + { + ctx = reget(nullptr); + } + catch (...) + { + std::unique_lock uni(m_mutex); + m_pop_size--; + throw; + } return std::make_shared(ctx, this); } @@ -170,13 +182,15 @@ void module::redis_pool::recover(redisContext* ctx) { if (ctx == nullptr) return; + std::unique_lock uni(m_mutex); if (m_closed) { redisFree(ctx); return; } m_queue.push(ctx); - m_pop_size--; + if (m_pop_size > 0) + m_pop_size--; } module::redis::redis(redisContext* context, redis_pool* pool):m_context(context),m_pool(pool) @@ -190,7 +204,14 @@ module::redis::~redis() } void module::redis::close() { - m_pool->recover(m_context); + // Guard against double-return: Lua close() + shared_ptr destructor + // previously pushed the same redisContext* into the pool twice, which + // leads to concurrent redisCommand use and "double free or corruption". + if (m_context == nullptr || m_pool == nullptr) + return; + redisContext* ctx = m_context; + m_context = nullptr; + m_pool->recover(ctx); } sol::object module::redis::command(const std::string& cmd, sol::this_state ts) {