# Build artifacts
^libmemcached/libmemcached.la$
+^libmemcachedutil/libmemcachedutil.la$
^clients/mem(cat|cp|flush|rm|slap|stat|error)$
^tests/testapp$
.(deps|libs)/*$
INCLUDES =
-SUBDIRS = docs libmemcached tests support clients
+SUBDIRS = docs libmemcached libmemcachedutil tests support clients
EXTRA_dist = README.FIRST
test: all
--- /dev/null
+BUILD_UTILLIB=yes
+
+AC_ARG_ENABLE(utils,
+ [ --enable-utils Build libmemcachedutils [[default=yes]]],
+ [
+ if test "x$enableval" = "xno"; then
+ BUILD_UTILLIB="no"
+ fi
+ ]
+ )
+
+if test "x$BUILD_UTILLIB" = "xyes"; then
+ AC_SEARCH_LIBS(pthread_create, pthread)
+ if test "x$ac_cv_search_pthread_create" = "xno"; then
+ AC_MSG_ERROR([Sorry you need POSIX thread library to build libmemcachedutil.])
+ fi
+ AC_DEFINE([HAVE_LIBMEMCACHEDUTIL], [1], [Enables libmemcachedutil Support])
+fi
+
+AM_CONDITIONAL([BUILD_LIBMEMCACHEDUTIL],[test "x$BUILD_UTILLIB" = "xyes"])
sinclude(config/memcached.m4)
sinclude(config/setsockopt.m4)
sinclude(config/hsieh.m4)
+sinclude(config/util.m4)
# We only support GCC and Sun's forte at the moment
if test "$GCC" = "yes"
LDFLAGS="-lm"
-AC_OUTPUT(Makefile clients/Makefile tests/Makefile docs/Makefile libmemcached/Makefile support/Makefile support/libmemcached.pc support/libmemcached.spec support/libmemcached-fc.spec)
+AC_OUTPUT(Makefile clients/Makefile tests/Makefile docs/Makefile libmemcached/Makefile libmemcachedutil/Makefile support/Makefile support/libmemcached.pc support/libmemcached.spec support/libmemcached-fc.spec)
INCLUDES = include\r
\r
EXTRA_DIST = libmemcached.pod\\r
+ libmemcachedutil.pod\\r
memcached_flush.pod\\r
memcached_stats.pod\\r
memrm.pod\\r
memcached_server_st.pod\\r
memcat.pod\\r
memcached_create.pod\\r
+ memcached_pool.pod\\r
memcached_servers.pod\\r
memcp.pod\\r
memcached_delete.pod\\r
memcached_flush_buffers.3\\r
memcached_generate_hash_value.3\r
\r
+if BUILD_LIBMEMCACHEDUTIL\r
+man_MANS+= libmemcachedutil.3 \\r
+ memcached_pool_create.3 \\r
+ memcached_pool_destroy.3 \\r
+ memcached_pool_push.3 \\r
+ memcached_pool_pop.3\r
+endif\r
+\r
+\r
libmemcached.3: libmemcached.pod \r
@POD2MAN@ -c "libmemcached" -r "" -s 3 libmemcached.pod > libmemcached.3\r
\r
+libmemcachedutil.3: libmemcachedutil.pod \r
+ @POD2MAN@ -c "libmemcachedutil" -r "" -s 3 libmemcachedutil.pod > libmemcachedutil.3\r
+\r
libmemcached_examples.3: libmemcached_examples.pod\r
@POD2MAN@ -c "libmemcached" -r "" -s 3 libmemcached_examples.pod > libmemcached_examples.3\r
\r
memcached_generate_hash_value.3: memcached_generate_hash_value.pod\r
@POD2MAN@ -c "libmemcached" -r "" -s 3 memcached_generate_hash_value.pod > memcached_generate_hash_value.3\r
\r
+memcached_pool_create.3: memcached_pool.pod \r
+ @POD2MAN@ -c "libmemcachedutil" -r "" -s 3 memcached_pool.pod > memcached_pool_create.3\r
+\r
+memcached_pool_destroy.3: memcached_pool.pod \r
+ @POD2MAN@ -c "libmemcachedutil" -r "" -s 3 memcached_pool.pod > memcached_pool_destroy.3\r
+\r
+memcached_pool_pop.3: memcached_pool.pod \r
+ @POD2MAN@ -c "libmemcachedutil" -r "" -s 3 memcached_pool.pod > memcached_pool_pop.3\r
+\r
+memcached_pool_push.3: memcached_pool.pod \r
+ @POD2MAN@ -c "libmemcachedutil" -r "" -s 3 memcached_pool.pod > memcached_pool_push.3\r
+\r
memcp.1: memcp.pod\r
@POD2MAN@ -c "libmemcached" -r "" -s 1 memcp.pod > memcp.1\r
\r
podchecker memcached_version.pod\r
podchecker memflush.pod\r
podchecker memcached_flush_buffers.pod\r
+ podchecker memcached_pool.pod\r
\r
html:\r
pod2htmltree "/libmemcached" .\r
--- /dev/null
+=head1 NAME
+
+libmemcachedutil - Utility library for libmemcached
+
+=head1 LIBRARY
+
+C Client Library containing utility functions for libmemcached (libmemcachedutil, -lmemcachedutil)
+
+=head1 SYNOPSIS
+
+ cc [ flag ... ] file ... -lmemcachedutil
+
+ #include <libmemcached/memcached_util.h>
+
+=head1 DESCRIPTION
+
+B<libmemcachedutil> is a small and thread-safe client library that provides
+extra functionality built on top of B<libmemcached>.
+
+=head1 THREADS AND PROCESSES
+
+When using threads or forked processes it is important to keep an instance
+of C<memcached_st> per process or thread. Without creating your own locking
+structures you can not share a single C<memcached_st>. You can though call
+memcached_quit(3) on a C<memcached_st> and then use the resulting cloned
+structure.
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Trond Norbye, E<lt>trond.norbye@sun.comE<gt>
+
+=head1 SEE ALSO
+
+libmemcached(3) memcached_pool_create(3) memcached_pool_destroy(3) memcached_pool_pop(3) memcached_pool_push(3)
+
+=cut
--- /dev/null
+=head1 NAME
+
+memcached_pool_create, memcached_pool_destroy, memcached_pool_push, memcached_pool_pop
+
+=head1 LIBRARY
+
+C Client Library for memcached (libmemcachedutil, -lmemcachedutil)
+
+=head1 SYNOPSIS
+
+ #include <libmemcached/memcached_pool.h>
+
+ memcached_pool_st *memcached_pool_create(memcached_st* mmc, int initial, int max);
+ memcached_st* memcached_pool_destroy(memcached_pool_st* pool);
+
+ memcached_st* memcached_pool_pop(memcached_pool_st* pool, bool block);
+ void memcached_pool_push(memcached_pool_st* pool, memcached_st *mmc);
+
+ memcached_st *memcached_create (memcached_st *ptr);
+
+
+=head1 DESCRIPTION
+
+memcached_pool_create() is used to create a connection pool of objects you
+may use to remove the overhead of using memcached_clone for short
+lived C<memcached_st> objects. The mmc argument should be an
+initialised C<memcached_st> structure, and a successfull invocation of
+memcached_pool_create takes full ownership of the variable (until it
+is released by memcached_pool_destroy). The C<initial> argument
+specifies the initial size of the connection pool, and the C<max>
+argument specifies the maximum size the connection pool should grow
+to. Please note that the library will allocate a fixed size buffer
+scaled to the max size of the connection pool, so you should not pass
+MAXINT or some other large number here.
+
+memcached_pool_destroy() is used to destroy the connection pool
+created with memcached_pool_create() and release all allocated
+resources. It will return the pointer to the C<memcached_st> structure
+passed as an argument to memcached_pool_create(), and returns the
+ownership of the pointer to the caller.
+
+memcached_pool_pop() is used to grab a connection structure from the
+connection pool. The block argument specifies if the function should
+block and wait for a connection structure to be available if we try to
+exceed the maximum size.
+
+memcached_pool_push() is used to return a connection structure back to the pool.
+
+=head1 RETURN
+
+memcached_pool_create() returns a pointer to the newly created
+memcached_pool_st structure. On an allocation failure, it returns
+NULL.
+
+memcached_pool_destroy() returns the pointer (and ownership) to the
+memcached_st structure used to create the pool. If connections are in
+use it returns NULL.
+
+memcached_pool_pop() returns a pointer to a memcached_st structure
+from the pool (or NULL if an allocation cannot be satisfied).
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Trond Norbye, E<lt>trond.norbye@sun.comE<gt>
+
+=head1 SEE ALSO
+
+memcached(1) libmemcached(3) memcached_create(3) memcached_free(3) libmemcachedutil(3)
+
+=cut
memcached_types.h \
memcached_watchpoint.h
+if BUILD_LIBMEMCACHEDUTIL
+pkginclude_HEADERS+= memcached_util.h memcached_pool.h
+endif
+
lib_LTLIBRARIES = libmemcached.la
libmemcached_la_SOURCES = crc.c \
--- /dev/null
+/*
+ * Summary: Connection pool implementation for libmemcached.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Trond Norbye
+ */
+
+#ifndef MEMCACHED_POOL_H
+#define MEMCACHED_POOL_H
+
+#include <libmemcached/memcached.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct memcached_pool_st;
+typedef struct memcached_pool_st memcached_pool_st;
+memcached_pool_st *memcached_pool_create(memcached_st* mmc, int initial,
+ int max);
+memcached_st* memcached_pool_destroy(memcached_pool_st* pool);
+memcached_st* memcached_pool_pop(memcached_pool_st* pool, bool block);
+void memcached_pool_push(memcached_pool_st* pool, memcached_st *mmc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCACHED_POOL_H */
--- /dev/null
+/*
+ * Summary: interface for libmemcached utility library
+ * Description: main include file for libmemcachedutil
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Trond Norbye
+ */
+
+#ifndef MEMCACHED_UTIL_H
+#define MEMCACHED_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libmemcached/memcached_pool.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCACHED_UTIL_H */
--- /dev/null
+INCLUDES = -I$(top_builddir)
+LIBS = @LIBS@
+
+lib_LTLIBRARIES=
+if BUILD_LIBMEMCACHEDUTIL
+lib_LTLIBRARIES+= libmemcachedutil.la
+endif
+
+libmemcachedutil_la_SOURCES= memcached_pool.c
--- /dev/null
+#include "common.h"
+#include "libmemcached/memcached_pool.h"
+#include <pthread.h>
+
+struct memcached_pool_st
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ memcached_st *master;
+ memcached_st **mmc;
+ int firstfree;
+ int size;
+ int current_size;
+};
+
+/**
+ * Lock a the pthread mutex and handle error conditions. If we fail to
+ * lock the mutex there must be something really wrong and we cannot continue.
+ */
+static void mutex_enter(pthread_mutex_t *mutex)
+{
+ int ret;
+ do
+ ret= pthread_mutex_lock(mutex);
+ while (ret == -1 && errno == EINTR);
+
+ if (ret == -1)
+ {
+ /*
+ ** This means something is seriously wrong (deadlock or an internal
+ ** error in the posix library. Print out an error message and abort
+ ** the program.
+ */
+ fprintf(stderr, "pthread_mutex_lock failed: %s\n", strerror(errno));
+ fflush(stderr);
+ abort();
+ }
+}
+
+/**
+ * Unlock a the pthread mutex and handle error conditions.
+ * All errors except EINTR is fatal errors and will terminate the program
+ * with a coredump.
+ */
+static void mutex_exit(pthread_mutex_t *mutex) {
+ int ret;
+ do
+ ret = pthread_mutex_unlock(mutex);
+ while (ret == -1 && errno == EINTR);
+
+ if (ret == -1)
+ {
+ /*
+ ** This means something is seriously wrong (deadlock or an internal
+ ** error in the posix library. Print out an error message and abort
+ ** the program.
+ */
+ fprintf(stderr, "pthread_mutex_unlock %s\n", strerror(errno));
+ fflush(stderr);
+ abort();
+ }
+}
+
+/**
+ * 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, int initial, int 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 (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 ii = 0; ii <= pool->firstfree; ++ii)
+ {
+ memcached_free(pool->mmc[ii]);
+ free(pool->mmc[ii]);
+ pool->mmc[ii] = 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_st *ret = NULL;
+ mutex_enter(&pool->mutex);
+ do
+ {
+ if (pool->firstfree > -1)
+ ret = pool->mmc[pool->firstfree--];
+ else if (pool->current_size == pool->size)
+ {
+ if (!block)
+ {
+ mutex_exit(&pool->mutex);
+ return NULL;
+ }
+
+ if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1)
+ {
+ /*
+ ** This means something is seriously wrong (an internal error in the
+ ** posix library. Print out an error message and abort the program.
+ */
+ fprintf(stderr, "pthread cond_wait %s\n", strerror(errno));
+ fflush(stderr);
+ abort();
+ }
+ }
+ else if (grow_pool(pool) == -1)
+ {
+ mutex_exit(&pool->mutex);
+ return NULL;
+ }
+ }
+ while (ret == NULL);
+
+ mutex_exit(&pool->mutex);
+ return ret;
+}
+
+void memcached_pool_push(memcached_pool_st* pool, memcached_st *mmc)
+{
+ mutex_enter(&pool->mutex);
+ 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);
+ }
+ mutex_exit(&pool->mutex);
+}
INCLUDES = -I$(top_builddir)
LDADDS = $(top_builddir)/libmemcached/libmemcached.la
+if BUILD_LIBMEMCACHEDUTIL
+LDADDS+= $(top_builddir)/libmemcachedutil/libmemcachedutil.la
+endif
+
+
EXTRA_DIST = output.res output2.res\
r/memcat.res\
r/memcp.res\
#include "test.h"
+#ifdef HAVE_LIBMEMCACHEDUTIL
+#include <pthread.h>
+#include "libmemcached/memcached_util.h"
+#endif
+
#define GLOBAL_COUNT 10000
#define GLOBAL2_COUNT 100
#define SERVERS_TO_CREATE 5
return TEST_SUCCESS;
}
+#ifdef HAVE_LIBMEMCACHEDUTIL
+static void* connection_release(void *arg) {
+ struct {
+ memcached_pool_st* pool;
+ memcached_st* mmc;
+ } *resource= arg;
+
+ usleep(250);
+ memcached_pool_push(resource->pool, resource->mmc);
+}
+
+static test_return connection_pool_test(memcached_st *memc)
+{
+ memcached_pool_st* pool= memcached_pool_create(memc, 5, 10);
+ assert(pool != NULL);
+ memcached_st* mmc[10];
+
+ for (int x= 0; x < 10; ++x) {
+ mmc[x]= memcached_pool_pop(pool, false);
+ assert(mmc[x] != NULL);
+ }
+
+ assert(memcached_pool_pop(pool, false) == NULL);
+ pthread_t tid;
+ struct {
+ 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);
+ pthread_join(tid, NULL);
+ assert(mmc[9] == item.mmc);
+ const char *key= "key";
+ size_t keylen= strlen(key);
+
+ // verify that I can do ops with all connections
+ memcached_return rc;
+ rc= memcached_set(mmc[0], key, keylen, "0", 1, 0, 0);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ for (int x= 0; x < 10; ++x) {
+ uint64_t number_value;
+ rc= memcached_increment(mmc[x], key, keylen, 1, &number_value);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(number_value == (x+1));
+ }
+
+ // Release them..
+ for (int x= 0; x < 10; ++x)
+ memcached_pool_push(pool, mmc[x]);
+
+ assert(memcached_pool_destroy(pool) == memc);
+ return TEST_SUCCESS;
+}
+#endif
+
static void increment_request_id(uint16_t *id)
{
(*id)++;
{"delete_through", 1, delete_through },
{"noreply", 1, noreply_test},
{"analyzer", 1, analyzer_test},
+#ifdef HAVE_LIBMEMCACHEDUTIL
+ {"connectionpool", 1, connection_pool_test },
+#endif
{0, 0, 0}
};