#include "libmemcached/common.h" #include "libmemcached/memcached_pool.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; }; static memcached_return 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 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 0; } memcached_pool_st *memcached_pool_create(memcached_st* mmc, uint32_t initial, uint32_t max) { memcached_pool_st* ret = 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 }; if (object.mmc != NULL) { ret= calloc(1, sizeof(*ret)); if (ret == NULL) { free(object.mmc); 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_st* memcached_pool_destroy(memcached_pool_st* pool) { 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); free(pool); return ret; } memcached_st* memcached_pool_pop(memcached_pool_st* pool, bool block, memcached_return *rc) { 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 memcached_pool_push(memcached_pool_st* pool, memcached_st *mmc) { memcached_return rc= mutex_enter(&pool->mutex); if (rc != MEMCACHED_SUCCESS) return rc; 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); } return mutex_exit(&pool->mutex); }