From a67cb6c2387217bb31d512f034374bade5a2c2f6 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 19 Apr 2011 14:30:45 -0700 Subject: [PATCH] Merge in updates for the pool library. --- clients/memstat.c | 16 +- libmemcached/include.am | 6 +- libmemcached/memcached.c | 1 + libmemcached/memcached.h | 1 + libmemcached/util/{ping.c => ping.cc} | 0 libmemcached/util/pool.c | 332 ----------------- libmemcached/util/pool.cc | 392 ++++++++++++++++++++ libmemcached/util/{version.c => version.cc} | 2 +- tests/mem_functions.c | 6 +- 9 files changed, 409 insertions(+), 347 deletions(-) rename libmemcached/util/{ping.c => ping.cc} (100%) delete mode 100644 libmemcached/util/pool.c create mode 100644 libmemcached/util/pool.cc rename libmemcached/util/{version.c => version.cc} (92%) diff --git a/clients/memstat.c b/clients/memstat.c index 765e7ab7..d3518458 100644 --- a/clients/memstat.c +++ b/clients/memstat.c @@ -79,10 +79,6 @@ static memcached_return_t stat_printer(memcached_server_instance_st instance, int main(int argc, char *argv[]) { - memcached_return_t rc; - memcached_st *memc; - memcached_server_st *servers; - options_parse(argc, argv); initialize_sockets(); @@ -100,10 +96,12 @@ int main(int argc, char *argv[]) } } - memc= memcached_create(NULL); + memcached_st *memc= memcached_create(NULL); + + memcached_server_st *servers= memcached_servers_parse(opt_servers); + free(opt_servers); - servers= memcached_servers_parse(opt_servers); - rc= memcached_server_push(memc, servers); + memcached_return_t rc= memcached_server_push(memc, servers); memcached_server_list_free(servers); if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS) @@ -131,11 +129,9 @@ int main(int argc, char *argv[]) rc= memcached_stat_execute(memc, stat_args, stat_printer, NULL); } - free(opt_servers); - memcached_free(memc); - return rc == MEMCACHED_SUCCESS ? 0: -1; + return rc == MEMCACHED_SUCCESS ? EXIT_SUCCESS: EXIT_FAILURE; } static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat) diff --git a/libmemcached/include.am b/libmemcached/include.am index 5a4b8b09..496b7a9b 100644 --- a/libmemcached/include.am +++ b/libmemcached/include.am @@ -151,9 +151,9 @@ lib_LTLIBRARIES+= libmemcached/libmemcachedutil.la endif libmemcached_libmemcachedutil_la_SOURCES= \ - libmemcached/util/ping.c \ - libmemcached/util/pool.c \ - libmemcached/util/version.c + libmemcached/util/ping.cc \ + libmemcached/util/pool.cc \ + libmemcached/util/version.cc libmemcached_libmemcachedutil_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} ${PTHREAD_CFLAGS} libmemcached_libmemcachedutil_la_LIBADD= libmemcached/libmemcached.la libmemcached_libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} ${PTHREAD_LIBS} -version-info ${MEMCACHED_UTIL_LIBRARY_VERSION} diff --git a/libmemcached/memcached.c b/libmemcached/memcached.c index 3c583502..c57b2577 100644 --- a/libmemcached/memcached.c +++ b/libmemcached/memcached.c @@ -123,6 +123,7 @@ static inline bool _memcached_init(memcached_st *self) self->prefix_key= NULL; self->configure.initial_pool_size= 1; self->configure.max_pool_size= 1; + self->configure.version= -1; self->configure.filename= NULL; return true; diff --git a/libmemcached/memcached.h b/libmemcached/memcached.h index 2f1caf2b..83072e9a 100644 --- a/libmemcached/memcached.h +++ b/libmemcached/memcached.h @@ -161,6 +161,7 @@ struct memcached_st { struct { uint32_t initial_pool_size; uint32_t max_pool_size; + int32_t version; // This is used by pool and others to determine if the memcached_st is out of date. struct memcached_array_st *filename; } configure; struct { diff --git a/libmemcached/util/ping.c b/libmemcached/util/ping.cc similarity index 100% rename from libmemcached/util/ping.c rename to libmemcached/util/ping.cc diff --git a/libmemcached/util/pool.c b/libmemcached/util/pool.c deleted file mode 100644 index 948f765b..00000000 --- a/libmemcached/util/pool.c +++ /dev/null @@ -1,332 +0,0 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: connects to a host, and makes sure it is alive. - * - */ - -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "libmemcached/common.h" -#include "libmemcached/memcached_util.h" - -#include -#include - -struct memcached_pool_st -{ - pthread_mutex_t mutex; - pthread_cond_t cond; - memcached_st *master; - memcached_st **mmc; - int firstfree; - uint32_t size; - uint32_t current_size; - bool _owns_master; - char *version; -}; - -static memcached_return_t mutex_enter(pthread_mutex_t *mutex) -{ - int ret; - do - ret= pthread_mutex_lock(mutex); - while (ret == -1 && errno == EINTR); - - return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; -} - -static memcached_return_t mutex_exit(pthread_mutex_t *mutex) -{ - int ret; - do - ret= pthread_mutex_unlock(mutex); - while (ret == -1 && errno == EINTR); - - return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; -} - -/** - * Grow the connection pool by creating a connection structure and clone the - * original memcached handle. - */ -static int grow_pool(memcached_pool_st* pool) -{ - memcached_st *obj= calloc(1, sizeof(*obj)); - - if (obj == NULL) - return -1; - - if (memcached_clone(obj, pool->master) == NULL) - { - free(obj); - return -1; - } - - pool->mmc[++pool->firstfree] = obj; - pool->current_size++; - - return EXIT_SUCCESS; -} - -static inline memcached_pool_st *_pool_create(memcached_st* mmc, uint32_t initial, uint32_t max) -{ - memcached_pool_st* ret= NULL; - - if (! initial || ! max || initial > max) - { - errno= EINVAL; - return NULL; - } - - memcached_pool_st object= { .mutex = PTHREAD_MUTEX_INITIALIZER, - .cond= PTHREAD_COND_INITIALIZER, - .master= mmc, - .mmc= calloc(max, sizeof(memcached_st*)), - .firstfree= -1, - .size= max, - .current_size= 0, - ._owns_master= false}; - - if (object.mmc != NULL) - { - ret= (memcached_pool_st*)calloc(1, sizeof(memcached_pool_st)); - if (ret == NULL) - { - free(object.mmc); - errno= ENOMEM; // Set this for the failed calloc - return NULL; - } - - *ret= object; - - /* - Try to create the initial size of the pool. An allocation failure at - this time is not fatal.. - */ - for (unsigned int ii= 0; ii < initial; ++ii) - { - if (grow_pool(ret) == -1) - break; - } - } - - return ret; -} - -memcached_pool_st *memcached_pool_create(memcached_st* mmc, uint32_t initial, uint32_t max) -{ - return _pool_create(mmc, initial, max); -} - -memcached_pool_st * memcached_pool(const char *option_string, size_t option_string_length) -{ - memcached_pool_st *self; - memcached_st *memc= memcached(option_string, option_string_length); - - if (! memc) - return NULL; - - self= memcached_pool_create(memc, memc->configure.initial_pool_size, memc->configure.max_pool_size); - if (! self) - { - memcached_free(memc); - errno= ENOMEM; - return NULL; - } - errno= 0; - - self->_owns_master= true; - - return self; -} - -memcached_st* memcached_pool_destroy(memcached_pool_st* pool) -{ - if (! pool) - return NULL; - - memcached_st *ret= pool->master; - - for (int xx= 0; xx <= pool->firstfree; ++xx) - { - memcached_free(pool->mmc[xx]); - free(pool->mmc[xx]); - pool->mmc[xx] = NULL; - } - - pthread_mutex_destroy(&pool->mutex); - pthread_cond_destroy(&pool->cond); - free(pool->mmc); - if (pool->_owns_master) - { - memcached_free(pool->master); - ret= NULL; - } - free(pool); - - return ret; -} - -memcached_st* memcached_pool_pop(memcached_pool_st* pool, - bool block, - memcached_return_t *rc) -{ - if (! pool || ! rc) - { - errno= EINVAL; - return NULL; - } - - memcached_st *ret= NULL; - if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS) - { - return NULL; - } - - do - { - if (pool->firstfree > -1) - { - ret= pool->mmc[pool->firstfree--]; - } - else if (pool->current_size == pool->size) - { - if (!block) - { - *rc= mutex_exit(&pool->mutex); - return NULL; - } - - if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1) - { - int err= errno; - mutex_exit(&pool->mutex); - errno= err; - *rc= MEMCACHED_ERRNO; - return NULL; - } - } - else if (grow_pool(pool) == -1) - { - *rc= mutex_exit(&pool->mutex); - return NULL; - } - } - while (ret == NULL); - - *rc= mutex_exit(&pool->mutex); - - return ret; -} - -memcached_return_t memcached_pool_push(memcached_pool_st* pool, - memcached_st *mmc) -{ - if (! pool) - return MEMCACHED_INVALID_ARGUMENTS; - - memcached_return_t rc= mutex_enter(&pool->mutex); - - if (rc != MEMCACHED_SUCCESS) - return rc; - - char* version= memcached_get_user_data(mmc); - /* Someone updated the behavior on the object.. */ - if (version != pool->version) - { - memcached_free(mmc); - memset(mmc, 0, sizeof(*mmc)); - if (memcached_clone(mmc, pool->master) == NULL) - { - rc= MEMCACHED_SOME_ERRORS; - } - } - - pool->mmc[++pool->firstfree]= mmc; - - if (pool->firstfree == 0 && pool->current_size == pool->size) - { - /* we might have people waiting for a connection.. wake them up :-) */ - pthread_cond_broadcast(&pool->cond); - } - - memcached_return_t rval= mutex_exit(&pool->mutex); - if (rc == MEMCACHED_SOME_ERRORS) - return rc; - - return rval; -} - - -memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t data) -{ - if (! pool) - return MEMCACHED_INVALID_ARGUMENTS; - - memcached_return_t rc= mutex_enter(&pool->mutex); - if (rc != MEMCACHED_SUCCESS) - return rc; - - /* update the master */ - rc= memcached_behavior_set(pool->master, flag, data); - if (rc != MEMCACHED_SUCCESS) - { - mutex_exit(&pool->mutex); - return rc; - } - - ++pool->version; - memcached_set_user_data(pool->master, pool->version); - /* update the clones */ - for (int xx= 0; xx <= pool->firstfree; ++xx) - { - rc= memcached_behavior_set(pool->mmc[xx], flag, data); - if (rc == MEMCACHED_SUCCESS) - { - memcached_set_user_data(pool->mmc[xx], pool->version); - } - else - { - memcached_free(pool->mmc[xx]); - memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx])); - - if (memcached_clone(pool->mmc[xx], pool->master) == NULL) - { - /* I'm not sure what to do in this case.. this would happen - if we fail to push the server list inside the client.. - I should add a testcase for this, but I believe the following - would work, except that you would add a hole in the pool list.. - in theory you could end up with an empty pool.... - */ - free(pool->mmc[xx]); - pool->mmc[xx]= NULL; - } - } - } - - return mutex_exit(&pool->mutex); -} - -memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t *value) -{ - if (! pool) - return MEMCACHED_INVALID_ARGUMENTS; - - memcached_return_t rc= mutex_enter(&pool->mutex); - if (rc != MEMCACHED_SUCCESS) - { - return rc; - } - - *value= memcached_behavior_get(pool->master, flag); - - return mutex_exit(&pool->mutex); -} diff --git a/libmemcached/util/pool.cc b/libmemcached/util/pool.cc new file mode 100644 index 00000000..17cff077 --- /dev/null +++ b/libmemcached/util/pool.cc @@ -0,0 +1,392 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include +#include +#include +#include + +static bool grow_pool(memcached_pool_st* pool); + +struct memcached_pool_st +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + memcached_st *master; + memcached_st **server_pool; + int firstfree; + const uint32_t size; + uint32_t current_size; + bool _owns_master; + + memcached_pool_st(memcached_st *master_arg, size_t max_arg) : + master(master_arg), + server_pool(NULL), + firstfree(-1), + size(max_arg), + current_size(0), + _owns_master(false) + { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); + } + + bool init(uint32_t initial) + { + server_pool= new (std::nothrow) memcached_st *[size]; + if (not server_pool) + return false; + + /* + Try to create the initial size of the pool. An allocation failure at + this time is not fatal.. + */ + for (unsigned int x= 0; x < initial; ++x) + { + if (not grow_pool(this)) + break; + } + + return true; + } + + ~memcached_pool_st() + { + for (int x= 0; x <= firstfree; ++x) + { + memcached_free(server_pool[x]); + server_pool[x] = NULL; + } + + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); + delete [] server_pool; + if (_owns_master) + { + memcached_free(master); + } + } + + void increment_version() + { + ++master->configure.version; + } + + bool compare_version(const memcached_st *arg) const + { + return (arg->configure.version == version()); + } + + int32_t version() const + { + return master->configure.version; + } +}; + +static memcached_return_t mutex_enter(pthread_mutex_t *mutex) +{ + int ret; + do + { + ret= pthread_mutex_lock(mutex); + } while (ret == -1 && errno == EINTR); + + return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; +} + +static memcached_return_t mutex_exit(pthread_mutex_t *mutex) +{ + int ret; + do + { + ret= pthread_mutex_unlock(mutex); + } while (ret == -1 && errno == EINTR); + + return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; +} + +/** + * Grow the connection pool by creating a connection structure and clone the + * original memcached handle. + */ +static bool grow_pool(memcached_pool_st* pool) +{ + memcached_st *obj; + if (not (obj= memcached_clone(NULL, pool->master))) + { + return false; + } + + pool->server_pool[++pool->firstfree]= obj; + pool->current_size++; + obj->configure.version= pool->version(); + + return true; +} + +static inline memcached_pool_st *_pool_create(memcached_st* master, uint32_t initial, uint32_t max) +{ + if (! initial || ! max || initial > max) + { + errno= EINVAL; + return NULL; + } + + memcached_pool_st *object= new (std::nothrow) memcached_pool_st(master, max); + if (not object) + { + errno= ENOMEM; // Set this for the failed calloc + return NULL; + } + + /* + Try to create the initial size of the pool. An allocation failure at + this time is not fatal.. + */ + if (not object->init(initial)) + { + delete object; + return NULL; + } + + return object; +} + +memcached_pool_st *memcached_pool_create(memcached_st* master, uint32_t initial, uint32_t max) +{ + return _pool_create(master, initial, max); +} + +memcached_pool_st * memcached_pool(const char *option_string, size_t option_string_length) +{ + memcached_st *memc= memcached(option_string, option_string_length); + + if (not memc) + return NULL; + + memcached_pool_st *self; + self= memcached_pool_create(memc, memc->configure.initial_pool_size, memc->configure.max_pool_size); + if (not self) + { + memcached_free(memc); + errno= ENOMEM; + return NULL; + } + errno= 0; + + self->_owns_master= true; + + return self; +} + +memcached_st* memcached_pool_destroy(memcached_pool_st* pool) +{ + if (not pool) + return NULL; + + // Legacy that we return the original structure + memcached_st *ret= NULL; + if (pool->_owns_master) + { } + else + { + ret= pool->master; + } + + delete pool; + + return ret; +} + +memcached_st* memcached_pool_pop(memcached_pool_st* pool, + bool block, + memcached_return_t *rc) +{ + assert(pool); + assert(rc); + if (not pool || not rc) + { + errno= EINVAL; + return NULL; + } + + if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS) + { + return NULL; + } + + memcached_st *ret= NULL; + do + { + if (pool->firstfree > -1) + { + ret= pool->server_pool[pool->firstfree--]; + } + else if (pool->current_size == pool->size) + { + if (not block) + { + *rc= mutex_exit(&pool->mutex); // this should be a different error + return NULL; + } + + if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1) + { + int err= errno; + mutex_exit(&pool->mutex); + errno= err; + *rc= MEMCACHED_ERRNO; + return NULL; + } + } + else if (not grow_pool(pool)) + { + (void)mutex_exit(&pool->mutex); + *rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + } + while (ret == NULL); + + *rc= mutex_exit(&pool->mutex); + + return ret; +} + +memcached_return_t memcached_pool_push(memcached_pool_st* pool, memcached_st *released) +{ + if (not pool) + return MEMCACHED_INVALID_ARGUMENTS; + + memcached_return_t rc= mutex_enter(&pool->mutex); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + /* Someone updated the behavior on the object.. */ + if (not pool->compare_version(released)) + { + memcached_free(released); + if (not (released= memcached_clone(NULL, pool->master))) + { + rc= MEMCACHED_SOME_ERRORS; + } + } + + pool->server_pool[++pool->firstfree]= released; + + if (pool->firstfree == 0 && pool->current_size == pool->size) + { + /* we might have people waiting for a connection.. wake them up :-) */ + pthread_cond_broadcast(&pool->cond); + } + + memcached_return_t rval= mutex_exit(&pool->mutex); + if (rc == MEMCACHED_SOME_ERRORS) + return rc; + + return rval; +} + + +memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t data) +{ + if (not pool) + return MEMCACHED_INVALID_ARGUMENTS; + + memcached_return_t rc= mutex_enter(&pool->mutex); + if (rc != MEMCACHED_SUCCESS) + return rc; + + /* update the master */ + rc= memcached_behavior_set(pool->master, flag, data); + if (rc != MEMCACHED_SUCCESS) + { + mutex_exit(&pool->mutex); + return rc; + } + + pool->increment_version(); + /* update the clones */ + for (int xx= 0; xx <= pool->firstfree; ++xx) + { + rc= memcached_behavior_set(pool->server_pool[xx], flag, data); + if (rc == MEMCACHED_SUCCESS) + { + pool->server_pool[xx]->configure.version= pool->version(); + } + else + { + memcached_free(pool->server_pool[xx]); + if (not (pool->server_pool[xx]= memcached_clone(NULL, pool->master))) + { + /* I'm not sure what to do in this case.. this would happen + if we fail to push the server list inside the client.. + I should add a testcase for this, but I believe the following + would work, except that you would add a hole in the pool list.. + in theory you could end up with an empty pool.... + */ + } + } + } + + return mutex_exit(&pool->mutex); +} + +memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t *value) +{ + if (! pool) + return MEMCACHED_INVALID_ARGUMENTS; + + memcached_return_t rc= mutex_enter(&pool->mutex); + if (rc != MEMCACHED_SUCCESS) + { + return rc; + } + + *value= memcached_behavior_get(pool->master, flag); + + return mutex_exit(&pool->mutex); +} diff --git a/libmemcached/util/version.c b/libmemcached/util/version.cc similarity index 92% rename from libmemcached/util/version.c rename to libmemcached/util/version.cc index a0b69255..761cbbf5 100644 --- a/libmemcached/util/version.c +++ b/libmemcached/util/version.cc @@ -54,7 +54,7 @@ bool libmemcached_util_version_check(memcached_st *memc, if (rc != MEMCACHED_SUCCESS) return false; - struct local_context check= { .major_version= major_version, .minor_version= minor_version, .micro_version= micro_version, .truth= true }; + struct local_context check= { major_version, minor_version, micro_version, true }; callbacks[0]= check_server_version; memcached_server_cursor(memc, callbacks, (void *)&check, 1); diff --git a/tests/mem_functions.c b/tests/mem_functions.c index a5e28e69..1ab8d6a3 100644 --- a/tests/mem_functions.c +++ b/tests/mem_functions.c @@ -4310,6 +4310,7 @@ static void* connection_release(void *arg) } *resource= arg; usleep(250); + // Release all of the memc we are holding assert(memcached_pool_push(resource->pool, resource->mmc) == MEMCACHED_SUCCESS); return arg; } @@ -4322,6 +4323,7 @@ static test_return_t connection_pool_test(memcached_st *memc) memcached_st *mmc[POOL_SIZE]; memcached_return_t rc; + // Fill up our array that we will store the memc that are in the pool for (size_t x= 0; x < POOL_SIZE; ++x) { mmc[x]= memcached_pool_pop(pool, false, &rc); @@ -4329,6 +4331,7 @@ static test_return_t connection_pool_test(memcached_st *memc) test_true(rc == MEMCACHED_SUCCESS); } + // All memc should be gone test_true(memcached_pool_pop(pool, false, &rc) == NULL); test_true(rc == MEMCACHED_SUCCESS); @@ -4337,11 +4340,12 @@ static test_return_t connection_pool_test(memcached_st *memc) memcached_pool_st* pool; memcached_st* mmc; } item= { .pool = pool, .mmc = mmc[9] }; + pthread_create(&tid, NULL, connection_release, &item); mmc[9]= memcached_pool_pop(pool, true, &rc); test_true(rc == MEMCACHED_SUCCESS); pthread_join(tid, NULL); - test_true(mmc[9] == item.mmc); + test_true(mmc[9]); const char *key= "key"; size_t keylen= strlen(key); -- 2.30.2