m4_include([m4/memcached_sasl.m4])
m4_include([m4/gearmand.m4])
m4_include([m4/libgearman.m4])
-m4_include([memcached/version.m4])
AM_CONDITIONAL(BUILDING_LIBMEMCACHED, true)
AM_CONDITIONAL(HAVE_LIBMEMCACHED, false)
namespace {
-#if 0
std::string print_argv(libtest::vchar_ptr_t& built_argv)
{
std::stringstream arg_buffer;
return arg_buffer.str();
}
-#endif
+#if 0
std::string print_argv(char** argv)
{
std::stringstream arg_buffer;
return arg_buffer.str();
}
+#endif
+
static Application::error_t int_to_error_t(int arg)
{
if (exit_code == Application::INVALID)
{
Error << print_argv(built_argv, _argc);
+ /
}
#endif
slurp();
-#if 0
if (exit_code == Application::INVALID)
{
- Error << print_argv(built_argv, _argc);
+ Error << print_argv(built_argv);
}
-#endif
-
+;
return exit_code;
}
+++ /dev/null
-Dependencies:
-
- -- libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
-
-If using Linux, you need a kernel with epoll. Sure, libevent will
-work with normal select, but it sucks.
-
-epoll isn't in Linux 2.4, but there's a backport at:
-
- http://www.xmailserver.org/linux-patches/nio-improve.html
-
-You want the epoll-lt patch (level-triggered).
-
-If you're using MacOS, you'll want libevent 1.1 or higher to deal with
-a kqueue bug.
-
-Also, be warned that the -k (mlockall) option to memcached might be
-dangerous when using a large cache. Just make sure the memcached machines
-don't swap. memcached does non-blocking network I/O, but not disk. (it
-should never go to disk, or you've lost the whole point of it)
-
-The memcached website is at:
-
- http://www.memcached.org
-
-Want to contribute? Up-to-date pointers should be at:
-
- http://contributing.appspot.com/memcached
--- /dev/null
+# Memcached
+
+## Dependencies
+
+* libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
+
+## Environment
+
+### Linux
+
+If using Linux, you need a kernel with epoll. Sure, libevent will
+work with normal select, but it sucks.
+
+epoll isn't in Linux 2.4, but there's a backport at:
+
+ http://www.xmailserver.org/linux-patches/nio-improve.html
+
+You want the epoll-lt patch (level-triggered).
+
+### Mac OS X
+
+If you're using MacOS, you'll want libevent 1.1 or higher to deal with
+a kqueue bug.
+
+Also, be warned that the -k (mlockall) option to memcached might be
+dangerous when using a large cache. Just make sure the memcached machines
+don't swap. memcached does non-blocking network I/O, but not disk. (it
+should never go to disk, or you've lost the whole point of it)
+
+## Website
+
+* http://www.memcached.org
+
+## Contributing
+
+Want to contribute? Up-to-date pointers should be at:
+
+* http://contributing.appspot.com/memcached
*/
#include "memcached.h"
-
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/signal.h>
static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-#endif
-
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
typedef unsigned char ub1; /* unsigned 1-byte quantities */
pthread_cond_wait(&maintenance_cond, &cache_lock);
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
return NULL;
}
mutex_lock(&cache_lock);
do_run_maintenance_thread = 0;
pthread_cond_signal(&maintenance_cond);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
/* Wait for the maintenance thread to stop */
pthread_join(maintenance_tid, NULL);
echo "automake..."
if test x$AUTOMAKE = x; then
- AUTOMAKE=`locate_binary automake-1.11 automake-1.10 automake-1.9 automake-1.7`
+ AUTOMAKE=`locate_binary automake-1.12 automake-1.11 automake-1.10 automake-1.9 automake-1.7`
if test x$AUTOMAKE = x; then
die "Did not find a supported automake"
fi
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#include "memcached.h"
+#include <config.h>
#include <stdlib.h>
#include <string.h>
free(cache->name);
free(cache->ptr);
pthread_mutex_destroy(&cache->mutex);
+ free(cache);
}
void* cache_alloc(cache_t *cache) {
* SUCH DAMAGE.
*/
+#include <config.h>
+
#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
# pragma ident "@(#)$Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $"
# pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $"
#endif
-#include "memcached.h"
-
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include "memcached.h"
+
int daemonize(int nochdir, int noclose)
{
int fd;
use FindBin qw($Bin);
chdir "$Bin/.." or die;
-my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am);
+my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
push(@exempted, glob("doc/*.xml"));
push(@exempted, glob("doc/xml2rfc/*.xsl"));
push(@exempted, glob("m4/*backport*m4"));
- <source class> is an id number for the slab class to steal a page from
+A source class id of -1 means "pick from any valid class"
+
- <dest class> is an id number for the slab class to move a page to
The response line could be one of:
The automover can be enabled or disabled at runtime with this command.
-slabs automove <1|0>
+slabs automove <0|1>
-- 1|0 is the indicator on whether to enable the slabs automover or not.
+- 0|1|2 is the indicator on whether to enable the slabs automover or not.
The response should always be "OK\r\n"
+- <0> means to set the thread on standby
+
+- <1> means to run the builtin slow algorithm to choose pages to move
+
+- <2> is a highly aggressive mode which causes pages to be moved every time
+ there is an eviction. It is not recommended to run for very long in this
+ mode unless your access patterns are very well understood.
+
Statistics
----------
stats <args>\r\n
Depending on <args>, various internal data is sent by the server. The
-kinds of arguments and the data sent are not documented in this vesion
+kinds of arguments and the data sent are not documented in this version
of the protocol, and are subject to change for the convenience of
memcache developers.
* whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG
* are set in the configure script.
*/
-#if defined(ENDIAN_BIG) && ENDIAN_BIG == 1
+#if ENDIAN_BIG == 1
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 1
#else
-# if defined(ENDIAN_LITTLE) && ENDIAN_LITTLE == 1
+# if ENDIAN_LITTLE == 1
# define HASH_LITTLE_ENDIAN 1
# define HASH_BIG_ENDIAN 0
# else
case 2 : a+=k[0]&0xffff; break;
case 1 : a+=k[0]&0xff; break;
case 0 : return c; /* zero length strings require no mixing */
- default:
- abort();
}
#else /* make valgrind happy */
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length strings require no mixing */
- default:
- abort();
}
} else { /* need to read the key one byte at a time */
case 1 : a+=k[0];
break;
case 0 : return c; /* zero length strings require no mixing */
- default:
- abort();
}
}
void item_stats_reset(void) {
mutex_lock(&cache_lock);
memset(itemstats, 0, sizeof(itemstats));
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
} else if ((it = slabs_alloc(ntotal, id)) == NULL) {
if (settings.evict_to_free == 0) {
itemstats[id].outofmemory++;
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
return NULL;
}
itemstats[id].evicted++;
do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
/* Initialize the item block: */
it->slabs_clsid = 0;
+
+ /* If we've just evicted an item, and the automover is set to
+ * angry bird mode, attempt to rip memory into this slab class.
+ * TODO: Move valid object detection into a function, and on a
+ * "successful" memory pull, look behind and see if the next alloc
+ * would be an eviction. Then kick off the slab mover before the
+ * eviction happens.
+ */
+ if (settings.slab_automove == 2)
+ slabs_reassign(-1, id);
} else {
refcount_decr(&search->refcount);
}
search->refcount = 1;
do_item_unlink_nolock(search, hash(ITEM_key(search), search->nkey, 0));
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
return NULL;
}
* been removed from the slab LRU.
*/
it->refcount = 1; /* the caller will have a reference */
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
it->next = it->prev = it->h_next = 0;
it->slabs_clsid = id;
assoc_insert(it, hv);
item_link_q(it);
refcount_incr(&it->refcount);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
return 1;
}
item_unlink_q(it);
do_item_remove(it);
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
/* FIXME: Is it necessary to keep this copy/pasted code? */
it->time = current_time;
item_link_q(it);
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
}
return do_item_link(new_it, hv);
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
-
/*@null@*/
char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes) {
unsigned int memlimit = 2 * 1024 * 1024; /* 2MB max response size */
for (i = 0; i < LARGEST_ID; i++) {
evicted[i] = itemstats[i].evicted;
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
void do_item_stats(ADD_STAT add_stats, void *c) {
it = NULL;
}
}
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
int was_found = 0;
if (settings.verbose > 2) {
*/
static volatile bool allow_new_conns = true;
static struct event maxconnsevent;
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
static void maxconns_handler(const int fd, const short which, void *arg) {
struct timeval t = {.tv_sec = 0, .tv_usec = 10000};
settings.maxconns_fast = false;
settings.hashpower_init = 0;
settings.slab_reassign = false;
- settings.slab_automove = false;
+ settings.slab_automove = 0;
}
/*
}
static const char *prot_text(enum protocol prot) {
- const char *rv = "unknown";
+ char *rv = "unknown";
switch(prot) {
case ascii_prot:
rv = "ascii";
case negotiating_prot:
rv = "auto-negotiate";
break;
- default:
- abort();
}
return rv;
}
return statenames[state];
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wtype-limits"
-#endif
/*
* Sets a connection's current state in the state machine. Any special
* processing that needs to happen on certain state transitions can
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wsign-compare"
-#endif
static void out_string(conn *c, const char *str) {
size_t len;
case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
errstr = "Auth failure.";
break;
- case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
- assert(false);
- case PROTOCOL_BINARY_RESPONSE_SUCCESS:
- assert(false);
default:
assert(false);
errstr = "UNHANDLED ERROR";
}
/* Form and send a response to a command over the binary protocol */
-static void write_bin_response(conn *c, const void *d, int hlen, int keylen, int dlen) {
+static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) {
if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET ||
c->cmd == PROTOCOL_BINARY_CMD_GETK) {
add_bin_header(c, 0, hlen, keylen, dlen);
case DELTA_ITEM_CAS_MISMATCH:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, 0);
break;
-
- default:
- assert(0);
- abort();
}
}
eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED;
}
write_bin_error(c, eno, 0);
- default:
- assert(false);
- abort();
}
item_remove(c->item); /* release the c->item reference */
protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->wbuf;
char* key = binary_get_key(c);
size_t nkey = c->binary_header.request.keylen;
- protocol_binary_request_touch *t = (void *)&c->binary_header;
- uint32_t exptime = ntohl(t->message.body.expiration);
+ protocol_binary_request_touch *t = binary_get_request(c);
+ time_t exptime = ntohl(t->message.body.expiration);
if (settings.verbose > 1) {
int ii;
}
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
static void append_bin_stats(const char *key, const uint16_t klen,
const char *val, const uint32_t vlen,
conn *c) {
uint32_t bodylen = klen + vlen;
protocol_binary_response_header header = {
.response.magic = (uint8_t)PROTOCOL_BINARY_RES,
- .response.opcode = (uint8_t)PROTOCOL_BINARY_CMD_STAT,
+ .response.opcode = PROTOCOL_BINARY_CMD_STAT,
.response.keylen = (uint16_t)htons(klen),
- .response.extlen = (uint8_t)0,
.response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES,
- .response.status = (uint16_t)0,
.response.bodylen = htonl(bodylen),
- .response.opaque = c->opaque,
- .response.cas = (uint64_t)0
+ .response.opaque = c->opaque
};
memcpy(buf, header.bytes, sizeof(header.response));
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_VERSION:
if (extlen == 0 && keylen == 0 && bodylen == 0) {
- write_bin_response(c, RVERSION, 0, 0, strlen(RVERSION));
+ write_bin_response(c, VERSION, 0, 0, strlen(VERSION));
} else {
protocol_error = 1;
}
case bin_reading_sasl_auth_data:
process_bin_complete_sasl_auth(c);
break;
- case bin_reading_cas_header:
- assert(0);
- case bin_no_state:
- assert(0);
default:
fprintf(stderr, "Not handling substate %d\n", c->substate);
assert(0);
APPEND_STAT("pid", "%lu", (long)pid);
APPEND_STAT("uptime", "%u", now);
APPEND_STAT("time", "%ld", now + (long)process_started);
- APPEND_STAT("version", "%s", RVERSION);
+ APPEND_STAT("version", "%s", VERSION);
APPEND_STAT("libevent", "%s", event_get_version());
APPEND_STAT("pointer_size", "%d", (int)(8 * sizeof(void *)));
APPEND_STAT("maxconns_fast", "%s", settings.maxconns_fast ? "yes" : "no");
APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);
APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");
- APPEND_STAT("slab_automove", "%s", settings.slab_automove ? "yes" : "no");
+ APPEND_STAT("slab_automove", "%d", settings.slab_automove);
}
static void process_stat(conn *c, token_t *tokens, const size_t ntokens) {
}
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
-#endif
/* ntokens is overwritten here... shrug.. */
static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
char *key;
break;
case DELTA_ITEM_CAS_MISMATCH:
break; /* Should never get here */
- default:
- assert(false);
- abort();
}
}
need to update the CAS on the existing item. */
mutex_lock(&cache_lock); /* FIXME */
ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
memcpy(ITEM_data(it), buf, res);
memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
level = strtoul(tokens[2].value, NULL, 10);
if (level == 0) {
- settings.slab_automove = false;
- } else if (level == 1) {
- settings.slab_automove = true;
+ settings.slab_automove = 0;
+ } else if (level == 1 || level == 2) {
+ settings.slab_automove = level;
} else {
out_string(c, "ERROR");
return;
} else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0)) {
- out_string(c, "VERSION " RVERSION);
+ out_string(c, "VERSION " VERSION);
} else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0)) {
case REASSIGN_NOSPARE:
out_string(c, "NOSPARE source class has no spare pages");
break;
- case REASSIGN_DEST_NOT_FULL:
- out_string(c, "NOTFULL dest class has spare memory");
- break;
- case REASSIGN_SRC_NOT_SAFE:
- out_string(c, "UNSAFE src class is in an unsafe state");
- break;
case REASSIGN_SRC_DST_SAME:
out_string(c, "SAME src and dst class are identical");
break;
- default:
- assert(false);
- abort();
}
return;
} else if (ntokens == 4 &&
case READ_MEMORY_ERROR: /* Failed to allocate more memory */
/* State already set by try_read_network */
break;
- default:
- assert(false);
- abort();
}
break;
case TRANSMIT_SOFT_ERROR:
stop = true;
break;
- default:
- assert(false);
- abort();
}
break;
case conn_max_state:
assert(false);
break;
- default:
- assert(false);
- abort();
}
}
}
#endif
- error = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
- if (error != 0)
- {
- perror("setsockopt(SO_REUSEADDR)");
- }
-
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
if (IS_UDP(transport)) {
maximize_sndbuf(sfd);
} else {
}
static void usage(void) {
- printf(RPACKAGE " " RVERSION "\n");
+ printf(PACKAGE " " VERSION "\n");
printf("-p <num> TCP port number to listen on (default: 11211)\n"
"-U <num> UDP port number to listen on (default: 11211, 0 is off)\n"
"-s <file> UNIX socket path to listen on (disables network support)\n"
}
static void usage_license(void) {
- printf(RPACKAGE " " RVERSION "\n\n");
+ printf(PACKAGE " " VERSION "\n\n");
printf(
"Copyright (c) 2003, Danga Interactive, Inc. <http://www.danga.com/>\n"
"All rights reserved.\n"
return ret;
#else
- return 0;
+ return -1;
#endif
}
SLAB_AUTOMOVE
};
char *const subopts_tokens[] = {
- [MAXCONNS_FAST] = (char*)"maxconns_fast",
- [HASHPOWER_INIT] = (char*)"hashpower",
- [SLAB_REASSIGN] = (char*)"slab_reassign",
- [SLAB_AUTOMOVE] = (char*)"slab_automove",
+ [MAXCONNS_FAST] = "maxconns_fast",
+ [HASHPOWER_INIT] = "hashpower",
+ [SLAB_REASSIGN] = "slab_reassign",
+ [SLAB_AUTOMOVE] = "slab_automove",
NULL
};
case 'L' :
if (enable_large_pages() == 0) {
preallocate = true;
+ } else {
+ fprintf(stderr, "Cannot enable large pages on this system\n"
+ "(There is no Linux support as of this version)\n");
+ return 1;
}
break;
case 'C' :
settings.slab_reassign = true;
break;
case SLAB_AUTOMOVE:
- settings.slab_automove = true;
+ if (subopts_value == NULL) {
+ settings.slab_automove = 1;
+ break;
+ }
+ settings.slab_automove = atoi(subopts_value);
+ if (settings.slab_automove < 0 || settings.slab_automove > 2) {
+ fprintf(stderr, "slab_automove must be between 0 and 2\n");
+ return 1;
+ }
break;
default:
printf("Illegal suboption \"%s\"\n", subopts_value);
stop_assoc_maintenance_thread();
/* remove the PID file if we're a daemon */
-#if 0
if (do_daemonize)
remove_pidfile(pid_file);
-#endif
/* Clean up strdup() call for bind() address */
if (settings.inter)
free(settings.inter);
#endif
#include <stdbool.h>
-#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#define POWER_SMALLEST 1
#define POWER_LARGEST 200
#define CHUNK_ALIGN_BYTES 8
-#define DONT_PREALLOC_SLABS
#define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1)
/** How long an object can reasonably be assumed to be locked before
bool sasl; /* SASL on/off */
bool maxconns_fast; /* Whether or not to early close connections */
bool slab_reassign; /* Whether or not slab reassignment is allowed */
- bool slab_automove; /* Whether or not to automatically move slabs */
+ int slab_automove; /* Whether or not to automatically move slabs */
int hashpower_init; /* Starting hash power level */
};
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
extern struct stats stats;
extern time_t process_started;
extern struct settings settings;
#define ITEM_FETCHED 8
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
/**
* Structure for storing items within memcached.
*/
enum store_item_type store_item(item *item, int comm, conn *c);
-#if defined(HAVE_DROP_PRIVILEGES) && HAVE_DROP_PRIVILEGES
+#if HAVE_DROP_PRIVILEGES
extern void drop_privileges(void);
#else
#define drop_privileges()
%files
%defattr(-,root,root,-)
-%doc AUTHORS ChangeLog COPYING NEWS README doc/CONTRIBUTORS doc/*.txt
+%doc AUTHORS ChangeLog COPYING NEWS README.md doc/CONTRIBUTORS doc/*.txt
%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
%dir %attr(750,nobody,nobody) %{_localstatedir}/run/memcached
*/
probe command__append(int connid, const char *key, int keylen, int size, int64_t casid);
+ /**
+ * Fired for an touch-command.
+ * @param connid connection id
+ * @param key requested key
+ * @param keylen length of the key
+ * @param size the new size of the key's data (or signed int -1 if
+ * not found)
+ * @param casid the casid for the item
+ */
+ probe command__touch(int connid, const char *key, int keylen, int size, int64_t casid);
+
/**
* Fired for a cas-command.
* @param connid connection id
void *slots; /* list of item ptrs */
unsigned int sl_curr; /* total free items in list */
- void *end_page_ptr; /* pointer to next free item at end of page, or 0 */
- unsigned int end_page_free; /* number of items remaining at end of last alloced page */
-
unsigned int slabs; /* how many slabs were allocated for this class */
void **slab_list; /* array of slab pointers */
* Access to the slab allocator is protected by this lock
*/
static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t slabs_rebalance_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* Forward Declarations
*/
static int do_slabs_newslab(const unsigned int id);
static void *memory_allocate(size_t size);
+static void do_slabs_free(void *ptr, const size_t size, unsigned int id);
-#ifndef DONT_PREALLOC_SLABS
/* Preallocate as many slab pages as possible (called from slabs_init)
on start-up, so users don't get confused out-of-memory errors when
they do have free (in-slab) space, but no space to make new slabs.
slab types can be made. if max memory is less than 18 MB, only the
smaller ones will be made. */
static void slabs_preallocate (const unsigned int maxslabs);
-#endif
/*
* Figures out which slab class (chunk size) is required to store an item of
}
-#ifndef DONT_PREALLOC_SLABS
- {
- char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
-
- if (pre_alloc == NULL || atoi(pre_alloc) != 0) {
- slabs_preallocate(power_largest);
- }
+ if (prealloc) {
+ slabs_preallocate(power_largest);
}
-#endif
}
-#ifndef DONT_PREALLOC_SLABS
static void slabs_preallocate (const unsigned int maxslabs) {
int i;
unsigned int prealloc = 0;
for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
if (++prealloc > maxslabs)
return;
- do_slabs_newslab(i);
+ if (do_slabs_newslab(i) == 0) {
+ fprintf(stderr, "Error while preallocating slab memory!\n"
+ "If using -L or other prealloc options, max memory must be "
+ "at least %d megabytes.\n", power_largest);
+ exit(1);
+ }
}
}
-#endif
static int grow_slab_list (const unsigned int id) {
slabclass_t *p = &slabclass[id];
return 1;
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wsign-compare"
-#endif
+static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {
+ slabclass_t *p = &slabclass[id];
+ int x;
+ for (x = 0; x < p->perslab; x++) {
+ do_slabs_free(ptr, 0, id);
+ ptr += p->size;
+ }
+}
static int do_slabs_newslab(const unsigned int id) {
slabclass_t *p = &slabclass[id];
}
memset(ptr, 0, (size_t)len);
- p->end_page_ptr = ptr;
- p->end_page_free = p->perslab;
+ split_slab_page_into_freelist(ptr, id);
p->slab_list[p->slabs++] = ptr;
mem_malloced += len;
p = &slabclass[id];
assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);
-#ifdef USE_SYSTEM_MALLOC
- if (mem_limit && mem_malloced + size > mem_limit) {
- MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
- return 0;
- }
- mem_malloced += size;
- ret = malloc(size);
- MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret);
- return ret;
-#endif
-
/* fail unless we have space at the end of a recently allocated page,
we have something on our freelist, or we could allocate a new page */
- if (! (p->end_page_ptr != 0 || p->sl_curr != 0 ||
- do_slabs_newslab(id) != 0)) {
+ if (! (p->sl_curr != 0 || do_slabs_newslab(id) != 0)) {
/* We don't have more memory available */
ret = NULL;
} else if (p->sl_curr != 0) {
if (it->next) it->next->prev = 0;
p->sl_curr--;
ret = (void *)it;
- } else {
- /* if we recently allocated a whole page, return from that */
- assert(p->end_page_ptr != NULL);
- ret = p->end_page_ptr;
- if (--p->end_page_free != 0) {
- p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size;
- } else {
- p->end_page_ptr = 0;
- }
}
if (ret) {
MEMCACHED_SLABS_FREE(size, id, ptr);
p = &slabclass[id];
-#ifdef USE_SYSTEM_MALLOC
- mem_malloced -= size;
- free(ptr);
- return;
-#endif
-
it = (item *)ptr;
it->it_flags |= ITEM_SLABBED;
it->prev = 0;
APPEND_NUM_STAT(i, "total_pages", "%u", slabs);
APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab);
APPEND_NUM_STAT(i, "used_chunks", "%u",
- slabs*perslab - p->sl_curr - p->end_page_free);
+ slabs*perslab - p->sl_curr);
APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr);
- APPEND_NUM_STAT(i, "free_chunks_end", "%u", p->end_page_free);
+ /* Stat is dead, but displaying zero instead of removing it. */
+ APPEND_NUM_STAT(i, "free_chunks_end", "%u", 0);
APPEND_NUM_STAT(i, "mem_requested", "%llu",
(unsigned long long)p->requested);
APPEND_NUM_STAT(i, "get_hits", "%llu",
}
static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t slab_rebalance_cond = PTHREAD_COND_INITIALIZER;
static volatile int do_run_slab_thread = 1;
+static volatile int do_run_slab_rebalance_thread = 1;
#define DEFAULT_SLAB_BULK_CHECK 1
int slab_bulk_check = DEFAULT_SLAB_BULK_CHECK;
static int slab_rebalance_start(void) {
slabclass_t *s_cls;
- slabclass_t *d_cls;
int no_go = 0;
pthread_mutex_lock(&cache_lock);
no_go = -2;
s_cls = &slabclass[slab_rebal.s_clsid];
- d_cls = &slabclass[slab_rebal.d_clsid];
- if (d_cls->end_page_ptr || s_cls->end_page_ptr ||
- !grow_slab_list(slab_rebal.d_clsid)) {
+ if (!grow_slab_list(slab_rebal.d_clsid)) {
no_go = -1;
}
break;
case MOVE_PASS:
break;
- default:
- assert(false);
- abort();
}
slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size;
memset(slab_rebal.slab_start, 0, (size_t)settings.item_size_max);
d_cls->slab_list[d_cls->slabs++] = slab_rebal.slab_start;
- d_cls->end_page_ptr = slab_rebal.slab_start;
- d_cls->end_page_free = d_cls->perslab;
+ split_slab_page_into_freelist(slab_rebal.slab_start,
+ slab_rebal.d_clsid);
slab_rebal.done = 0;
slab_rebal.s_clsid = 0;
return 0;
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
/* Slab rebalancer thread.
* Does not use spinlocks since it is not timing sensitive. Burn less CPU and
* go to sleep if locks are contended
*/
static void *slab_maintenance_thread(void *arg) {
- int was_busy = 0;
int src, dest;
while (do_run_slab_thread) {
+ if (settings.slab_automove == 1) {
+ if (slab_automove_decision(&src, &dest) == 1) {
+ /* Blind to the return codes. It will retry on its own */
+ slabs_reassign(src, dest);
+ }
+ sleep(1);
+ } else {
+ /* Don't wake as often if we're not enabled.
+ * This is lazier than setting up a condition right now. */
+ sleep(5);
+ }
+ }
+ return NULL;
+}
+
+/* Slab mover thread.
+ * Sits waiting for a condition to jump off and shovel some memory about
+ */
+static void *slab_rebalance_thread(void *arg) {
+ int was_busy = 0;
+
+ while (do_run_slab_rebalance_thread) {
if (slab_rebalance_signal == 1) {
if (slab_rebalance_start() < 0) {
/* Handle errors with more specifity as required. */
slab_rebalance_signal = 0;
}
+ was_busy = 0;
} else if (slab_rebalance_signal && slab_rebal.slab_start != NULL) {
- /* If we have a decision to continue, continue it */
was_busy = slab_rebalance_move();
- } else if (settings.slab_automove && slab_automove_decision(&src, &dest) == 1) {
- /* Blind to the return codes. It will retry on its own */
- slabs_reassign(src, dest);
}
if (slab_rebal.done) {
slab_rebalance_finish();
+ } else if (was_busy) {
+ /* Stuck waiting for some items to unlock, so slow down a bit
+ * to give them a chance to free up */
+ usleep(50);
}
- /* Sleep a bit if no work to do, or waiting on busy objects */
- if (was_busy || !slab_rebalance_signal)
- sleep(1);
+ if (slab_rebalance_signal == 0) {
+ /* always hold this lock while we're running */
+ pthread_cond_wait(&slab_rebalance_cond, &slabs_rebalance_lock);
+ }
}
return NULL;
}
+/* Iterate at most once through the slab classes and pick a "random" source.
+ * I like this better than calling rand() since rand() is slow enough that we
+ * can just check all of the classes once instead.
+ */
+static int slabs_reassign_pick_any(int dst) {
+ static int cur = POWER_SMALLEST - 1;
+ int tries = power_largest - POWER_SMALLEST + 1;
+ for (; tries > 0; tries--) {
+ cur++;
+ if (cur > power_largest)
+ cur = POWER_SMALLEST;
+ if (cur == dst)
+ continue;
+ if (slabclass[cur].slabs > 1) {
+ return cur;
+ }
+ }
+ return -1;
+}
+
static enum reassign_result_type do_slabs_reassign(int src, int dst) {
if (slab_rebalance_signal != 0)
return REASSIGN_RUNNING;
if (src == dst)
return REASSIGN_SRC_DST_SAME;
+ /* Special indicator to choose ourselves. */
+ if (src == -1) {
+ src = slabs_reassign_pick_any(dst);
+ /* TODO: If we end up back at -1, return a new error type */
+ }
+
if (src < POWER_SMALLEST || src > power_largest ||
dst < POWER_SMALLEST || dst > power_largest)
return REASSIGN_BADCLASS;
if (slabclass[src].slabs < 2)
return REASSIGN_NOSPARE;
- if (slabclass[dst].end_page_ptr)
- return REASSIGN_DEST_NOT_FULL;
-
- if (slabclass[src].end_page_ptr)
- return REASSIGN_SRC_NOT_SAFE;
-
slab_rebal.s_clsid = src;
slab_rebal.d_clsid = dst;
slab_rebalance_signal = 1;
+ pthread_cond_signal(&slab_rebalance_cond);
return REASSIGN_OK;
}
enum reassign_result_type slabs_reassign(int src, int dst) {
enum reassign_result_type ret;
- mutex_lock(&slabs_lock);
+ if (pthread_mutex_trylock(&slabs_rebalance_lock) != 0) {
+ return REASSIGN_RUNNING;
+ }
ret = do_slabs_reassign(src, dst);
- pthread_mutex_unlock(&slabs_lock);
+ pthread_mutex_unlock(&slabs_rebalance_lock);
return ret;
}
static pthread_t maintenance_tid;
+static pthread_t rebalance_tid;
int start_slab_maintenance_thread(void) {
int ret;
slab_bulk_check = DEFAULT_SLAB_BULK_CHECK;
}
}
+
+ if (pthread_cond_init(&slab_rebalance_cond, NULL) != 0) {
+ fprintf(stderr, "Can't intiialize rebalance condition\n");
+ return -1;
+ }
+ pthread_mutex_init(&slabs_rebalance_lock, NULL);
+
if ((ret = pthread_create(&maintenance_tid, NULL,
slab_maintenance_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
+ fprintf(stderr, "Can't create slab maint thread: %s\n", strerror(ret));
+ return -1;
+ }
+ if ((ret = pthread_create(&rebalance_tid, NULL,
+ slab_rebalance_thread, NULL)) != 0) {
+ fprintf(stderr, "Can't create rebal thread: %s\n", strerror(ret));
return -1;
}
return 0;
void stop_slab_maintenance_thread(void) {
mutex_lock(&cache_lock);
do_run_slab_thread = 0;
+ do_run_slab_rebalance_thread = 0;
pthread_cond_signal(&maintenance_cond);
pthread_mutex_unlock(&cache_lock);
/* Wait for the maintenance thread to stop */
pthread_join(maintenance_tid, NULL);
+ pthread_join(rebalance_tid, NULL);
}
* 0 means error: can't store such a large object
*/
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
unsigned int slabs_clsid(const size_t size);
/** Allocate object of given length. 0 on error */ /*@null@*/
enum reassign_result_type {
REASSIGN_OK=0, REASSIGN_RUNNING, REASSIGN_BADCLASS, REASSIGN_NOSPARE,
- REASSIGN_DEST_NOT_FULL, REASSIGN_SRC_NOT_SAFE, REASSIGN_SRC_DST_SAME
+ REASSIGN_SRC_DST_SAME
};
enum reassign_result_type slabs_reassign(int src, int dst);
use strict;
use warnings;
-use Test::More tests => 3539;
+use Test::More tests => 3549;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
$check->("totouch", 0, "toast2");
# Test miss as well
+ $mc->set("totouch", "toast3", 0, 1);
+ $res = $mc->touch("totouch", 1);
+ sleep 3;
+ $empty->("totouch");
}
# diag "Silent set.";
use strict;
use warnings;
-use Test::More tests => 131;
+use Test::More tests => 130;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
ok($slabs_before->{"25:total_pages"} != $slabs_after->{"25:total_pages"},
"slab 25 pagecount changed");
-# Try to move another slab, see that it complains
+# Try to move another slab, see that you can move two in a row
print $sock "slabs reassign 31 25\r\n";
-like(scalar <$sock>, qr/^NOTFULL/, "Cannot re-run against class with empty space");
+like(scalar <$sock>, qr/^OK/, "Cannot re-run against class with empty space");
# Try to move a page backwards. Should complain that source class isn't "safe"
# to move from.
-print $sock "slabs reassign 25 31\r\n";
-like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back");
+# TODO: Wait until the above command completes, then try to move it back?
+# Seems pointless...
+#print $sock "slabs reassign 25 31\r\n";
+#like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back");
# Try to insert items into both slabs
print $sock "set bfoo51 0 0 70000\r\n", $bigdata, "\r\n";
BEGIN {
chdir "$Bin/.." or die;
- my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am);
+ my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
push(@exempted, glob("doc/*.xml"));
push(@exempted, glob("doc/xml2rfc/*.xsl"));
push(@exempted, glob("m4/*backport*m4"));
+ push(@exempted, glob("*.orig"));
my %exempted_hash = map { $_ => 1 } @exempted;
my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`;
use Test::More tests => scalar(@files);
foreach my $f (@files) {
- open(my $fh, $f) or die;
+ open(my $fh, $f) or die "Cannot open file $f: $!";
my $before = do { local $/; <$fh>; };
close ($fh);
my $after = $before;
if (daemon) {
/* loop and wait for the pid file.. There is a potential race
* condition that the server just created the file but isn't
- * finished writing the content, but I'll take the chance....
- */
+ * finished writing the content, so we loop a few times
+ * reading as well */
while (access(pid_file, F_OK) == -1) {
usleep(10);
}
strerror(errno));
assert(false);
}
- assert(fgets(buffer, sizeof(buffer), fp) != NULL);
+
+ /* Avoid race by retrying 20 times */
+ for (int x = 0; x < 20 && fgets(buffer, sizeof(buffer), fp) == NULL; x++) {
+ usleep(10);
+ }
fclose(fp);
int32_t val;
mutex_lock(&atomics_mutex);
(*refcount)++;
res = *refcount;
- pthread_mutex_unlock(&atomics_mutex);
+ mutex_unlock(&atomics_mutex);
return res;
#endif
}
mutex_lock(&atomics_mutex);
(*refcount)--;
res = *refcount;
- pthread_mutex_unlock(&atomics_mutex);
+ mutex_unlock(&atomics_mutex);
return res;
#endif
}
}
void item_unlock(uint32_t hv) {
- pthread_mutex_unlock(&item_locks[hv & item_lock_mask]);
+ mutex_unlock(&item_locks[hv & item_lock_mask]);
}
/*
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
/*
* Processes an incoming "handle a new connection" item. This is called when
* input arrives on the libevent wakeup pipe.
void item_flush_expired() {
mutex_lock(&cache_lock);
do_item_flush_expired();
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
/*
mutex_lock(&cache_lock);
ret = do_item_cachedump(slabs_clsid, limit, bytes);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
return ret;
}
void item_stats(ADD_STAT add_stats, void *c) {
mutex_lock(&cache_lock);
do_item_stats(add_stats, c);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
/*
void item_stats_sizes(ADD_STAT add_stats, void *c) {
mutex_lock(&cache_lock);
do_item_stats_sizes(add_stats, c);
- pthread_mutex_unlock(&cache_lock);
+ mutex_unlock(&cache_lock);
}
/******************************* GLOBAL STATS ******************************/
}
}
-#ifndef __INTEL_COMPILER
-#pragma GCC diagnostic ignored "-Wsign-compare"
-#endif
/*
* Initializes the thread subsystem, creating various worker threads.
*
-#include "memcached.h"
+#include <config.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
+#include "memcached.h"
+
/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
#define xisspace(c) isspace((unsigned char)c)
+++ /dev/null
-m4_define([VERSION_NUMBER], [1.4.13])