[-]
[+]
|
Changed |
memcached.changes
|
|
[-]
[+]
|
Changed |
memcached.spec
^
|
|
|
Deleted |
memcached-1.4.33.tar.bz2
^
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/ChangeLog
^
|
@@ -1,3 +1,7 @@
+2016-08-23
+ * ChangeLog moved from Google Code to Github Wiki, it's now available at:
+ https://github.com/memcached/memcached/wiki/ReleaseNotes
+
2010-10-11
* ChangeLog is no longer being updated.
See http://code.google.com/p/memcached/wiki/ReleaseNotes
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/LICENSE.bipbuffer
^
|
@@ -0,0 +1,24 @@
+Copyright (c) 2011, Willem-Hendrik Thiart
+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 WILLEM-HENDRIK THIART 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.
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/Makefile.am
^
|
@@ -18,7 +18,11 @@
thread.c daemon.c \
stats.c stats.h \
util.c util.h \
- trace.h cache.h sasl_defs.h
+ trace.h cache.h sasl_defs.h \
+ bipbuffer.c bipbuffer.h \
+ logger.c logger.h \
+ crawler.c crawler.h \
+ itoa_ljust.c itoa_ljust.h
if BUILD_CACHE
memcached_SOURCES += cache.c
@@ -71,7 +75,7 @@
SUBDIRS = doc
DIST_DIRS = scripts
-EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4 README.md
+EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4 README.md LICENSE.bipbuffer
MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/Makefile.in
^
|
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -16,7 +16,17 @@
VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
@@ -79,7 +89,6 @@
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-target_triplet = @target@
bin_PROGRAMS = memcached$(EXEEXT)
noinst_PROGRAMS = memcached-debug$(EXEEXT) sizes$(EXEEXT) \
testapp$(EXEEXT) timedrun$(EXEEXT)
@@ -95,16 +104,13 @@
@DTRACE_INSTRUMENT_OBJ_TRUE@am__append_10 = memcached_debug_dtrace.o
@DTRACE_INSTRUMENT_OBJ_TRUE@am__append_11 = memcached_dtrace.o memcached_debug_dtrace.o
subdir = .
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
- $(top_srcdir)/configure $(am__configure_deps) \
- $(srcdir)/config.h.in depcomp $(pkginclude_HEADERS) AUTHORS \
- COPYING ChangeLog NEWS compile config.guess config.sub \
- install-sh missing
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/version.m4 \
$(top_srcdir)/m4/c99-backport.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+ $(am__configure_deps) $(pkginclude_HEADERS) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
@@ -117,7 +123,9 @@
jenkins_hash.c jenkins_hash.h murmur3_hash.c murmur3_hash.h \
slabs.c slabs.h items.c items.h assoc.c assoc.h thread.c \
daemon.c stats.c stats.h util.c util.h trace.h cache.h \
- sasl_defs.h cache.c solaris_priv.c sasl_defs.c
+ sasl_defs.h bipbuffer.c bipbuffer.h logger.c logger.h \
+ crawler.c crawler.h itoa_ljust.c itoa_ljust.h cache.c \
+ solaris_priv.c sasl_defs.c
@BUILD_CACHE_TRUE@am__objects_1 = memcached-cache.$(OBJEXT)
@BUILD_SOLARIS_PRIVS_TRUE@am__objects_2 = \
@BUILD_SOLARIS_PRIVS_TRUE@ memcached-solaris_priv.$(OBJEXT)
@@ -128,13 +136,17 @@
memcached-items.$(OBJEXT) memcached-assoc.$(OBJEXT) \
memcached-thread.$(OBJEXT) memcached-daemon.$(OBJEXT) \
memcached-stats.$(OBJEXT) memcached-util.$(OBJEXT) \
+ memcached-bipbuffer.$(OBJEXT) memcached-logger.$(OBJEXT) \
+ memcached-crawler.$(OBJEXT) memcached-itoa_ljust.$(OBJEXT) \
$(am__objects_1) $(am__objects_2) $(am__objects_3)
memcached_OBJECTS = $(am_memcached_OBJECTS)
am__memcached_debug_SOURCES_DIST = memcached.c memcached.h hash.c \
hash.h jenkins_hash.c jenkins_hash.h murmur3_hash.c \
murmur3_hash.h slabs.c slabs.h items.c items.h assoc.c assoc.h \
thread.c daemon.c stats.c stats.h util.c util.h trace.h \
- cache.h sasl_defs.h cache.c solaris_priv.c sasl_defs.c
+ cache.h sasl_defs.h bipbuffer.c bipbuffer.h logger.c logger.h \
+ crawler.c crawler.h itoa_ljust.c itoa_ljust.h cache.c \
+ solaris_priv.c sasl_defs.c
@BUILD_CACHE_TRUE@am__objects_4 = memcached_debug-cache.$(OBJEXT)
@BUILD_SOLARIS_PRIVS_TRUE@am__objects_5 = memcached_debug-solaris_priv.$(OBJEXT)
@ENABLE_SASL_TRUE@am__objects_6 = memcached_debug-sasl_defs.$(OBJEXT)
@@ -148,7 +160,11 @@
memcached_debug-thread.$(OBJEXT) \
memcached_debug-daemon.$(OBJEXT) \
memcached_debug-stats.$(OBJEXT) memcached_debug-util.$(OBJEXT) \
- $(am__objects_4) $(am__objects_5) $(am__objects_6)
+ memcached_debug-bipbuffer.$(OBJEXT) \
+ memcached_debug-logger.$(OBJEXT) \
+ memcached_debug-crawler.$(OBJEXT) \
+ memcached_debug-itoa_ljust.$(OBJEXT) $(am__objects_4) \
+ $(am__objects_5) $(am__objects_6)
am_memcached_debug_OBJECTS = $(am__objects_7)
memcached_debug_OBJECTS = $(am_memcached_debug_OBJECTS)
memcached_debug_LINK = $(CCLD) $(memcached_debug_CFLAGS) $(CFLAGS) \
@@ -272,6 +288,9 @@
CTAGS = ctags
CSCOPE = cscope
DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in AUTHORS \
+ COPYING ChangeLog NEWS compile config.guess config.sub depcomp \
+ install-sh missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
@@ -409,15 +428,12 @@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
-target = @target@
target_alias = @target_alias@
-target_cpu = @target_cpu@
-target_os = @target_os@
-target_vendor = @target_vendor@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
@@ -429,7 +445,9 @@
jenkins_hash.c jenkins_hash.h murmur3_hash.c murmur3_hash.h \
slabs.c slabs.h items.c items.h assoc.c assoc.h thread.c \
daemon.c stats.c stats.h util.c util.h trace.h cache.h \
- sasl_defs.h $(am__append_1) $(am__append_3) $(am__append_4)
+ sasl_defs.h bipbuffer.c bipbuffer.h logger.c logger.h \
+ crawler.c crawler.h itoa_ljust.c itoa_ljust.h $(am__append_1) \
+ $(am__append_3) $(am__append_4)
memcached_debug_SOURCES = $(memcached_SOURCES)
memcached_CPPFLAGS = -DNDEBUG
memcached_debug_LDADD = @PROFILER_LDFLAGS@ $(am__append_9)
@@ -440,7 +458,7 @@
CLEANFILES = $(am__append_6) $(am__append_11)
SUBDIRS = doc
DIST_DIRS = scripts
-EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4 README.md
+EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4 README.md LICENSE.bipbuffer
MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov
all: $(BUILT_SOURCES) config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
@@ -462,7 +480,6 @@
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -570,11 +587,15 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-assoc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-bipbuffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-cache.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-crawler.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-daemon.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-items.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-itoa_ljust.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-jenkins_hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-logger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-memcached.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-murmur3_hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-sasl_defs.Po@am__quote@
@@ -584,11 +605,15 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached-util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-assoc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-bipbuffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-cache.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-crawler.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-daemon.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-items.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-itoa_ljust.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-jenkins_hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-logger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-memcached.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-murmur3_hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcached_debug-sasl_defs.Po@am__quote@
@@ -770,6 +795,62 @@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi`
+memcached-bipbuffer.o: bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-bipbuffer.o -MD -MP -MF $(DEPDIR)/memcached-bipbuffer.Tpo -c -o memcached-bipbuffer.o `test -f 'bipbuffer.c' || echo '$(srcdir)/'`bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-bipbuffer.Tpo $(DEPDIR)/memcached-bipbuffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bipbuffer.c' object='memcached-bipbuffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-bipbuffer.o `test -f 'bipbuffer.c' || echo '$(srcdir)/'`bipbuffer.c
+
+memcached-bipbuffer.obj: bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-bipbuffer.obj -MD -MP -MF $(DEPDIR)/memcached-bipbuffer.Tpo -c -o memcached-bipbuffer.obj `if test -f 'bipbuffer.c'; then $(CYGPATH_W) 'bipbuffer.c'; else $(CYGPATH_W) '$(srcdir)/bipbuffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-bipbuffer.Tpo $(DEPDIR)/memcached-bipbuffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bipbuffer.c' object='memcached-bipbuffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-bipbuffer.obj `if test -f 'bipbuffer.c'; then $(CYGPATH_W) 'bipbuffer.c'; else $(CYGPATH_W) '$(srcdir)/bipbuffer.c'; fi`
+
+memcached-logger.o: logger.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-logger.o -MD -MP -MF $(DEPDIR)/memcached-logger.Tpo -c -o memcached-logger.o `test -f 'logger.c' || echo '$(srcdir)/'`logger.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-logger.Tpo $(DEPDIR)/memcached-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logger.c' object='memcached-logger.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-logger.o `test -f 'logger.c' || echo '$(srcdir)/'`logger.c
+
+memcached-logger.obj: logger.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-logger.obj -MD -MP -MF $(DEPDIR)/memcached-logger.Tpo -c -o memcached-logger.obj `if test -f 'logger.c'; then $(CYGPATH_W) 'logger.c'; else $(CYGPATH_W) '$(srcdir)/logger.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-logger.Tpo $(DEPDIR)/memcached-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logger.c' object='memcached-logger.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-logger.obj `if test -f 'logger.c'; then $(CYGPATH_W) 'logger.c'; else $(CYGPATH_W) '$(srcdir)/logger.c'; fi`
+
+memcached-crawler.o: crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-crawler.o -MD -MP -MF $(DEPDIR)/memcached-crawler.Tpo -c -o memcached-crawler.o `test -f 'crawler.c' || echo '$(srcdir)/'`crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-crawler.Tpo $(DEPDIR)/memcached-crawler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crawler.c' object='memcached-crawler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-crawler.o `test -f 'crawler.c' || echo '$(srcdir)/'`crawler.c
+
+memcached-crawler.obj: crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-crawler.obj -MD -MP -MF $(DEPDIR)/memcached-crawler.Tpo -c -o memcached-crawler.obj `if test -f 'crawler.c'; then $(CYGPATH_W) 'crawler.c'; else $(CYGPATH_W) '$(srcdir)/crawler.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-crawler.Tpo $(DEPDIR)/memcached-crawler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crawler.c' object='memcached-crawler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-crawler.obj `if test -f 'crawler.c'; then $(CYGPATH_W) 'crawler.c'; else $(CYGPATH_W) '$(srcdir)/crawler.c'; fi`
+
+memcached-itoa_ljust.o: itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-itoa_ljust.o -MD -MP -MF $(DEPDIR)/memcached-itoa_ljust.Tpo -c -o memcached-itoa_ljust.o `test -f 'itoa_ljust.c' || echo '$(srcdir)/'`itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-itoa_ljust.Tpo $(DEPDIR)/memcached-itoa_ljust.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='itoa_ljust.c' object='memcached-itoa_ljust.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-itoa_ljust.o `test -f 'itoa_ljust.c' || echo '$(srcdir)/'`itoa_ljust.c
+
+memcached-itoa_ljust.obj: itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-itoa_ljust.obj -MD -MP -MF $(DEPDIR)/memcached-itoa_ljust.Tpo -c -o memcached-itoa_ljust.obj `if test -f 'itoa_ljust.c'; then $(CYGPATH_W) 'itoa_ljust.c'; else $(CYGPATH_W) '$(srcdir)/itoa_ljust.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-itoa_ljust.Tpo $(DEPDIR)/memcached-itoa_ljust.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='itoa_ljust.c' object='memcached-itoa_ljust.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o memcached-itoa_ljust.obj `if test -f 'itoa_ljust.c'; then $(CYGPATH_W) 'itoa_ljust.c'; else $(CYGPATH_W) '$(srcdir)/itoa_ljust.c'; fi`
+
memcached-cache.o: cache.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(memcached_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT memcached-cache.o -MD -MP -MF $(DEPDIR)/memcached-cache.Tpo -c -o memcached-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached-cache.Tpo $(DEPDIR)/memcached-cache.Po
@@ -966,6 +1047,62 @@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi`
+memcached_debug-bipbuffer.o: bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-bipbuffer.o -MD -MP -MF $(DEPDIR)/memcached_debug-bipbuffer.Tpo -c -o memcached_debug-bipbuffer.o `test -f 'bipbuffer.c' || echo '$(srcdir)/'`bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-bipbuffer.Tpo $(DEPDIR)/memcached_debug-bipbuffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bipbuffer.c' object='memcached_debug-bipbuffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-bipbuffer.o `test -f 'bipbuffer.c' || echo '$(srcdir)/'`bipbuffer.c
+
+memcached_debug-bipbuffer.obj: bipbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-bipbuffer.obj -MD -MP -MF $(DEPDIR)/memcached_debug-bipbuffer.Tpo -c -o memcached_debug-bipbuffer.obj `if test -f 'bipbuffer.c'; then $(CYGPATH_W) 'bipbuffer.c'; else $(CYGPATH_W) '$(srcdir)/bipbuffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-bipbuffer.Tpo $(DEPDIR)/memcached_debug-bipbuffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bipbuffer.c' object='memcached_debug-bipbuffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-bipbuffer.obj `if test -f 'bipbuffer.c'; then $(CYGPATH_W) 'bipbuffer.c'; else $(CYGPATH_W) '$(srcdir)/bipbuffer.c'; fi`
+
+memcached_debug-logger.o: logger.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-logger.o -MD -MP -MF $(DEPDIR)/memcached_debug-logger.Tpo -c -o memcached_debug-logger.o `test -f 'logger.c' || echo '$(srcdir)/'`logger.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-logger.Tpo $(DEPDIR)/memcached_debug-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logger.c' object='memcached_debug-logger.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-logger.o `test -f 'logger.c' || echo '$(srcdir)/'`logger.c
+
+memcached_debug-logger.obj: logger.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-logger.obj -MD -MP -MF $(DEPDIR)/memcached_debug-logger.Tpo -c -o memcached_debug-logger.obj `if test -f 'logger.c'; then $(CYGPATH_W) 'logger.c'; else $(CYGPATH_W) '$(srcdir)/logger.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-logger.Tpo $(DEPDIR)/memcached_debug-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logger.c' object='memcached_debug-logger.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-logger.obj `if test -f 'logger.c'; then $(CYGPATH_W) 'logger.c'; else $(CYGPATH_W) '$(srcdir)/logger.c'; fi`
+
+memcached_debug-crawler.o: crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-crawler.o -MD -MP -MF $(DEPDIR)/memcached_debug-crawler.Tpo -c -o memcached_debug-crawler.o `test -f 'crawler.c' || echo '$(srcdir)/'`crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-crawler.Tpo $(DEPDIR)/memcached_debug-crawler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crawler.c' object='memcached_debug-crawler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-crawler.o `test -f 'crawler.c' || echo '$(srcdir)/'`crawler.c
+
+memcached_debug-crawler.obj: crawler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-crawler.obj -MD -MP -MF $(DEPDIR)/memcached_debug-crawler.Tpo -c -o memcached_debug-crawler.obj `if test -f 'crawler.c'; then $(CYGPATH_W) 'crawler.c'; else $(CYGPATH_W) '$(srcdir)/crawler.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-crawler.Tpo $(DEPDIR)/memcached_debug-crawler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crawler.c' object='memcached_debug-crawler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-crawler.obj `if test -f 'crawler.c'; then $(CYGPATH_W) 'crawler.c'; else $(CYGPATH_W) '$(srcdir)/crawler.c'; fi`
+
+memcached_debug-itoa_ljust.o: itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-itoa_ljust.o -MD -MP -MF $(DEPDIR)/memcached_debug-itoa_ljust.Tpo -c -o memcached_debug-itoa_ljust.o `test -f 'itoa_ljust.c' || echo '$(srcdir)/'`itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-itoa_ljust.Tpo $(DEPDIR)/memcached_debug-itoa_ljust.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='itoa_ljust.c' object='memcached_debug-itoa_ljust.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-itoa_ljust.o `test -f 'itoa_ljust.c' || echo '$(srcdir)/'`itoa_ljust.c
+
+memcached_debug-itoa_ljust.obj: itoa_ljust.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-itoa_ljust.obj -MD -MP -MF $(DEPDIR)/memcached_debug-itoa_ljust.Tpo -c -o memcached_debug-itoa_ljust.obj `if test -f 'itoa_ljust.c'; then $(CYGPATH_W) 'itoa_ljust.c'; else $(CYGPATH_W) '$(srcdir)/itoa_ljust.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-itoa_ljust.Tpo $(DEPDIR)/memcached_debug-itoa_ljust.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='itoa_ljust.c' object='memcached_debug-itoa_ljust.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -c -o memcached_debug-itoa_ljust.obj `if test -f 'itoa_ljust.c'; then $(CYGPATH_W) 'itoa_ljust.c'; else $(CYGPATH_W) '$(srcdir)/itoa_ljust.c'; fi`
+
memcached_debug-cache.o: cache.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(memcached_debug_CFLAGS) $(CFLAGS) -MT memcached_debug-cache.o -MD -MP -MF $(DEPDIR)/memcached_debug-cache.Tpo -c -o memcached_debug-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/memcached_debug-cache.Tpo $(DEPDIR)/memcached_debug-cache.Po
@@ -1219,15 +1356,15 @@
$(am__post_remove_distdir)
dist-tarZ: distdir
- @echo WARNING: "Support for shar distribution archives is" \
- "deprecated." >&2
+ @echo WARNING: "Support for distribution archives compressed with" \
+ "legacy program 'compress' is deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__post_remove_distdir)
dist-shar: distdir
- @echo WARNING: "Support for distribution archives compressed with" \
- "legacy program 'compress' is deprecated." >&2
+ @echo WARNING: "Support for shar distribution archives is" \
+ "deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
$(am__post_remove_distdir)
@@ -1263,17 +1400,17 @@
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
- mkdir $(distdir)/_build $(distdir)/_inst
+ mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
- && $(am__cd) $(distdir)/_build \
- && ../configure \
+ && $(am__cd) $(distdir)/_build/sub \
+ && ../../configure \
$(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
- --srcdir=.. --prefix="$$dc_install_base" \
+ --srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
@@ -1465,6 +1602,8 @@
mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-binPROGRAMS uninstall-pkgincludeHEADERS
+.PRECIOUS: Makefile
+
memcached_dtrace.h: memcached_dtrace.d
${DTRACE} -h -s memcached_dtrace.d
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/README.md
^
|
@@ -1,28 +1,27 @@
# Memcached
-## Dependencies
-
-* libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
+Memcached is a high performance multithreaded event-based key/value cache
+store intended to be used in a distributed system.
-## Environment
+See: https://memcached.org/about
-### Linux
+A fun story explaining usage: https://memcached.org/tutorial
-If using Linux, you need a kernel with epoll. Sure, libevent will
-work with normal select, but it sucks.
+If you're having trouble, try the wiki: https://memcached.org/wiki
-epoll isn't in Linux 2.4, but there's a backport at:
+If you're trying to troubleshoot odd behavior or timeouts, see:
+https://memcached.org/timeouts
- http://www.xmailserver.org/linux-patches/nio-improve.html
+https://memcached.org/ is a good resource in general. Please use the mailing
+list to ask questions, github issues aren't seen by everyone!
-You want the epoll-lt patch (level-triggered).
+## Dependencies
-### Mac OS X
+* libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
-If you're using MacOS, you'll want libevent 1.1 or higher to deal with
-a kqueue bug.
+## Environment
-Also, be warned that the -k (mlockall) option to memcached might be
+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)
@@ -33,6 +32,4 @@
## Contributing
-Want to contribute? Up-to-date pointers should be at:
-
-* http://contributing.appspot.com/memcached
+See https://github.com/memcached/memcached/wiki/DevelopmentRepos
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/aclocal.m4
^
|
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.15 -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -20,7 +20,7 @@
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
-# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -32,10 +32,10 @@
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.14'
+[am__api_version='1.15'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.14.1], [],
+m4_if([$1], [1.15], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -51,14 +51,14 @@
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.14.1])dnl
+[AM_AUTOMAKE_VERSION([1.15])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -103,15 +103,14 @@
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
-[dnl Rely on autoconf to set up CDPATH properly.
-AC_PREREQ([2.50])dnl
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -142,7 +141,7 @@
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -333,7 +332,7 @@
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -409,7 +408,7 @@
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -499,8 +498,8 @@
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
@@ -573,7 +572,11 @@
END
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
fi
-fi])
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
@@ -602,7 +605,7 @@
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -613,7 +616,7 @@
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -623,7 +626,7 @@
fi
AC_SUBST([install_sh])])
-# Copyright (C) 2003-2013 Free Software Foundation, Inc.
+# Copyright (C) 2003-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -644,7 +647,7 @@
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -694,7 +697,7 @@
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -735,7 +738,7 @@
# Obsolete and "removed" macros, that must however still report explicit
# error messages when used, to smooth transition.
#
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -762,7 +765,7 @@
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -791,7 +794,7 @@
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -838,7 +841,7 @@
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -857,7 +860,7 @@
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -938,7 +941,7 @@
rm -f conftest.file
])
-# Copyright (C) 2009-2013 Free Software Foundation, Inc.
+# Copyright (C) 2009-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -998,7 +1001,7 @@
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1026,7 +1029,7 @@
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2013 Free Software Foundation, Inc.
+# Copyright (C) 2006-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1045,7 +1048,7 @@
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2013 Free Software Foundation, Inc.
+# Copyright (C) 2004-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/assoc.c
^
|
@@ -14,8 +14,8 @@
#include "memcached.h"
#include <sys/stat.h>
#include <sys/socket.h>
-#include <sys/signal.h>
#include <sys/resource.h>
+#include <signal.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
@@ -70,8 +70,8 @@
exit(EXIT_FAILURE);
}
STATS_LOCK();
- stats.hash_power_level = hashpower;
- stats.hash_bytes = hashsize(hashpower) * sizeof(void *);
+ stats_state.hash_power_level = hashpower;
+ stats_state.hash_bytes = hashsize(hashpower) * sizeof(void *);
STATS_UNLOCK();
}
@@ -134,9 +134,9 @@
expanding = true;
expand_bucket = 0;
STATS_LOCK();
- stats.hash_power_level = hashpower;
- stats.hash_bytes += hashsize(hashpower) * sizeof(void *);
- stats.hash_is_expanding = 1;
+ stats_state.hash_power_level = hashpower;
+ stats_state.hash_bytes += hashsize(hashpower) * sizeof(void *);
+ stats_state.hash_is_expanding = true;
STATS_UNLOCK();
} else {
primary_hashtable = old_hashtable;
@@ -238,8 +238,8 @@
expanding = false;
free(old_hashtable);
STATS_LOCK();
- stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *);
- stats.hash_is_expanding = 0;
+ stats_state.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *);
+ stats_state.hash_is_expanding = false;
STATS_UNLOCK();
if (settings.verbose > 1)
fprintf(stderr, "Hash table expansion done\n");
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/bipbuffer.c
^
|
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2011, Willem-Hendrik Thiart
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE.bipbuffer file.
+ *
+ * @file
+ * @author Willem Thiart himself@willemthiart.com
+ */
+
+#include "stdio.h"
+#include <stdlib.h>
+
+/* for memcpy */
+#include <string.h>
+
+#include "bipbuffer.h"
+
+static size_t bipbuf_sizeof(const unsigned int size)
+{
+ return sizeof(bipbuf_t) + size;
+}
+
+int bipbuf_unused(const bipbuf_t* me)
+{
+ if (1 == me->b_inuse)
+ /* distance between region B and region A */
+ return me->a_start - me->b_end;
+ else
+ return me->size - me->a_end;
+}
+
+int bipbuf_size(const bipbuf_t* me)
+{
+ return me->size;
+}
+
+int bipbuf_used(const bipbuf_t* me)
+{
+ return (me->a_end - me->a_start) + me->b_end;
+}
+
+void bipbuf_init(bipbuf_t* me, const unsigned int size)
+{
+ me->a_start = me->a_end = me->b_end = 0;
+ me->size = size;
+ me->b_inuse = 0;
+}
+
+bipbuf_t *bipbuf_new(const unsigned int size)
+{
+ bipbuf_t *me = malloc(bipbuf_sizeof(size));
+ if (!me)
+ return NULL;
+ bipbuf_init(me, size);
+ return me;
+}
+
+void bipbuf_free(bipbuf_t* me)
+{
+ free(me);
+}
+
+int bipbuf_is_empty(const bipbuf_t* me)
+{
+ return me->a_start == me->a_end;
+}
+
+/* find out if we should turn on region B
+ * ie. is the distance from A to buffer's end less than B to A? */
+static void __check_for_switch_to_b(bipbuf_t* me)
+{
+ if (me->size - me->a_end < me->a_start - me->b_end)
+ me->b_inuse = 1;
+}
+
+/* TODO: DOCUMENT THESE TWO FUNCTIONS */
+unsigned char *bipbuf_request(bipbuf_t* me, const int size)
+{
+ if (bipbuf_unused(me) < size)
+ return 0;
+ if (1 == me->b_inuse)
+ {
+ return (unsigned char *)me->data + me->b_end;
+ }
+ else
+ {
+ return (unsigned char *)me->data + me->a_end;
+ }
+}
+
+int bipbuf_push(bipbuf_t* me, const int size)
+{
+ if (bipbuf_unused(me) < size)
+ return 0;
+
+ if (1 == me->b_inuse)
+ {
+ me->b_end += size;
+ }
+ else
+ {
+ me->a_end += size;
+ }
+
+ __check_for_switch_to_b(me);
+ return size;
+}
+
+int bipbuf_offer(bipbuf_t* me, const unsigned char *data, const int size)
+{
+ /* not enough space */
+ if (bipbuf_unused(me) < size)
+ return 0;
+
+ if (1 == me->b_inuse)
+ {
+ memcpy(me->data + me->b_end, data, size);
+ me->b_end += size;
+ }
+ else
+ {
+ memcpy(me->data + me->a_end, data, size);
+ me->a_end += size;
+ }
+
+ __check_for_switch_to_b(me);
+ return size;
+}
+
+unsigned char *bipbuf_peek(const bipbuf_t* me, const unsigned int size)
+{
+ /* make sure we can actually peek at this data */
+ if (me->size < me->a_start + size)
+ return NULL;
+
+ if (bipbuf_is_empty(me))
+ return NULL;
+
+ return (unsigned char *)me->data + me->a_start;
+}
+
+unsigned char *bipbuf_peek_all(const bipbuf_t* me, unsigned int *size)
+{
+ if (bipbuf_is_empty(me))
+ return NULL;
+
+ *size = me->a_end - me->a_start;
+ return (unsigned char*)me->data + me->a_start;
+}
+
+unsigned char *bipbuf_poll(bipbuf_t* me, const unsigned int size)
+{
+ if (bipbuf_is_empty(me))
+ return NULL;
+
+ /* make sure we can actually poll this data */
+ if (me->size < me->a_start + size)
+ return NULL;
+
+ void *end = me->data + me->a_start;
+ me->a_start += size;
+
+ /* we seem to be empty.. */
+ if (me->a_start == me->a_end)
+ {
+ /* replace a with region b */
+ if (1 == me->b_inuse)
+ {
+ me->a_start = 0;
+ me->a_end = me->b_end;
+ me->b_end = me->b_inuse = 0;
+ }
+ else
+ /* safely move cursor back to the start because we are empty */
+ me->a_start = me->a_end = 0;
+ }
+
+ __check_for_switch_to_b(me);
+ return end;
+}
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/bipbuffer.h
^
|
@@ -0,0 +1,87 @@
+#ifndef BIPBUFFER_H
+#define BIPBUFFER_H
+
+typedef struct
+{
+ unsigned long int size;
+
+ /* region A */
+ unsigned int a_start, a_end;
+
+ /* region B */
+ unsigned int b_end;
+
+ /* is B inuse? */
+ int b_inuse;
+
+ unsigned char data[];
+} bipbuf_t;
+
+/**
+ * Create a new bip buffer.
+ *
+ * malloc()s space
+ *
+ * @param[in] size The size of the buffer */
+bipbuf_t *bipbuf_new(const unsigned int size);
+
+/**
+ * Initialise a bip buffer. Use memory provided by user.
+ *
+ * No malloc()s are performed.
+ *
+ * @param[in] size The size of the array */
+void bipbuf_init(bipbuf_t* me, const unsigned int size);
+
+/**
+ * Free the bip buffer */
+void bipbuf_free(bipbuf_t *me);
+
+/* TODO: DOCUMENTATION */
+unsigned char *bipbuf_request(bipbuf_t* me, const int size);
+int bipbuf_push(bipbuf_t* me, const int size);
+
+/**
+ * @param[in] data The data to be offered to the buffer
+ * @param[in] size The size of the data to be offered
+ * @return number of bytes offered */
+int bipbuf_offer(bipbuf_t *me, const unsigned char *data, const int size);
+
+/**
+ * Look at data. Don't move cursor
+ *
+ * @param[in] len The length of the data to be peeked
+ * @return data on success, NULL if we can't peek at this much data */
+unsigned char *bipbuf_peek(const bipbuf_t* me, const unsigned int len);
+
+/**
+ * Look at data. Don't move cursor
+ *
+ * @param[in] len The length of the data returned
+ * @return data on success, NULL if nothing available */
+unsigned char *bipbuf_peek_all(const bipbuf_t* me, unsigned int *len);
+
+/**
+ * Get pointer to data to read. Move the cursor on.
+ *
+ * @param[in] len The length of the data to be polled
+ * @return pointer to data, NULL if we can't poll this much data */
+unsigned char *bipbuf_poll(bipbuf_t* me, const unsigned int size);
+
+/**
+ * @return the size of the bipbuffer */
+int bipbuf_size(const bipbuf_t* me);
+
+/**
+ * @return 1 if buffer is empty; 0 otherwise */
+int bipbuf_is_empty(const bipbuf_t* me);
+
+/**
+ * @return how much space we have assigned */
+int bipbuf_used(const bipbuf_t* cb);
+
+/**
+ * @return bytes of unused space */
+int bipbuf_unused(const bipbuf_t* me);
+
+#endif /* BIPBUFFER_H */
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/cache.c
^
|
@@ -70,8 +70,15 @@
void* cache_alloc(cache_t *cache) {
void *ret;
- void *object;
pthread_mutex_lock(&cache->mutex);
+ ret = do_cache_alloc(cache);
+ pthread_mutex_unlock(&cache->mutex);
+ return ret;
+}
+
+void* do_cache_alloc(cache_t *cache) {
+ void *ret;
+ void *object;
if (cache->freecurr > 0) {
ret = cache->ptr[--cache->freecurr];
object = get_object(ret);
@@ -87,7 +94,6 @@
}
}
}
- pthread_mutex_unlock(&cache->mutex);
#ifndef NDEBUG
if (object != NULL) {
@@ -105,14 +111,17 @@
void cache_free(cache_t *cache, void *ptr) {
pthread_mutex_lock(&cache->mutex);
+ do_cache_free(cache, ptr);
+ pthread_mutex_unlock(&cache->mutex);
+}
+void do_cache_free(cache_t *cache, void *ptr) {
#ifndef NDEBUG
/* validate redzone... */
if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
&redzone_pattern, sizeof(redzone_pattern)) != 0) {
raise(SIGABRT);
cache_error = 1;
- pthread_mutex_unlock(&cache->mutex);
return;
}
uint64_t *pre = ptr;
@@ -120,7 +129,6 @@
if (*pre != redzone_pattern) {
raise(SIGABRT);
cache_error = -1;
- pthread_mutex_unlock(&cache->mutex);
return;
}
ptr = pre;
@@ -143,6 +151,5 @@
}
}
- pthread_mutex_unlock(&cache->mutex);
}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/cache.h
^
|
@@ -31,7 +31,7 @@
* Destructor used to clean up allocated objects before they are
* returned to the operating system.
*
- * @param obj pointer to the object to initialized.
+ * @param obj pointer to the object to clean up.
* @param notused1 This parameter is currently not used.
* @param notused2 This parameter is currently not used.
* @return you should return 0, but currently this is not checked
@@ -67,7 +67,7 @@
*
* The object cache will let you allocate objects of the same size. It is fully
* MT safe, so you may allocate objects from multiple threads without having to
- * do any syncrhonization in the application code.
+ * do any synchronization in the application code.
*
* @param name the name of the object cache. This name may be used for debug purposes
* and may help you track down what kind of object you have problems with
@@ -101,6 +101,7 @@
* the allocation cannot be satisfied.
*/
void* cache_alloc(cache_t* handle);
+void* do_cache_alloc(cache_t* handle);
/**
* Return an object back to the cache.
*
@@ -111,6 +112,7 @@
* @param ptr pointer to the object to return.
*/
void cache_free(cache_t* handle, void* ptr);
+void do_cache_free(cache_t* handle, void* ptr);
#endif
#endif
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/compile
^
|
@@ -3,7 +3,7 @@
scriptversion=2012-10-14.11; # UTC
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/config.guess
^
|
@@ -1,8 +1,8 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2013 Free Software Foundation, Inc.
+# Copyright 1992-2015 Free Software Foundation, Inc.
-timestamp='2013-06-10'
+timestamp='2015-08-20'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -24,12 +24,12 @@
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -50,7 +50,7 @@
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -149,7 +149,7 @@
LIBC=gnu
#endif
EOF
- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
;;
esac
@@ -168,20 +168,27 @@
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
sysctl="sysctl -n hw.machine_arch"
- UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
- /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ /sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || \
+ echo unknown)`
case "${UNAME_MACHINE_ARCH}" in
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine=${arch}${endian}-unknown
+ ;;
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently, or will in the future.
case "${UNAME_MACHINE_ARCH}" in
- arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
eval $set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
@@ -197,6 +204,13 @@
os=netbsd
;;
esac
+ # Determine ABI tags.
+ case "${UNAME_MACHINE_ARCH}" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+ ;;
+ esac
# The OS release
# Debian GNU/NetBSD machines have a different userland, and
# thus, need a distinct triplet. However, they do not need
@@ -207,13 +221,13 @@
release='-gnu'
;;
*)
- release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
- echo "${machine}-${os}${release}"
+ echo "${machine}-${os}${release}${abi}"
exit ;;
*:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
@@ -235,6 +249,9 @@
*:MirBSD:*:*)
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
exit ;;
+ *:Sortix:*:*)
+ echo ${UNAME_MACHINE}-unknown-sortix
+ exit ;;
alpha:OSF1:*:*)
case $UNAME_RELEASE in
*4.0)
@@ -579,8 +596,9 @@
else
IBM_ARCH=powerpc
fi
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
fi
@@ -826,7 +844,7 @@
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
- i*:MSYS*:*)
+ *:MSYS*:*)
echo ${UNAME_MACHINE}-pc-msys
exit ;;
i*:windows32*:*)
@@ -932,6 +950,9 @@
crisv32:Linux:*:*)
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
exit ;;
+ e2k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
frv:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
@@ -969,10 +990,10 @@
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
;;
- or1k:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-${LIBC}
exit ;;
- or32:Linux:*:*)
+ or32:Linux:*:* | or1k*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
padre:Linux:*:*)
@@ -1020,7 +1041,7 @@
echo ${UNAME_MACHINE}-dec-linux-${LIBC}
exit ;;
x86_64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo ${UNAME_MACHINE}-pc-linux-${LIBC}
exit ;;
xtensa*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
@@ -1260,16 +1281,26 @@
if test "$UNAME_PROCESSOR" = unknown ; then
UNAME_PROCESSOR=powerpc
fi
- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
- if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
- grep IS_64BIT_ARCH >/dev/null
- then
- case $UNAME_PROCESSOR in
- i386) UNAME_PROCESSOR=x86_64 ;;
- powerpc) UNAME_PROCESSOR=powerpc64 ;;
- esac
+ if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # Avoid executing cc on OS X 10.9, as it ships with a stub
+ # that puts up a graphical alert prompting to install
+ # developer tools. Any system running Mac OS X 10.7 or
+ # later (Darwin 11 and later) is required to have a 64-bit
+ # processor. This is not true of the ARM version of Darwin
+ # that Apple uses in portable devices.
+ UNAME_PROCESSOR=x86_64
fi
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
exit ;;
@@ -1361,154 +1392,6 @@
exit ;;
esac
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
- /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
- I don't know.... */
- printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
- printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
- "4"
-#else
- ""
-#endif
- ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
- printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
- printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
- int version;
- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
- if (version < 4)
- printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
- else
- printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
- exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
- printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
- printf ("ns32k-encore-mach\n"); exit (0);
-#else
- printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
- printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
- printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
- printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
- struct utsname un;
-
- uname(&un);
-
- if (strncmp(un.version, "V2", 2) == 0) {
- printf ("i386-sequent-ptx2\n"); exit (0);
- }
- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
- printf ("i386-sequent-ptx1\n"); exit (0);
- }
- printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-# include <sys/param.h>
-# if defined (BSD)
-# if BSD == 43
- printf ("vax-dec-bsd4.3\n"); exit (0);
-# else
-# if BSD == 199006
- printf ("vax-dec-bsd4.3reno\n"); exit (0);
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# endif
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# else
- printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
- printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
- exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
- { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
- case `getsysinfo -f cpu_type` in
- c1*)
- echo c1-convex-bsd
- exit ;;
- c2*)
- if getsysinfo -f scalar_acc
- then echo c32-convex-bsd
- else echo c2-convex-bsd
- fi
- exit ;;
- c34*)
- echo c34-convex-bsd
- exit ;;
- c38*)
- echo c38-convex-bsd
- exit ;;
- c4*)
- echo c4-convex-bsd
- exit ;;
- esac
-fi
-
cat >&2 <<EOF
$0: unable to guess system type
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/config.h.in
^
|
@@ -24,6 +24,9 @@
/* Define this if you have an implementation of drop_privileges() */
#undef HAVE_DROP_PRIVILEGES
+/* GCC 64bit Atomics available */
+#undef HAVE_GCC_64ATOMICS
+
/* GCC Atomics available */
#undef HAVE_GCC_ATOMICS
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/config.sub
^
|
@@ -1,8 +1,8 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2013 Free Software Foundation, Inc.
+# Copyright 1992-2015 Free Software Foundation, Inc.
-timestamp='2013-08-10'
+timestamp='2015-08-20'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
@@ -68,7 +68,7 @@
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -117,7 +117,7 @@
case $maybe_os in
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
- knetbsd*-gnu* | netbsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
@@ -255,16 +255,18 @@
| arc | arceb \
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
| avr | avr32 \
+ | ba \
| be32 | be64 \
| bfin \
| c4x | c8051 | clipper \
| d10v | d30v | dlx | dsp16xx \
- | epiphany \
- | fido | fr30 | frv \
+ | e2k | epiphany \
+ | fido | fr30 | frv | ft32 \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | k1om \
| le32 | le64 \
| lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
@@ -282,8 +284,10 @@
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipsr5900 | mipsr5900el \
@@ -295,14 +299,14 @@
| nds32 | nds32le | nds32be \
| nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | open8 \
- | or1k | or32 \
+ | open8 | or1k | or1knd | or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | riscv32 | riscv64 \
| rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
@@ -310,6 +314,7 @@
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
| ubicom32 \
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | visium \
| we32k \
| x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
@@ -324,7 +329,10 @@
c6x)
basic_machine=tic6x-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+ leon|leon[3-9])
+ basic_machine=sparc-$basic_machine
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -369,18 +377,20 @@
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* | avr32-* \
+ | ba-* \
| be32-* | be64-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* \
| c8051-* | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
- | elxsi-* \
+ | e2k-* | elxsi-* \
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
| hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | k1om-* \
| le32-* | le64-* \
| lm32-* \
| m32c-* | m32r-* | m32rle-* \
@@ -400,8 +410,10 @@
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa32r6-* | mipsisa32r6el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64r6-* | mipsisa64r6el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
| mipsr5900-* | mipsr5900el-* \
@@ -413,16 +425,18 @@
| nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| open8-* \
+ | or1k*-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
+ | riscv32-* | riscv64-* \
| rl78-* | romp-* | rs6000-* | rx-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
| sparclite-* \
- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
| tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
| tile*-* \
@@ -430,6 +444,7 @@
| ubicom32-* \
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
| vax-* \
+ | visium-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
@@ -506,6 +521,9 @@
basic_machine=i386-pc
os=-aros
;;
+ asmjs)
+ basic_machine=asmjs-unknown
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -767,6 +785,9 @@
basic_machine=m68k-isi
os=-sysv
;;
+ leon-*|leon[3-9]-*)
+ basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+ ;;
m68knommu)
basic_machine=m68k-unknown
os=-linux
@@ -822,6 +843,10 @@
basic_machine=powerpc-unknown
os=-morphos
;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=-moxiebox
+ ;;
msdos)
basic_machine=i386-pc
os=-msdos
@@ -1354,7 +1379,7 @@
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
| -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* | -aros* \
+ | -aos* | -aros* | -cloudabi* | -sortix* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1367,14 +1392,14 @@
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1592,9 +1617,6 @@
mips*-*)
os=-elf
;;
- or1k-*)
- os=-elf
- ;;
or32-*)
os=-coff
;;
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/configure
^
|
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for memcached 1.4.24.
+# Generated by GNU Autoconf 2.69 for memcached 1.4.36.
#
# Report bugs to <memcached@googlegroups.com>.
#
@@ -580,8 +580,8 @@
# Identity of this package.
PACKAGE_NAME='memcached'
PACKAGE_TARNAME='memcached'
-PACKAGE_VERSION='1.4.24'
-PACKAGE_STRING='memcached 1.4.24'
+PACKAGE_VERSION='1.4.36'
+PACKAGE_STRING='memcached 1.4.36'
PACKAGE_BUGREPORT='memcached@googlegroups.com'
PACKAGE_URL=''
@@ -693,10 +693,6 @@
INSTALL_DATA
INSTALL_SCRIPT
INSTALL_PROGRAM
-target_os
-target_vendor
-target_cpu
-target
host_os
host_vendor
host_cpu
@@ -724,6 +720,7 @@
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -803,6 +800,7 @@
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1055,6 +1053,15 @@
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1192,7 +1199,7 @@
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1305,7 +1312,7 @@
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures memcached 1.4.24 to adapt to many kinds of systems.
+\`configure' configures memcached 1.4.36 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1345,6 +1352,7 @@
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1370,13 +1378,12 @@
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
- --target=TARGET configure for building compilers for TARGET [HOST]
_ACEOF
fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of memcached 1.4.24:";;
+ short | recursive ) echo "Configuration of memcached 1.4.36:";;
esac
cat <<\_ACEOF
@@ -1478,7 +1485,7 @@
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-memcached configure 1.4.24
+memcached configure 1.4.36
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1947,7 +1954,7 @@
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by memcached $as_me 1.4.24, which was
+It was created by memcached $as_me 1.4.36, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2395,48 +2402,8 @@
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
-$as_echo_n "checking target system type... " >&6; }
-if ${ac_cv_target+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "x$target_alias" = x; then
- ac_cv_target=$ac_cv_host
-else
- ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
- as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5
-fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5
-$as_echo "$ac_cv_target" >&6; }
-case $ac_cv_target in
-*-*-*) ;;
-*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;;
-esac
-target=$ac_cv_target
-ac_save_IFS=$IFS; IFS='-'
-set x $ac_cv_target
-shift
-target_cpu=$1
-target_vendor=$2
-shift; shift
-# Remember, the first character of IFS is used to create $*,
-# except with old shells:
-target_os=$*
-IFS=$ac_save_IFS
-case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
-
-
-# The aliases save the names the user supplied, while $host etc.
-# will get canonicalized.
-test -n "$target_alias" &&
- test "$program_prefix$program_suffix$program_transform_name" = \
- NONENONEs,x,x, &&
- program_prefix=${target_alias}-
-
-
-am__api_version='1.14'
+am__api_version='1.15'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
@@ -2608,8 +2575,8 @@
ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
if test x"${MISSING+set}" != xset; then
case $am_aux_dir in
@@ -2628,7 +2595,7 @@
$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
fi
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -2921,9 +2888,8 @@
# Define the identity of the package.
-
- PACKAGE=memcached
- VERSION=1.4.24
+ PACKAGE='memcached'
+ VERSION='1.4.36'
cat >>confdefs.h <<_ACEOF
@@ -2957,8 +2923,8 @@
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
mkdir_p='$(MKDIR_P)'
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AMTAR='$${TAR-tar}'
@@ -3015,6 +2981,7 @@
as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
fi
fi
+
ac_config_headers="$ac_config_headers config.h"
@@ -6271,6 +6238,36 @@
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_atomics" >&5
$as_echo "$have_gcc_atomics" >&6; }
+have_gcc_64atomics=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GCC 64bit atomics" >&5
+$as_echo_n "checking for GCC 64bit atomics... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ uint64_t a;
+ uint64_t b;
+ b = __sync_add_and_fetch(&a, 1);
+ b = __sync_sub_and_fetch(&a, 2);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ have_gcc_64atomics=yes
+
+$as_echo "#define HAVE_GCC_64ATOMICS 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_64atomics" >&5
+$as_echo "$have_gcc_64atomics" >&6; }
+
for ac_func in setppriv
do :
ac_fn_c_check_func "$LINENO" "setppriv" "ac_cv_func_setppriv"
@@ -7004,7 +7001,7 @@
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by memcached $as_me 1.4.24, which was
+This file was extended by memcached $as_me 1.4.36, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7070,7 +7067,7 @@
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-memcached config.status 1.4.24
+memcached config.status 1.4.36
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/configure.ac
^
|
@@ -1,11 +1,11 @@
AC_PREREQ(2.52)
m4_include([version.m4])
m4_include([m4/c99-backport.m4])
-AC_INIT(memcached, VERSION_NUMBER, memcached@googlegroups.com)
-AC_CANONICAL_SYSTEM
-AC_CONFIG_SRCDIR(memcached.c)
-AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
-AM_CONFIG_HEADER(config.h)
+AC_INIT([memcached], [VERSION_NUMBER], [memcached@googlegroups.com])
+AC_CANONICAL_HOST
+AC_CONFIG_SRCDIR([memcached.c])
+AM_INIT_AUTOMAKE([foreign])
+AM_CONFIG_HEADER([config.h])
AC_PROG_CC
@@ -535,6 +535,19 @@
AC_DEFINE(HAVE_GCC_ATOMICS, 1, [GCC Atomics available])])
AC_MSG_RESULT($have_gcc_atomics)
+dnl Check for usage of 64bit atomics
+dnl 32bit systems shouldn't have these.
+have_gcc_64atomics=no
+AC_MSG_CHECKING(for GCC 64bit atomics)
+AC_TRY_LINK([],[
+ uint64_t a;
+ uint64_t b;
+ b = __sync_add_and_fetch(&a, 1);
+ b = __sync_sub_and_fetch(&a, 2);
+ ],[have_gcc_64atomics=yes
+ AC_DEFINE(HAVE_GCC_64ATOMICS, 1, [GCC 64bit Atomics available])])
+AC_MSG_RESULT($have_gcc_64atomics)
+
dnl Check for the requirements for running memcached with less privileges
dnl than the default privilege set. On Solaris we need setppriv and priv.h
dnl If you want to add support for other platforms you should check for
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/crawler.c
^
|
@@ -0,0 +1,636 @@
+/* Copyright 2016 Netflix.
+ *
+ * Use and distribution licensed under the BSD license. See
+ * the LICENSE file for full text.
+ */
+
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include "memcached.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <unistd.h>
+#include <poll.h>
+
+#define LARGEST_ID POWER_LARGEST
+
+typedef struct {
+ void *c; /* original connection structure. still with source thread attached. */
+ int sfd; /* client fd. */
+ bipbuf_t *buf; /* output buffer */
+ char *cbuf; /* current buffer */
+} crawler_client_t;
+
+typedef struct _crawler_module_t crawler_module_t;
+
+typedef void (*crawler_eval_func)(crawler_module_t *cm, item *it, uint32_t hv, int slab_cls);
+typedef int (*crawler_init_func)(crawler_module_t *cm, void *data); // TODO: init args?
+typedef void (*crawler_deinit_func)(crawler_module_t *cm); // TODO: extra args?
+typedef void (*crawler_doneclass_func)(crawler_module_t *cm, int slab_cls);
+typedef void (*crawler_finalize_func)(crawler_module_t *cm);
+
+typedef struct {
+ crawler_init_func init; /* run before crawl starts */
+ crawler_eval_func eval; /* runs on an item. */
+ crawler_doneclass_func doneclass; /* runs once per sub-crawler completion. */
+ crawler_finalize_func finalize; /* runs once when all sub-crawlers are done. */
+ bool needs_lock; /* whether or not we need the LRU lock held when eval is called */
+ bool needs_client; /* whether or not to grab onto the remote client */
+} crawler_module_reg_t;
+
+struct _crawler_module_t {
+ void *data; /* opaque data pointer */
+ crawler_client_t c;
+ crawler_module_reg_t *mod;
+};
+
+static int crawler_expired_init(crawler_module_t *cm, void *data);
+static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls);
+static void crawler_expired_finalize(crawler_module_t *cm);
+static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);
+
+crawler_module_reg_t crawler_expired_mod = {
+ .init = crawler_expired_init,
+ .eval = crawler_expired_eval,
+ .doneclass = crawler_expired_doneclass,
+ .finalize = crawler_expired_finalize,
+ .needs_lock = true,
+ .needs_client = false
+};
+
+static void crawler_metadump_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);
+
+crawler_module_reg_t crawler_metadump_mod = {
+ .init = NULL,
+ .eval = crawler_metadump_eval,
+ .doneclass = NULL,
+ .finalize = NULL,
+ .needs_lock = false,
+ .needs_client = true
+};
+
+crawler_module_reg_t *crawler_mod_regs[2] = {
+ &crawler_expired_mod,
+ &crawler_metadump_mod
+};
+
+crawler_module_t active_crawler_mod;
+
+static crawler crawlers[LARGEST_ID];
+
+static int crawler_count = 0;
+static volatile int do_run_lru_crawler_thread = 0;
+static int lru_crawler_initialized = 0;
+static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t lru_crawler_cond = PTHREAD_COND_INITIALIZER;
+
+/* Will crawl all slab classes a minimum of once per hour */
+#define MAX_MAINTCRAWL_WAIT 60 * 60
+
+/*** LRU CRAWLER THREAD ***/
+
+#define LRU_CRAWLER_WRITEBUF 8192
+
+static void lru_crawler_close_client(crawler_client_t *c) {
+ //fprintf(stderr, "CRAWLER: Closing client\n");
+ sidethread_conn_close(c->c);
+ c->c = NULL;
+ c->cbuf = NULL;
+ bipbuf_free(c->buf);
+ c->buf = NULL;
+}
+
+static void lru_crawler_release_client(crawler_client_t *c) {
+ //fprintf(stderr, "CRAWLER: Closing client\n");
+ redispatch_conn(c->c);
+ c->c = NULL;
+ c->cbuf = NULL;
+ bipbuf_free(c->buf);
+ c->buf = NULL;
+}
+
+static int crawler_expired_init(crawler_module_t *cm, void *data) {
+ struct crawler_expired_data *d;
+ if (data != NULL) {
+ d = data;
+ d->is_external = true;
+ cm->data = data;
+ } else {
+ // allocate data.
+ d = calloc(1, sizeof(struct crawler_expired_data));
+ if (d == NULL) {
+ return -1;
+ }
+ // init lock.
+ pthread_mutex_init(&d->lock, NULL);
+ d->is_external = false;
+ d->start_time = current_time;
+
+ cm->data = d;
+ }
+ pthread_mutex_lock(&d->lock);
+ memset(&d->crawlerstats, 0, sizeof(crawlerstats_t) * MAX_NUMBER_OF_SLAB_CLASSES);
+ for (int x = 0; x < MAX_NUMBER_OF_SLAB_CLASSES; x++) {
+ d->crawlerstats[x].start_time = current_time;
+ d->crawlerstats[x].run_complete = false;
+ }
+ pthread_mutex_unlock(&d->lock);
+ return 0;
+}
+
+static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls) {
+ struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
+ pthread_mutex_lock(&d->lock);
+ d->crawlerstats[CLEAR_LRU(slab_cls)].end_time = current_time;
+ d->crawlerstats[CLEAR_LRU(slab_cls)].run_complete = true;
+ pthread_mutex_unlock(&d->lock);
+}
+
+static void crawler_expired_finalize(crawler_module_t *cm) {
+ struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
+ pthread_mutex_lock(&d->lock);
+ d->end_time = current_time;
+ d->crawl_complete = true;
+ pthread_mutex_unlock(&d->lock);
+
+ if (!d->is_external) {
+ free(d);
+ }
+}
+
+/* I pulled this out to make the main thread clearer, but it reaches into the
+ * main thread's values too much. Should rethink again.
+ */
+static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i) {
+ int slab_id = CLEAR_LRU(i);
+ struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
+ pthread_mutex_lock(&d->lock);
+ crawlerstats_t *s = &d->crawlerstats[slab_id];
+ int is_flushed = item_is_flushed(search);
+ if ((search->exptime != 0 && search->exptime < current_time)
+ || is_flushed) {
+ crawlers[i].reclaimed++;
+ s->reclaimed++;
+
+ if (settings.verbose > 1) {
+ int ii;
+ char *key = ITEM_key(search);
+ fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ",
+ search->it_flags, search->slabs_clsid);
+ for (ii = 0; ii < search->nkey; ++ii) {
+ fprintf(stderr, "%c", key[ii]);
+ }
+ fprintf(stderr, "\n");
+ }
+ if ((search->it_flags & ITEM_FETCHED) == 0 && !is_flushed) {
+ crawlers[i].unfetched++;
+ }
+ do_item_unlink_nolock(search, hv);
+ do_item_remove(search);
+ assert(search->slabs_clsid == 0);
+ } else {
+ s->seen++;
+ refcount_decr(search);
+ if (search->exptime == 0) {
+ s->noexp++;
+ } else if (search->exptime - current_time > 3599) {
+ s->ttl_hourplus++;
+ } else {
+ rel_time_t ttl_remain = search->exptime - current_time;
+ int bucket = ttl_remain / 60;
+ s->histo[bucket]++;
+ }
+ }
+ pthread_mutex_unlock(&d->lock);
+}
+
+static void crawler_metadump_eval(crawler_module_t *cm, item *it, uint32_t hv, int i) {
+ //int slab_id = CLEAR_LRU(i);
+ char keybuf[KEY_MAX_LENGTH * 3 + 1];
+ int is_flushed = item_is_flushed(it);
+ /* Ignore expired content. */
+ if ((it->exptime != 0 && it->exptime < current_time)
+ || is_flushed) {
+ refcount_decr(it);
+ return;
+ }
+ // TODO: uriencode directly into the buffer.
+ uriencode(ITEM_key(it), keybuf, it->nkey, KEY_MAX_LENGTH * 3 + 1);
+ int total = snprintf(cm->c.cbuf, 4096,
+ "key=%s exp=%ld la=%llu cas=%llu fetch=%s\n",
+ keybuf,
+ (it->exptime == 0) ? -1 : (long)it->exptime + process_started,
+ (unsigned long long)it->time + process_started,
+ (unsigned long long)ITEM_get_cas(it),
+ (it->it_flags & ITEM_FETCHED) ? "yes" : "no");
+ refcount_decr(it);
+ // TODO: some way of tracking the errors. these are very unlikely though.
+ if (total >= LRU_CRAWLER_WRITEBUF - 1 || total <= 0) {
+ /* Failed to write, don't push it. */
+ return;
+ }
+ bipbuf_push(cm->c.buf, total);
+}
+
+static int lru_crawler_poll(crawler_client_t *c) {
+ unsigned char *data;
+ unsigned int data_size = 0;
+ struct pollfd to_poll[1];
+ to_poll[0].fd = c->sfd;
+ to_poll[0].events = POLLOUT;
+
+ int ret = poll(to_poll, 1, 1000);
+
+ if (ret < 0) {
+ // fatal.
+ return -1;
+ }
+
+ if (ret == 0) return 0;
+
+ if (to_poll[0].revents & POLLIN) {
+ char buf[1];
+ int res = read(c->sfd, buf, 1);
+ if (res == 0 || (res == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))) {
+ lru_crawler_close_client(c);
+ return -1;
+ }
+ }
+ if ((data = bipbuf_peek_all(c->buf, &data_size)) != NULL) {
+ if (to_poll[0].revents & (POLLHUP|POLLERR)) {
+ lru_crawler_close_client(c);
+ return -1;
+ } else if (to_poll[0].revents & POLLOUT) {
+ int total = write(c->sfd, data, data_size);
+ if (total == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ lru_crawler_close_client(c);
+ return -1;
+ }
+ } else if (total == 0) {
+ lru_crawler_close_client(c);
+ return -1;
+ } else {
+ bipbuf_poll(c->buf, total);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Grab some space to work with, if none exists, run the poll() loop and wait
+ * for it to clear up or close.
+ * Return NULL if closed.
+ */
+static int lru_crawler_client_getbuf(crawler_client_t *c) {
+ void *buf = NULL;
+ if (c->c == NULL) return -1;
+ /* not enough space. */
+ while ((buf = bipbuf_request(c->buf, LRU_CRAWLER_WRITEBUF)) == NULL) {
+ // TODO: max loops before closing.
+ int ret = lru_crawler_poll(c);
+ if (ret < 0) return ret;
+ }
+
+ c->cbuf = buf;
+ return 0;
+}
+
+static void lru_crawler_class_done(int i) {
+ crawlers[i].it_flags = 0;
+ crawler_count--;
+ do_item_unlinktail_q((item *)&crawlers[i]);
+ do_item_stats_add_crawl(i, crawlers[i].reclaimed,
+ crawlers[i].unfetched, crawlers[i].checked);
+ pthread_mutex_unlock(&lru_locks[i]);
+ if (active_crawler_mod.mod->doneclass != NULL)
+ active_crawler_mod.mod->doneclass(&active_crawler_mod, i);
+}
+
+static void *item_crawler_thread(void *arg) {
+ int i;
+ int crawls_persleep = settings.crawls_persleep;
+
+ pthread_mutex_lock(&lru_crawler_lock);
+ pthread_cond_signal(&lru_crawler_cond);
+ settings.lru_crawler = true;
+ if (settings.verbose > 2)
+ fprintf(stderr, "Starting LRU crawler background thread\n");
+ while (do_run_lru_crawler_thread) {
+ pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
+
+ while (crawler_count) {
+ item *search = NULL;
+ void *hold_lock = NULL;
+
+ for (i = POWER_SMALLEST; i < LARGEST_ID; i++) {
+ if (crawlers[i].it_flags != 1) {
+ continue;
+ }
+
+ /* Get memory from bipbuf, if client has no space, flush. */
+ if (active_crawler_mod.c.c != NULL) {
+ int ret = lru_crawler_client_getbuf(&active_crawler_mod.c);
+ if (ret != 0) {
+ lru_crawler_class_done(i);
+ continue;
+ }
+ } else if (active_crawler_mod.mod->needs_client) {
+ lru_crawler_class_done(i);
+ continue;
+ }
+ pthread_mutex_lock(&lru_locks[i]);
+ search = do_item_crawl_q((item *)&crawlers[i]);
+ if (search == NULL ||
+ (crawlers[i].remaining && --crawlers[i].remaining < 1)) {
+ if (settings.verbose > 2)
+ fprintf(stderr, "Nothing left to crawl for %d\n", i);
+ lru_crawler_class_done(i);
+ continue;
+ }
+ uint32_t hv = hash(ITEM_key(search), search->nkey);
+ /* Attempt to hash item lock the "search" item. If locked, no
+ * other callers can incr the refcount
+ */
+ if ((hold_lock = item_trylock(hv)) == NULL) {
+ pthread_mutex_unlock(&lru_locks[i]);
+ continue;
+ }
+ /* Now see if the item is refcount locked */
+ if (refcount_incr(search) != 2) {
+ refcount_decr(search);
+ if (hold_lock)
+ item_trylock_unlock(hold_lock);
+ pthread_mutex_unlock(&lru_locks[i]);
+ continue;
+ }
+
+ crawlers[i].checked++;
+ /* Frees the item or decrements the refcount. */
+ /* Interface for this could improve: do the free/decr here
+ * instead? */
+ if (!active_crawler_mod.mod->needs_lock) {
+ pthread_mutex_unlock(&lru_locks[i]);
+ }
+
+ active_crawler_mod.mod->eval(&active_crawler_mod, search, hv, i);
+
+ if (hold_lock)
+ item_trylock_unlock(hold_lock);
+ if (active_crawler_mod.mod->needs_lock) {
+ pthread_mutex_unlock(&lru_locks[i]);
+ }
+
+ if (crawls_persleep-- <= 0 && settings.lru_crawler_sleep) {
+ usleep(settings.lru_crawler_sleep);
+ crawls_persleep = settings.crawls_persleep;
+ }
+ }
+ }
+
+ if (active_crawler_mod.mod != NULL) {
+ if (active_crawler_mod.mod->finalize != NULL)
+ active_crawler_mod.mod->finalize(&active_crawler_mod);
+ while (active_crawler_mod.c.c != NULL && bipbuf_used(active_crawler_mod.c.buf)) {
+ lru_crawler_poll(&active_crawler_mod.c);
+ }
+ // Double checking in case the client closed during the poll
+ if (active_crawler_mod.c.c != NULL) {
+ lru_crawler_release_client(&active_crawler_mod.c);
+ }
+ active_crawler_mod.mod = NULL;
+ }
+
+ if (settings.verbose > 2)
+ fprintf(stderr, "LRU crawler thread sleeping\n");
+ STATS_LOCK();
+ stats_state.lru_crawler_running = false;
+ STATS_UNLOCK();
+ }
+ pthread_mutex_unlock(&lru_crawler_lock);
+ if (settings.verbose > 2)
+ fprintf(stderr, "LRU crawler thread stopping\n");
+
+ return NULL;
+}
+
+static pthread_t item_crawler_tid;
+
+int stop_item_crawler_thread(void) {
+ int ret;
+ pthread_mutex_lock(&lru_crawler_lock);
+ do_run_lru_crawler_thread = 0;
+ pthread_cond_signal(&lru_crawler_cond);
+ pthread_mutex_unlock(&lru_crawler_lock);
+ if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) {
+ fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));
+ return -1;
+ }
+ settings.lru_crawler = false;
+ return 0;
+}
+
+/* Lock dance to "block" until thread is waiting on its condition:
+ * caller locks mtx. caller spawns thread.
+ * thread blocks on mutex.
+ * caller waits on condition, releases lock.
+ * thread gets lock, sends signal.
+ * caller can't wait, as thread has lock.
+ * thread waits on condition, releases lock
+ * caller wakes on condition, gets lock.
+ * caller immediately releases lock.
+ * thread is now safely waiting on condition before the caller returns.
+ */
+int start_item_crawler_thread(void) {
+ int ret;
+
+ if (settings.lru_crawler)
+ return -1;
+ pthread_mutex_lock(&lru_crawler_lock);
+ do_run_lru_crawler_thread = 1;
+ if ((ret = pthread_create(&item_crawler_tid, NULL,
+ item_crawler_thread, NULL)) != 0) {
+ fprintf(stderr, "Can't create LRU crawler thread: %s\n",
+ strerror(ret));
+ pthread_mutex_unlock(&lru_crawler_lock);
+ return -1;
+ }
+ /* Avoid returning until the crawler has actually started */
+ pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
+ pthread_mutex_unlock(&lru_crawler_lock);
+
+ return 0;
+}
+
+/* 'remaining' is passed in so the LRU maintainer thread can scrub the whole
+ * LRU every time.
+ */
+static int do_lru_crawler_start(uint32_t id, uint32_t remaining) {
+ int i;
+ uint32_t sid;
+ uint32_t tocrawl[3];
+ int starts = 0;
+ tocrawl[0] = id | HOT_LRU;
+ tocrawl[1] = id | WARM_LRU;
+ tocrawl[2] = id | COLD_LRU;
+
+ for (i = 0; i < 3; i++) {
+ sid = tocrawl[i];
+ pthread_mutex_lock(&lru_locks[sid]);
+ // TODO: Pretty sure this is a needless optimization.
+ //if (tails[sid] != NULL) {
+ if (settings.verbose > 2)
+ fprintf(stderr, "Kicking LRU crawler off for LRU %u\n", sid);
+ crawlers[sid].nbytes = 0;
+ crawlers[sid].nkey = 0;
+ crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */
+ crawlers[sid].next = 0;
+ crawlers[sid].prev = 0;
+ crawlers[sid].time = 0;
+ crawlers[sid].remaining = remaining;
+ crawlers[sid].slabs_clsid = sid;
+ crawlers[sid].reclaimed = 0;
+ crawlers[sid].unfetched = 0;
+ crawlers[sid].checked = 0;
+ do_item_linktail_q((item *)&crawlers[sid]);
+ crawler_count++;
+ starts++;
+ //}
+ pthread_mutex_unlock(&lru_locks[sid]);
+ }
+ if (starts) {
+ STATS_LOCK();
+ stats_state.lru_crawler_running = true;
+ stats.lru_crawler_starts++;
+ STATS_UNLOCK();
+ }
+ return starts;
+}
+
+static int lru_crawler_set_client(crawler_module_t *cm, void *c, const int sfd) {
+ crawler_client_t *crawlc = &cm->c;
+ if (crawlc->c != NULL) {
+ return -1;
+ }
+ crawlc->c = c;
+ crawlc->sfd = sfd;
+
+ crawlc->buf = bipbuf_new(1024 * 128);
+ if (crawlc->buf == NULL) {
+ return -2;
+ }
+ return 0;
+}
+
+int lru_crawler_start(uint8_t *ids, uint32_t remaining,
+ const enum crawler_run_type type, void *data,
+ void *c, const int sfd) {
+ int starts = 0;
+ if (pthread_mutex_trylock(&lru_crawler_lock) != 0) {
+ return -1;
+ }
+
+ /* Configure the module */
+ assert(crawler_mod_regs[type] != NULL);
+ active_crawler_mod.mod = crawler_mod_regs[type];
+ if (active_crawler_mod.mod->init != NULL) {
+ active_crawler_mod.mod->init(&active_crawler_mod, data);
+ }
+ if (active_crawler_mod.mod->needs_client) {
+ if (c == NULL || sfd == 0) {
+ pthread_mutex_unlock(&lru_crawler_lock);
+ return -2;
+ }
+ if (lru_crawler_set_client(&active_crawler_mod, c, sfd) != 0) {
+ pthread_mutex_unlock(&lru_crawler_lock);
+ return -2;
+ }
+ }
+
+ for (int sid = POWER_SMALLEST; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
+ if (ids[sid]) {
+ starts += do_lru_crawler_start(sid, remaining);
+ }
+ }
+ if (starts) {
+ pthread_cond_signal(&lru_crawler_cond);
+ }
+ pthread_mutex_unlock(&lru_crawler_lock);
+ return starts;
+}
+
+/*
+ * Also only clear the crawlerstats once per sid.
+ */
+enum crawler_result_type lru_crawler_crawl(char *slabs, const enum crawler_run_type type,
+ void *c, const int sfd) {
+ char *b = NULL;
+ uint32_t sid = 0;
+ int starts = 0;
+ uint8_t tocrawl[MAX_NUMBER_OF_SLAB_CLASSES];
+
+ /* FIXME: I added this while debugging. Don't think it's needed? */
+ memset(tocrawl, 0, sizeof(uint8_t) * MAX_NUMBER_OF_SLAB_CLASSES);
+ if (strcmp(slabs, "all") == 0) {
+ for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
+ tocrawl[sid] = 1;
+ }
+ } else {
+ for (char *p = strtok_r(slabs, ",", &b);
+ p != NULL;
+ p = strtok_r(NULL, ",", &b)) {
+
+ if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST
+ || sid >= MAX_NUMBER_OF_SLAB_CLASSES) {
+ pthread_mutex_unlock(&lru_crawler_lock);
+ return CRAWLER_BADCLASS;
+ }
+ tocrawl[sid] = 1;
+ }
+ }
+
+ starts = lru_crawler_start(tocrawl, settings.lru_crawler_tocrawl,
+ type, NULL, c, sfd);
+ if (starts == -1) {
+ return CRAWLER_RUNNING;
+ } else if (starts == -2) {
+ return CRAWLER_ERROR; /* FIXME: not very helpful. */
+ } else if (starts) {
+ return CRAWLER_OK;
+ } else {
+ return CRAWLER_NOTSTARTED;
+ }
+}
+
+/* If we hold this lock, crawler can't wake up or move */
+void lru_crawler_pause(void) {
+ pthread_mutex_lock(&lru_crawler_lock);
+}
+
+void lru_crawler_resume(void) {
+ pthread_mutex_unlock(&lru_crawler_lock);
+}
+
+int init_lru_crawler(void) {
+ if (lru_crawler_initialized == 0) {
+ if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) {
+ fprintf(stderr, "Can't initialize lru crawler condition\n");
+ return -1;
+ }
+ pthread_mutex_init(&lru_crawler_lock, NULL);
+ active_crawler_mod.c.c = NULL;
+ active_crawler_mod.mod = NULL;
+ active_crawler_mod.data = NULL;
+ lru_crawler_initialized = 1;
+ }
+ return 0;
+}
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/crawler.h
^
|
@@ -0,0 +1,39 @@
+#ifndef CRAWLER_H
+#define CRAWLER_H
+
+typedef struct {
+ uint64_t histo[61];
+ uint64_t ttl_hourplus;
+ uint64_t noexp;
+ uint64_t reclaimed;
+ uint64_t seen;
+ rel_time_t start_time;
+ rel_time_t end_time;
+ bool run_complete;
+} crawlerstats_t;
+
+struct crawler_expired_data {
+ pthread_mutex_t lock;
+ crawlerstats_t crawlerstats[MAX_NUMBER_OF_SLAB_CLASSES];
+ /* redundant with crawlerstats_t so we can get overall start/stop/done */
+ rel_time_t start_time;
+ rel_time_t end_time;
+ bool crawl_complete;
+ bool is_external; /* whether this was an alloc local or remote to the module. */
+};
+
+enum crawler_result_type {
+ CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED, CRAWLER_ERROR
+};
+
+int start_item_crawler_thread(void);
+int stop_item_crawler_thread(void);
+int init_lru_crawler(void);
+enum crawler_result_type lru_crawler_crawl(char *slabs, enum crawler_run_type, void *c, const int sfd);
+int lru_crawler_start(uint8_t *ids, uint32_t remaining,
+ const enum crawler_run_type type, void *data,
+ void *c, const int sfd);
+void lru_crawler_pause(void);
+void lru_crawler_resume(void);
+
+#endif
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/depcomp
^
|
@@ -3,7 +3,7 @@
scriptversion=2013-05-30.07; # UTC
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/Makefile
^
|
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
# doc/Makefile. Generated from Makefile.in by configure.
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -14,7 +14,17 @@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
@@ -75,17 +85,16 @@
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
-build_triplet = x86_64-unknown-linux-gnu
-host_triplet = x86_64-unknown-linux-gnu
-target_triplet = x86_64-unknown-linux-gnu
+build_triplet = x86_64-pc-linux-gnu
+host_triplet = x86_64-pc-linux-gnu
#am__append_1 = protocol-binary.txt protocol-binary-range.txt
subdir = doc
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/version.m4 \
$(top_srcdir)/m4/c99-backport.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
@@ -141,15 +150,16 @@
NROFF = nroff
MANS = $(man_MANS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing aclocal-1.14
+ACLOCAL = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing aclocal-1.15
AMTAR = $${TAR-tar}
AM_DEFAULT_VERBOSITY = 1
AUTOCONF = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing autoconf
AUTOHEADER = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing autoheader
-AUTOMAKE = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing automake-1.14
+AUTOMAKE = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing automake-1.15
AWK = gawk
-CC = gcc -std=gnu99
+CC = gcc
CCDEPMODE = depmode=gcc3
CFLAGS = -g -O2 -pthread -pthread -Wall -Werror -pedantic -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls
CPP = gcc -E
@@ -181,10 +191,10 @@
PACKAGE = memcached
PACKAGE_BUGREPORT = memcached@googlegroups.com
PACKAGE_NAME = memcached
-PACKAGE_STRING = memcached 1.4.24
+PACKAGE_STRING = memcached 1.4.36
PACKAGE_TARNAME = memcached
PACKAGE_URL =
-PACKAGE_VERSION = 1.4.24
+PACKAGE_VERSION = 1.4.36
PATH_SEPARATOR = :
PROFILER = /usr/bin/gcov
PROFILER_FLAGS = -fprofile-arcs -ftest-coverage
@@ -192,8 +202,8 @@
SET_MAKE =
SHELL = /bin/bash
STRIP =
-VERSION = 1.4.24
-XML2RFC = no
+VERSION = 1.4.36
+XML2RFC = /usr/bin/xml2rfc
XSLTPROC = no
abs_builddir = /home/dormando/d/p/danga/git/memcached/doc
abs_srcdir = /home/dormando/d/p/danga/git/memcached/doc
@@ -206,22 +216,22 @@
am__tar = $${TAR-tar} chof - "$$tardir"
am__untar = $${TAR-tar} xf -
bindir = ${exec_prefix}/bin
-build = x86_64-unknown-linux-gnu
+build = x86_64-pc-linux-gnu
build_alias =
build_cpu = x86_64
build_os = linux-gnu
-build_vendor = unknown
+build_vendor = pc
builddir = .
datadir = ${datarootdir}
datarootdir = ${prefix}/share
docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
dvidir = ${docdir}
exec_prefix = ${prefix}
-host = x86_64-unknown-linux-gnu
+host = x86_64-pc-linux-gnu
host_alias =
host_cpu = x86_64
host_os = linux-gnu
-host_vendor = unknown
+host_vendor = pc
htmldir = ${docdir}
includedir = ${prefix}/include
infodir = ${datarootdir}/info
@@ -237,15 +247,12 @@
prefix = /usr/local
program_transform_name = s,x,x,
psdir = ${docdir}
+runstatedir = ${localstatedir}/run
sbindir = ${exec_prefix}/sbin
sharedstatedir = ${prefix}/com
srcdir = .
sysconfdir = ${prefix}/etc
-target = x86_64-unknown-linux-gnu
target_alias =
-target_cpu = x86_64
-target_os = linux-gnu
-target_vendor = unknown
top_build_prefix = ../
top_builddir = ..
top_srcdir = ..
@@ -269,7 +276,6 @@
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign doc/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -490,18 +496,20 @@
pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \
uninstall-man1
+.PRECIOUS: Makefile
+
-protocol-binary.txt: $(.TARGET:R).full
- no -c /home/dormando/d/p/danga/git/memcached/doc $(.TARGET:R).full $@
+protocol-binary.txt: protocol-binary.full
+ /usr/bin/xml2rfc -c /home/dormando/d/p/danga/git/memcached/doc protocol-binary.full $@
-protocol-binary-range.txt: $(.TARGET:R).full
- no -c /home/dormando/d/p/danga/git/memcached/doc $(.TARGET:R).full $@
+protocol-binary-range.txt: protocol-binary-range.full
+ /usr/bin/xml2rfc -c /home/dormando/d/p/danga/git/memcached/doc protocol-binary-range.full $@
-protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- no --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary.full: protocol-binary.xml xml2rfc/rfc2629-noinc.xsl
+ no --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary.xml > $@
-protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- no --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary-range.full: protocol-binary-range.xml xml2rfc/rfc2629-noinc.xsl
+ no --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary-range.xml > $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/Makefile.am
^
|
@@ -9,15 +9,15 @@
MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt
endif
-protocol-binary.txt: $(.TARGET:R).full
- @XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
+protocol-binary.txt: protocol-binary.full
+ @XML2RFC@ -c @abs_builddir@ protocol-binary.full $@
-protocol-binary-range.txt: $(.TARGET:R).full
- @XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
+protocol-binary-range.txt: protocol-binary-range.full
+ @XML2RFC@ -c @abs_builddir@ protocol-binary-range.full $@
-protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary.full: protocol-binary.xml xml2rfc/rfc2629-noinc.xsl
+ @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary.xml > $@
-protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary-range.full: protocol-binary-range.xml xml2rfc/rfc2629-noinc.xsl
+ @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary-range.xml > $@
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/Makefile.in
^
|
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -14,7 +14,17 @@
@SET_MAKE@
VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
@@ -77,15 +87,14 @@
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-target_triplet = @target@
@BUILD_SPECIFICATIONS_TRUE@am__append_1 = protocol-binary.txt protocol-binary-range.txt
subdir = doc
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/version.m4 \
$(top_srcdir)/m4/c99-backport.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
@@ -141,6 +150,7 @@
NROFF = nroff
MANS = $(man_MANS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
@@ -237,15 +247,12 @@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
-target = @target@
target_alias = @target_alias@
-target_cpu = @target_cpu@
-target_os = @target_os@
-target_vendor = @target_vendor@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
@@ -269,7 +276,6 @@
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign doc/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -490,18 +496,20 @@
pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \
uninstall-man1
+.PRECIOUS: Makefile
+
-protocol-binary.txt: $(.TARGET:R).full
- @XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
+protocol-binary.txt: protocol-binary.full
+ @XML2RFC@ -c @abs_builddir@ protocol-binary.full $@
-protocol-binary-range.txt: $(.TARGET:R).full
- @XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
+protocol-binary-range.txt: protocol-binary-range.full
+ @XML2RFC@ -c @abs_builddir@ protocol-binary-range.full $@
-protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary.full: protocol-binary.xml xml2rfc/rfc2629-noinc.xsl
+ @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary.xml > $@
-protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
- @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
+protocol-binary-range.full: protocol-binary-range.xml xml2rfc/rfc2629-noinc.xsl
+ @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary-range.xml > $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/memcached.1
^
|
@@ -23,13 +23,19 @@
.B \-s <file>
Unix socket path to listen on (disables network support).
.TP
+.B \-A
+Enable ascii "shutdown" command.
+.TP
.B \-a <perms>
Permissions (in octal format) for Unix socket created with \-s option.
.TP
-.B \-l <ip_addr>
-Listen on <ip_addr>; default to INADDR_ANY. This is an important option to
-consider as there is no other way to secure the installation. Binding to an
-internal or firewalled network interface is suggested.
+.B \-l <addr>
+Listen on <addr>; default to INADDR_ANY. <addr> may be specified as host:port.
+If you don't specify a port number, the value you specified with -p or -U is
+used. You may specify multiple addresses separated by comma or by using -l
+multiple times. This is an important option to consider as there is no other
+way to secure the installation. Binding to an internal or firewalled network
+interface is suggested.
.TP
.B \-d
Run memcached as a daemon.
@@ -95,6 +101,9 @@
Be even more verbose; same as \-v but also print client commands and
responses.
.TP
+.B \-vvv
+Be extremely verbose; same of the above and also print internal state transitions.
+.TP
.B \-i
Print memcached and libevent licenses.
.TP
@@ -105,7 +114,8 @@
Number of threads to use to process incoming requests. This option is only
meaningful if memcached was compiled with thread support enabled. It is
typically not useful to set this higher than the number of CPU cores on the
-memcached server. The default is 4.
+memcached server. Setting a high number (64 or more) of worker
+threads is not recommended. The default is 4.
.TP
.B \-D <char>
Use <char> as the delimiter between key prefixes and IDs. This is used for
@@ -119,6 +129,9 @@
get large pages from the OS, memcached will allocate the total item-cache in
one large chunk. Only available if supported on your OS.
.TP
+.B \-b <num>
+Set the backlog queue limit to <num> connections. The default is 1024.
+.TP
.B \-B <proto>
Specify the binding protocol to use. By default, the server will
autonegotiate client connections. By using this option, you can
@@ -126,18 +139,29 @@
(the default, autonegotiation behavior), "ascii" and "binary".
.TP
.B \-I <size>
-Override the default size of each slab page. Default is 1mb. Default is 1m,
-minimum is 1k, max is 128m. Adjusting this value changes the item size limit.
-Beware that this also increases the number of slabs (use -v to view), and the
+Override the default size of each slab page. The default size is 1mb. Default
+value for this parameter is 1m, minimum is 1k, max is 128m.
+Adjusting this value changes the item size limit.
+Beware that this also increases the number of slabs (use \-v to view), and the
overal memory usage of memcached.
.TP
+.B \-S
+Turn on SASL authentication. This option is only meaningful if memcached was
+compiled with SASL support enabled.
+.TP
.B \-F
Disables the "flush_all" command. The cmd_flush counter will increment, but
clients will receive an error message and the flush will not occur.
.TP
+.B \-X
+Disables the "stats cachedump" and "lru_crawler metadump" commands.
+.TP
.B \-o <options>
-Comma separated list of extended or experimental options. See -h or wiki for
+Comma separated list of extended or experimental options. See \-h or wiki for
up to date list.
+.TP
+.B \-V
+print version and exit
.br
.SH LICENSE
The memcached daemon is copyright Danga Interactive and is distributed under
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/new_lru.txt
^
|
@@ -9,14 +9,16 @@
* LRU's are now split between HOT, WARM, and COLD LRU's. New items enter the
HOT LRU.
+ * Items hit at least twice are considered active.
* LRU updates only happen as items reach the bottom of an LRU. If active in
- HOT, stay in HOT, if active in WARM, stay in WARM. If active in COLD, move
+ HOT, move to WARM, if active in WARM, stay in WARM. If active in COLD, move
to WARM.
+ The exception is that items active in COLD are immediately moved to WARM.
* HOT/WARM each capped at 32% of memory available for that slab class. COLD
is uncapped (by default, as of this writing).
* Items flow from HOT/WARM into COLD.
* A background thread exists which shuffles items between/within the LRU's as
- limits are reached.
+ limits are reached. This includes moves from COLD to WARM.
* The background thread can also control the lru_crawler, if enabled.
The primary goal is to better protect active items from "scanning". Items
@@ -25,7 +27,7 @@
eviction), move to WARM. There they can stay relatively protected.
A secondary goal is to improve latency. The LRU locks are no longer used on
-item reads, only during sets and from the background thread. Also the
+most item reads, largely during sets and from the background thread. Also the
background thread is likely to find expired items and release them back to the
slab class asynchronously, which speeds up new allocations.
@@ -34,6 +36,9 @@
slab classes for items with expired TTL's. If your items are always set to
never expire, you can omit this option safely.
-An extra option: `-o expirezero_does_not_evict` (when used with
-lru_maintainer) will make items with an expiration time of 0 unevictable. Take
-caution as this will crowd out memory available for other items.
+An extra option: `-o temporary_ttl=N` (when used with lru_maintainer) will make
+items with a TTL less than or equal to this value use a fourth TEMP LRU. Items
+stored in TEMP are never bumped within its LRU or moved to other LRU's. They
+also cannot be evicted. This can help reduce holes and load on the LRU crawler.
+
+Do not set temporary_ttl too high or memory could become exhausted.
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/protocol-binary.xml
^
|
@@ -1412,12 +1412,17 @@
</section>
<section anchor="security" title="Security Considerations">
<t>
- Memcache has no authentication or security layers whatsoever. It is
+ Memcache has few authentication and no security layers whatsoever. It is
RECOMMENDED that memcache be deployed strictly on closed, protected,
back-end networks within a single data center, within a single cluster of
servers, or even on a single host, providing shared caching for multiple
applications. Memcache MUST NOT be made available on a public network.
</t>
+
+ <t>
+ SASL is supported as an authentication mechanism. See the wiki for more
+ information.
+ </t>
</section>
</middle>
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/doc/protocol.txt
^
|
@@ -165,7 +165,8 @@
items). If it's non-zero (either Unix time or offset in seconds from
current time), it is guaranteed that clients will not be able to
retrieve this item after the expiration time arrives (measured by
- server time).
+ server time). If a negative value is given the item is immediately
+ expired.
- <bytes> is the number of bytes in the data block to follow, *not*
including the delimiting \r\n. <bytes> may be zero (in which case
@@ -207,7 +208,7 @@
Retrieval command:
------------------
-The retrieval commands "get" and "gets" operates like this:
+The retrieval commands "get" and "gets" operate like this:
get <key>*\r\n
gets <key>*\r\n
@@ -323,7 +324,7 @@
touch <key> <exptime> [noreply]\r\n
-- <key> is the key of the item the client wishes the server to delete
+- <key> is the key of the item the client wishes the server to touch
- <exptime> is expiration time. Works the same as with the update commands
(set/add/etc). This replaces the existing expiration time. If an existing
@@ -395,12 +396,49 @@
- <0> means to set the thread on standby
-- <1> means to run the builtin slow algorithm to choose pages to move
+- <1> means to return pages to a global pool when there are more than 2 pages
+ worth of free chunks in a slab class. Pages are then re-assigned back into
+ other classes as-needed.
- <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.
+LRU Tuning
+----------
+
+Memcached supports multiple LRU algorithms, with a few tunables. Effort is
+made to have sane defaults however you are able to tune while the daemon is
+running.
+
+The traditional model is "flat" mode, which is a single LRU chain per slab
+class. The newer (with `-o modern` or `-o lru_maintainer`) is segmented into
+HOT, WARM, COLD. There is also a TEMP LRU. See doc/new_lru.txt for details.
+
+lru <tune|mode|temp_ttl> <option list>
+
+- "tune" takes numeric arguments "percent hot", "percent warm",
+ "max hot age", "max warm age factor". IE: "lru tune 10 25 3600 2.0".
+ This would cap HOT_LRU at 10% of the cache, or tail is idle longer than
+ 3600s. WARM_LRU is up to 25% of cache, or tail is idle longer than 2x
+ COLD_LRU.
+
+- "mode" <flat|segmented>: "flat" is traditional mode. "segmented" uses
+ HOT|WARM|COLD split. "segmented" mode requires `-o lru_maintainer` at start
+ time. If switching from segmented to flat mode, the background thread will
+ pull items from HOT|WARM into COLD queue.
+
+- "temp_ttl" <ttl>: If TTL is less than zero, disable usage of TEMP_LRU. If
+ zero or above, items set with a TTL lower than this will go into TEMP_LRU
+ and be unevictable until they naturally expire or are otherwise deleted or
+ replaced.
+
+The response line could be one of:
+
+- "OK" to indicate a successful update of the settings.
+
+- "ERROR [message]" to indicate a failure or improper arguments.
+
LRU_Crawler
-----------
@@ -465,6 +503,71 @@
- "BADCLASS [message]" to indicate an invalid class was specified.
+lru_crawler metadump <classid,classid,classid|all>
+
+- Similar in function to the above "lru_crawler crawl" command, this function
+ outputs one line for every valid item found in the matching slab classes.
+ Similar to "cachedump", but does not lock the cache and can return all
+ items, not just 1MB worth.
+
+ Lines are in "key=value key2=value2" format, with value being URI encoded
+ (ie: %20 for a space).
+
+ The exact keys available are subject to change, but will include at least:
+
+ "key", "exp" (expiration time), "la", (last access time), "cas",
+ "fetch" (if item has been fetched before).
+
+The response line could be one of:
+
+- "OK" to indicate successful launch.
+
+- "BUSY [message]" to indicate the crawler is already processing a request.
+
+- "BADCLASS [message]" to indicate an invalid class was specified.
+
+Watchers
+--------
+
+Watchers are a way to connect to memcached and inspect what's going on
+internally. This is an evolving feature so new endpoints should show up over
+time.
+
+watch <fetchers|mutations|evictions>
+
+- Turn connection into a watcher. Options can be stacked and are
+ space-separated. Logs will be sent to the watcher until it disconnects.
+
+The response line could be one of:
+
+- "OK" to indicate the watcher is ready to send logs.
+
+- "ERROR [message]" something went wrong while enabling.
+
+The response format is in "key=value key2=value2" format, for easy parsing.
+Lines are prepending with "ts=" for a timestamp and "gid=" for a global ID
+number of the log line. Given how logs are collected internally they may be
+printed out of order. If this is important the GID may be used to put log
+lines back in order.
+
+The value of keys (and potentially other things) are "URI encoded". Since most
+keys used conform to standard ASCII, this should have no effect. For keys with
+less standard or binary characters, "%NN"'s are inserted to represent the
+byte, ie: "n%2Cfoo" for "n,foo".
+
+The arguments are:
+
+- "fetchers": Currently emits logs every time an item is fetched internally.
+ This means a "set" command would also emit an item_get log, as it checks for
+ an item before replacing it. Multigets should also emit multiple lines.
+
+- "mutations": Currently emits logs when an item is stored in most cases.
+ Shows errors for most cases when items cannot be stored.
+
+- "evictions": Shows some information about items as they are evicted from the
+ cache. Useful in seeing if items being evicted were actually used, and which
+ keys are getting removed.
+
Statistics
----------
@@ -519,14 +622,15 @@
| | | (seconds:microseconds) |
| rusage_system | 32u.32u | Accumulated system time for this process |
| | | (seconds:microseconds) |
-| curr_items | 32u | Current number of items stored |
-| total_items | 32u | Total number of items stored since |
+| curr_items | 64u | Current number of items stored |
+| total_items | 64u | Total number of items stored since |
| | | the server started |
| bytes | 64u | Current number of bytes used |
| | | to store items |
| curr_connections | 32u | Number of open connections |
| total_connections | 32u | Total number of connections opened since |
| | | the server started running |
+| rejected_connections | 64u | Conns rejected in maxconns_fast mode |
| connection_structures | 32u | Number of connection structures allocated |
| | | by the server |
| reserved_fds | 32u | Number of misc fds used internally |
@@ -538,6 +642,10 @@
| | | and found present |
| get_misses | 64u | Number of items that have been requested |
| | | and not found |
+| get_expired | 64u | Number of items that have been requested |
+| | | but had already expired. |
+| get_flushed | 64u | Number of items that have been requested |
+| | | but have been flushed via flush_all |
| delete_misses | 64u | Number of deletions reqs for missing keys |
| delete_hits | 64u | Number of deletion reqs resulting in |
| | | an item being removed. |
@@ -556,6 +664,8 @@
| auth_cmds | 64u | Number of authentication commands |
| | | handled, success or failure. |
| auth_errors | 64u | Number of failed authentications. |
+| idle_kicks | 64u | Number of connections closed due to |
+| | | reaching their idle timeout. |
| evictions | 64u | Number of valid items removed from cache |
| | | to free memory for new items |
| reclaimed | 64u | Number of times an entry was stored using |
@@ -564,8 +674,13 @@
| | | from network |
| bytes_written | 64u | Total number of bytes sent by this server |
| | | to network |
-| limit_maxbytes | 32u | Number of bytes this server is allowed to |
+| limit_maxbytes | size_t | Number of bytes this server is allowed to |
| | | use for storage. |
+| accepting_conns | bool | Whether or not server is accepting conns |
+| listen_disabled_num | 64u | Number of times server has stopped |
+| | | accepting new connections (maxconns). |
+| time_in_listen_disabled_us |
+| | 64u | Number of microseconds in maxconns. |
| threads | 32u | Number of worker threads requested. |
| | | (see doc/threads.txt) |
| conn_yields | 64u | Number of times any connection yielded to |
@@ -579,10 +694,12 @@
| | | expiring |
| evicted_unfetched | 64u | Items evicted from LRU that were never |
| | | touched by get/incr/append/etc. |
+| evicted_active | 64u | Items evicted from LRU that had been hit |
+| | | recently but did not jump to top of LRU |
| slab_reassign_running | bool | If a slab page is being moved |
| slabs_moved | 64u | Total slab pages moved |
| crawler_reclaimed | 64u | Total items freed by LRU Crawler |
-| crawler_items-checked | 64u | Total items examined by LRU Crawler |
+| crawler_items_checked | 64u | Total items examined by LRU Crawler |
| lrutail_reflocked | 64u | Times LRU tail was found with active ref. |
| | | Items can be evicted to avoid OOM errors. |
| moves_to_cold | 64u | Items moved from HOT/WARM to COLD LRU's |
@@ -593,6 +710,26 @@
| lru_crawler_starts | 64u | Times an LRU crawler was started |
| lru_maintainer_juggles |
| | 64u | Number of times the LRU bg thread woke up |
+| slab_global_page_pool | 32u | Slab pages returned to global pool for |
+| | | reassignment to other slab classes. |
+| slab_reassign_rescues | 64u | Items rescued from eviction in page move |
+| slab_reassign_evictions_nomem |
+| | 64u | Valid items evicted during a page move |
+| | | (due to no free memory in slab) |
+| slab_reassign_chunk_rescues |
+| | 64u | Individual sections of an item rescued |
+| | | during a page move. |
+| slab_reassign_inline_reclaim |
+| | 64u | Internal stat counter for when the page |
+| | | mover clears memory from the chunk |
+| | | freelist when it wasn't expecting to. |
+| slab_reassign_busy_items |
+| | 64u | Items busy during page move, requiring a |
+| | | retry before page can be moved. |
+| log_worker_dropped | 64u | Logs a worker never wrote due to full buf |
+| log_worker_written | 64u | Logs written by a worker, to be picked up |
+| log_watcher_skipped | 64u | Logs not sent to slow watchers. |
+| log_watcher_sent | 64u | Logs written to watchers. |
|-----------------------+---------+-------------------------------------------|
Settings statistics
@@ -611,7 +748,7 @@
|-------------------+----------+----------------------------------------------|
| Name | Type | Meaning |
|-------------------+----------+----------------------------------------------|
-| maxbytes | size_t | Maximum number of bytes allows in this cache |
+| maxbytes | size_t | Maximum number of bytes allowed in the cache |
| maxconns | 32 | Maximum number of clients allowed. |
| tcpport | 32 | TCP listen port. |
| udpport | 32 | UDP listen port. |
@@ -635,17 +772,33 @@
| hashpower_init | 32 | Starting size multiplier for hash table |
| slab_reassign | bool | Whether slab page reassignment is allowed |
| slab_automove | bool | Whether slab page automover is enabled |
+| slab_chunk_max | 32 | Max slab class size (avoid unless necessary) |
| hash_algorithm | char | Hash table algorithm in use |
| lru_crawler | bool | Whether the LRU crawler is enabled |
| lru_crawler_sleep | 32 | Microseconds to sleep between LRU crawls |
| lru_crawler_tocrawl |
-| | 32u | Max items to crawl per slab per run |
+| | 32u | Max items to crawl per slab per run |
| lru_maintainer_thread |
-| | bool | Split LRU mode and background threads |
-| hot_lru_pct | 32 | Pct of slab memory reserved for HOT LRU |
-| warm_lru_pct | 32 | Pct of slab memory reserved for WARM LRU |
-| expirezero_does_not_evict |
-| | bool | If yes, items with 0 exptime cannot evict |
+| | bool | Split LRU mode and background threads |
+| hot_lru_pct | 32 | Pct of slab memory reserved for HOT LRU |
+| warm_lru_pct | 32 | Pct of slab memory reserved for WARM LRU |
+| hot_max_age | 32u | Max idle age of items in HOT LRU |
+| warm_max_factor | float | Set idle age of WARM LRU to COLD age * this |
+| temp_lru | bool | If yes, items < temporary_ttl use TEMP_LRU |
+| temporary_ttl | 32u | Items with TTL < this are marked temporary |
+| idle_time | 0 | Drop connections that are idle this many |
+| | | seconds (0 disables) |
+| watcher_logbuf_size |
+| | 32u | Size of internal (not socket) write buffer |
+| | | per active watcher connected. |
+| worker_logbuf_size| 32u | Size of internal per-worker-thread buffer |
+| | | which the background thread reads from. |
+| track_sizes | bool | If yes, a "stats sizes" histogram is being |
+| | | dymamically tracked. |
+| inline_ascii_response |
+| | bool | If yes, stores numbers from VALUE response |
+| | | inside an item, using up to 24 bytes. |
+| | | Small slowdown for ASCII get, faster sets. |
|-------------------+----------+----------------------------------------------|
@@ -676,7 +829,9 @@
number_hot Number of items presently stored in the HOT LRU.
number_warm Number of items presently stored in the WARM LRU.
number_cold Number of items presently stored in the COLD LRU.
-number_noexp Number of items presently stored in the NOEXP class.
+number_temp Number of items presently stored in the TEMPORARY LRU.
+age_hot Age of the oldest item in HOT LRU.
+age_warm Age of the oldest item in WARM LRU.
age Age of the oldest item in the LRU.
evicted Number of times an item had to be evicted from the LRU
before it expired.
@@ -698,6 +853,9 @@
were never touched after being set.
evicted_unfetched Number of valid items evicted from the LRU which were
never touched after being set.
+evicted_active Number of valid items evicted from the LRU which were
+ recently touched but were evicted before being moved to
+ the top of the LRU again.
crawler_reclaimed Number of items freed by the LRU Crawler.
lrutail_reflocked Number of items found to be refcount locked in the
LRU tail.
@@ -719,13 +877,12 @@
The "stats" command with the argument of "sizes" returns information about the
general size and count of all items stored in the cache.
-WARNING: This command WILL lock up your cache! It iterates over *every item*
-and examines the size. While the operation is fast, if you have many items
-you could prevent memcached from serving requests for several seconds.
+WARNING: In versions prior to 1.4.27 this command causes the cache server to
+lock while it iterates the items. 1.4.27 and greater are safe.
The data is returned in the following format:
-<size> <count>\r\n
+STAT <size> <count>\r\n
The server terminates this list with the line
@@ -740,6 +897,36 @@
lower range could allow items to fit more snugly into their slab classes, if
most of your items are less than 200 bytes in size.
+In 1.4.27 and after, this feature must be manually enabled.
+
+A "stats" command with the argument of "sizes_enable" will enable the
+histogram at runtime. This has a small overhead to every store or delete
+operation. If you don't want to incur this, leave it off.
+
+A "stats" command with the argument of "sizes_disable" will disable the
+histogram.
+
+It can also be enabled at starttime with "-o track_sizes"
+
+If disabled, "stats sizes" will return:
+
+STAT sizes_status disabled\r\n
+
+"stats sizes_enable" will return:
+
+STAT sizes_status enabled\r\n
+
+"stats sizes_disable" will return:
+
+STAT sizes_status disabled\r\n
+
+If an error happens, it will return:
+
+STAT sizes_status error\r\n
+STAT sizes_error [error_message]\r\n
+
+CAVEAT: If CAS support is disabled, you cannot enable/disable this feature at
+runtime.
Slab statistics
---------------
@@ -874,6 +1061,12 @@
intervals (by passing 0 to the first, 10 to the second, 20 to the
third, etc. etc.).
+"cache_memlimit" is a command with a numeric argument. This allows runtime
+adjustments of the cache memory limit. It returns "OK\r\n" or an error (unless
+"noreply" is given as the last parameter). If the new memory limit is higher
+than the old one, the server may start requesting more memory from the OS. If
+the limit is lower, and slabs_reassign+automove are enabled, free memory may
+be released back to the OS asynchronously.
"version" is a command with no arguments:
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/install-sh
^
|
@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
-scriptversion=2011-11-20.07; # UTC
+scriptversion=2014-09-12.12; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -41,19 +41,15 @@
# This script is compatible with the BSD install script, but was written
# from scratch.
+tab=' '
nl='
'
-IFS=" "" $nl"
+IFS=" $tab$nl"
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
-# Don't use :- since 4.3BSD and earlier shells don't like it.
doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
@@ -68,17 +64,6 @@
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
posix_mkdir=
# Desired mode of installed file.
@@ -97,7 +82,7 @@
dst_arg=
copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,46 +122,57 @@
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
- shift;;
+ shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
-o) chowncmd="$chownprog $2"
- shift;;
+ shift;;
-s) stripcmd=$stripprog;;
- -t) dst_arg=$2
- # Protect names problematic for 'test' and other utilities.
- case $dst_arg in
- -* | [=\(\)!]) dst_arg=./$dst_arg;;
- esac
- shift;;
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
- -T) no_target_directory=true;;
+ -T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
- --) shift
- break;;
+ --) shift
+ break;;
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
*) break;;
esac
shift
done
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
@@ -208,6 +204,15 @@
fi
if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
+if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
@@ -223,16 +228,16 @@
*[0-7])
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw='% 200'
+ u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw=,u+rw
+ u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
@@ -269,41 +274,15 @@
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
+ dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
@@ -314,74 +293,81 @@
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
- else
- mkdir_mode=
- fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ # $RANDOM is not portable (e.g. dash); use it when possible to
+ # lower collision chance
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ # As "mkdir -p" follows symlinks and we work in /tmp possibly; so
+ # create the $tmpdir first (and fail if unsuccessful) to make sure
+ # that nobody tries to guess the $tmpdir name.
+ if (umask $mkdir_umask &&
+ $mkdirprog $mkdir_mode "$tmpdir" &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ test_tmpdir="$tmpdir/a"
+ ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
esac
if
$posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
@@ -391,53 +377,51 @@
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
- /*) prefix='/';;
- [-=\(\)!]*) prefix='./';;
- *) prefix='';;
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
esac
- eval "$initialize_posix_glob"
-
oIFS=$IFS
IFS=/
- $posix_glob set -f
+ set -f
set fnord $dstdir
shift
- $posix_glob set +f
+ set +f
IFS=$oIFS
prefixes=
for d
do
- test X"$d" = X && continue
+ test X"$d" = X && continue
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
done
if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
fi
fi
fi
@@ -472,15 +456,12 @@
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
+ set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
@@ -493,24 +474,24 @@
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/items.c
^
|
@@ -1,30 +1,26 @@
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
+#include "bipbuffer.h"
#include <sys/stat.h>
#include <sys/socket.h>
-#include <sys/signal.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
+#include <signal.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <unistd.h>
+#include <poll.h>
/* Forward Declarations */
static void item_link_q(item *it);
static void item_unlink_q(item *it);
-#define HOT_LRU 0
-#define WARM_LRU 64
-#define COLD_LRU 128
-#define NOEXP_LRU 192
-static unsigned int lru_type_map[4] = {HOT_LRU, WARM_LRU, COLD_LRU, NOEXP_LRU};
-
-#define CLEAR_LRU(id) (id & ~(3<<6))
+static unsigned int lru_type_map[4] = {HOT_LRU, WARM_LRU, COLD_LRU, TEMP_LRU};
#define LARGEST_ID POWER_LARGEST
typedef struct {
@@ -33,8 +29,9 @@
uint64_t reclaimed;
uint64_t outofmemory;
uint64_t tailrepairs;
- uint64_t expired_unfetched;
- uint64_t evicted_unfetched;
+ uint64_t expired_unfetched; /* items reclaimed but never touched */
+ uint64_t evicted_unfetched; /* items evicted but never touched */
+ uint64_t evicted_active; /* items evicted that should have been shuffled */
uint64_t crawler_reclaimed;
uint64_t crawler_items_checked;
uint64_t lrutail_reflocked;
@@ -45,35 +42,20 @@
rel_time_t evicted_time;
} itemstats_t;
-typedef struct {
- uint64_t histo[60];
- uint64_t ttl_hourplus;
- uint64_t noexp;
- uint64_t reclaimed;
- uint64_t seen;
- rel_time_t start_time;
- rel_time_t end_time;
- bool run_complete;
-} crawlerstats_t;
-
static item *heads[LARGEST_ID];
static item *tails[LARGEST_ID];
-static crawler crawlers[LARGEST_ID];
static itemstats_t itemstats[LARGEST_ID];
static unsigned int sizes[LARGEST_ID];
-static crawlerstats_t crawlerstats[MAX_NUMBER_OF_SLAB_CLASSES];
+static uint64_t sizes_bytes[LARGEST_ID];
+static unsigned int *stats_sizes_hist = NULL;
+static uint64_t stats_sizes_cas_min = 0;
+static int stats_sizes_buckets = 0;
-static int crawler_count = 0;
-static volatile int do_run_lru_crawler_thread = 0;
static volatile int do_run_lru_maintainer_thread = 0;
-static int lru_crawler_initialized = 0;
static int lru_maintainer_initialized = 0;
-static int lru_maintainer_check_clsid = 0;
-static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t lru_crawler_cond = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t lru_crawler_stats_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t lru_maintainer_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t cas_id_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t stats_sizes_lock = PTHREAD_MUTEX_INITIALIZER;
void item_stats_reset(void) {
int i;
@@ -84,9 +66,41 @@
}
}
+/* called with class lru lock held */
+void do_item_stats_add_crawl(const int i, const uint64_t reclaimed,
+ const uint64_t unfetched, const uint64_t checked) {
+ itemstats[i].crawler_reclaimed += reclaimed;
+ itemstats[i].expired_unfetched += unfetched;
+ itemstats[i].crawler_items_checked += checked;
+}
+
+#define LRU_PULL_EVICT 1
+#define LRU_PULL_CRAWL_BLOCKS 2
+
static int lru_pull_tail(const int orig_id, const int cur_lru,
- const unsigned int total_chunks, const bool do_evict, const uint32_t cur_hv);
-static int lru_crawler_start(uint32_t id, uint32_t remaining);
+ const uint64_t total_bytes, const uint8_t flags, const rel_time_t max_age);
+
+typedef struct _lru_bump_buf {
+ struct _lru_bump_buf *prev;
+ struct _lru_bump_buf *next;
+ pthread_mutex_t mutex;
+ bipbuf_t *buf;
+ uint64_t dropped;
+} lru_bump_buf;
+
+typedef struct {
+ item *it;
+ uint32_t hv;
+} lru_bump_entry;
+
+static lru_bump_buf *bump_buf_head = NULL;
+static lru_bump_buf *bump_buf_tail = NULL;
+static pthread_mutex_t bump_buf_lock = PTHREAD_MUTEX_INITIALIZER;
+/* TODO: tunable? Need bench results */
+#define LRU_BUMP_BUF_SIZE 8192
+
+static bool lru_bump_async(lru_bump_buf *b, item *it, uint32_t hv);
+static uint64_t lru_total_bumps_dropped(void);
/* Get the next CAS id for a new item. */
/* TODO: refactor some atomics for this. */
@@ -98,7 +112,7 @@
return next_id;
}
-static int is_flushed(item *it) {
+int item_is_flushed(item *it) {
rel_time_t oldest_live = settings.oldest_live;
uint64_t cas = ITEM_get_cas(it);
uint64_t oldest_cas = settings.oldest_cas;
@@ -111,11 +125,12 @@
return 0;
}
-static unsigned int noexp_lru_size(int slabs_clsid) {
+static unsigned int temp_lru_size(int slabs_clsid) {
int id = CLEAR_LRU(slabs_clsid);
+ id |= TEMP_LRU;
unsigned int ret;
pthread_mutex_lock(&lru_locks[id]);
- ret = sizes[id];
+ ret = sizes_bytes[id];
pthread_mutex_unlock(&lru_locks[id]);
return ret;
}
@@ -143,51 +158,44 @@
*
* Returns the total size of the header.
*/
-static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
+static size_t item_make_header(const uint8_t nkey, const unsigned int flags, const int nbytes,
char *suffix, uint8_t *nsuffix) {
- /* suffix is defined at 40 chars elsewhere.. */
- *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);
+ if (settings.inline_ascii_response) {
+ /* suffix is defined at 40 chars elsewhere.. */
+ *nsuffix = (uint8_t) snprintf(suffix, 40, " %u %d\r\n", flags, nbytes - 2);
+ } else {
+ *nsuffix = sizeof(flags);
+ }
return sizeof(item) + nkey + *nsuffix + nbytes;
}
-item *do_item_alloc(char *key, const size_t nkey, const int flags,
- const rel_time_t exptime, const int nbytes,
- const uint32_t cur_hv) {
- int i;
- uint8_t nsuffix;
+static item *do_item_alloc_pull(const size_t ntotal, const unsigned int id) {
item *it = NULL;
- char suffix[40];
- unsigned int total_chunks;
- size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
- if (settings.use_cas) {
- ntotal += sizeof(uint64_t);
- }
-
- unsigned int id = slabs_clsid(ntotal);
- if (id == 0)
- return 0;
-
+ int i;
/* If no memory is available, attempt a direct LRU juggle/eviction */
/* This is a race in order to simplify lru_pull_tail; in cases where
* locked items are on the tail, you want them to fall out and cause
* occasional OOM's, rather than internally work around them.
* This also gives one fewer code path for slab alloc/free
*/
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 10; i++) {
+ uint64_t total_bytes;
/* Try to reclaim memory first */
- if (!settings.lru_maintainer_thread) {
- lru_pull_tail(id, COLD_LRU, 0, false, cur_hv);
+ if (!settings.lru_segmented) {
+ lru_pull_tail(id, COLD_LRU, 0, 0, 0);
}
- it = slabs_alloc(ntotal, id, &total_chunks);
- if (settings.expirezero_does_not_evict)
- total_chunks -= noexp_lru_size(id);
+ it = slabs_alloc(ntotal, id, &total_bytes, 0);
+
+ if (settings.temp_lru)
+ total_bytes -= temp_lru_size(id);
+
if (it == NULL) {
- if (settings.lru_maintainer_thread) {
- lru_pull_tail(id, HOT_LRU, total_chunks, false, cur_hv);
- lru_pull_tail(id, WARM_LRU, total_chunks, false, cur_hv);
- lru_pull_tail(id, COLD_LRU, total_chunks, true, cur_hv);
- } else {
- lru_pull_tail(id, COLD_LRU, 0, true, cur_hv);
+ if (lru_pull_tail(id, COLD_LRU, total_bytes, LRU_PULL_EVICT, 0) <= 0) {
+ if (settings.lru_segmented) {
+ lru_pull_tail(id, HOT_LRU, total_bytes, 0, 0);
+ } else {
+ break;
+ }
}
} else {
break;
@@ -200,6 +208,80 @@
pthread_mutex_unlock(&lru_locks[id]);
}
+ return it;
+}
+
+/* Chain another chunk onto this chunk. */
+/* slab mover: if it finds a chunk without ITEM_CHUNK flag, and no ITEM_LINKED
+ * flag, it counts as busy and skips.
+ * I think it might still not be safe to do linking outside of the slab lock
+ */
+item_chunk *do_item_alloc_chunk(item_chunk *ch, const size_t bytes_remain) {
+ // TODO: Should be a cleaner way of finding real size with slabber calls
+ size_t size = bytes_remain + sizeof(item_chunk);
+ if (size > settings.slab_chunk_size_max)
+ size = settings.slab_chunk_size_max;
+ unsigned int id = slabs_clsid(size);
+
+ item_chunk *nch = (item_chunk *) do_item_alloc_pull(size, id);
+ if (nch == NULL)
+ return NULL;
+
+ // link in.
+ // ITEM_CHUNK[ED] bits need to be protected by the slabs lock.
+ slabs_mlock();
+ nch->head = ch->head;
+ ch->next = nch;
+ nch->prev = ch;
+ nch->next = 0;
+ nch->used = 0;
+ nch->slabs_clsid = id;
+ nch->size = size - sizeof(item_chunk);
+ nch->it_flags |= ITEM_CHUNK;
+ slabs_munlock();
+ return nch;
+}
+
+item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags,
+ const rel_time_t exptime, const int nbytes) {
+ uint8_t nsuffix;
+ item *it = NULL;
+ char suffix[40];
+ // Avoid potential underflows.
+ if (nbytes < 2)
+ return 0;
+
+ size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
+ if (settings.use_cas) {
+ ntotal += sizeof(uint64_t);
+ }
+
+ unsigned int id = slabs_clsid(ntotal);
+ unsigned int hdr_id = 0;
+ if (id == 0)
+ return 0;
+
+ /* This is a large item. Allocate a header object now, lazily allocate
+ * chunks while reading the upload.
+ */
+ if (ntotal > settings.slab_chunk_size_max) {
+ /* We still link this item into the LRU for the larger slab class, but
+ * we're pulling a header from an entirely different slab class. The
+ * free routines handle large items specifically.
+ */
+ int htotal = nkey + 1 + nsuffix + sizeof(item) + sizeof(item_chunk);
+ if (settings.use_cas) {
+ htotal += sizeof(uint64_t);
+ }
+ hdr_id = slabs_clsid(htotal);
+ it = do_item_alloc_pull(htotal, hdr_id);
+ /* setting ITEM_CHUNKED is fine here because we aren't LINKED yet. */
+ if (it != NULL)
+ it->it_flags |= ITEM_CHUNKED;
+ } else {
+ it = do_item_alloc_pull(ntotal, id);
+ }
+
if (it == NULL) {
pthread_mutex_lock(&lru_locks[id]);
itemstats[id].outofmemory++;
@@ -211,16 +293,16 @@
//assert(it != heads[id]);
/* Refcount is seeded to 1 by slabs_alloc() */
- it->next = it->prev = it->h_next = 0;
+ it->next = it->prev = 0;
+
/* Items are initially loaded into the HOT_LRU. This is '0' but I want at
* least a note here. Compiler (hopefully?) optimizes this out.
*/
- if (settings.lru_maintainer_thread) {
- if (exptime == 0 && settings.expirezero_does_not_evict) {
- id |= NOEXP_LRU;
- } else {
- id |= HOT_LRU;
- }
+ if (settings.temp_lru &&
+ exptime - current_time <= settings.temporary_ttl) {
+ id |= TEMP_LRU;
+ } else if (settings.lru_segmented) {
+ id |= HOT_LRU;
} else {
/* There is only COLD in compat-mode */
id |= COLD_LRU;
@@ -228,13 +310,31 @@
it->slabs_clsid = id;
DEBUG_REFCNT(it, '*');
- it->it_flags = settings.use_cas ? ITEM_CAS : 0;
+ it->it_flags |= settings.use_cas ? ITEM_CAS : 0;
it->nkey = nkey;
it->nbytes = nbytes;
memcpy(ITEM_key(it), key, nkey);
it->exptime = exptime;
- memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
+ if (settings.inline_ascii_response) {
+ memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
+ } else {
+ memcpy(ITEM_suffix(it), &flags, sizeof(flags));
+ }
it->nsuffix = nsuffix;
+
+ /* Initialize internal chunk. */
+ if (it->it_flags & ITEM_CHUNKED) {
+ item_chunk *chunk = (item_chunk *) ITEM_data(it);
+
+ chunk->next = 0;
+ chunk->prev = 0;
+ chunk->used = 0;
+ chunk->size = 0;
+ chunk->head = it;
+ chunk->orig_clsid = hdr_id;
+ }
+ it->h_next = 0;
+
return it;
}
@@ -283,6 +383,7 @@
*head = it;
if (*tail == 0) *tail = it;
sizes[it->slabs_clsid]++;
+ sizes_bytes[it->slabs_clsid] += ITEM_ntotal(it);
return;
}
@@ -292,6 +393,13 @@
pthread_mutex_unlock(&lru_locks[it->slabs_clsid]);
}
+static void item_link_q_warm(item *it) {
+ pthread_mutex_lock(&lru_locks[it->slabs_clsid]);
+ do_item_link_q(it);
+ itemstats[it->slabs_clsid].moves_to_warm++;
+ pthread_mutex_unlock(&lru_locks[it->slabs_clsid]);
+}
+
static void do_item_unlink_q(item *it) {
item **head, **tail;
head = &heads[it->slabs_clsid];
@@ -311,6 +419,7 @@
if (it->next) it->next->prev = it->prev;
if (it->prev) it->prev->next = it->next;
sizes[it->slabs_clsid]--;
+ sizes_bytes[it->slabs_clsid] -= ITEM_ntotal(it);
return;
}
@@ -327,8 +436,8 @@
it->time = current_time;
STATS_LOCK();
- stats.curr_bytes += ITEM_ntotal(it);
- stats.curr_items += 1;
+ stats_state.curr_bytes += ITEM_ntotal(it);
+ stats_state.curr_items += 1;
stats.total_items += 1;
STATS_UNLOCK();
@@ -336,7 +445,8 @@
ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
assoc_insert(it, hv);
item_link_q(it);
- refcount_incr(&it->refcount);
+ refcount_incr(it);
+ item_stats_sizes_add(it);
return 1;
}
@@ -346,9 +456,10 @@
if ((it->it_flags & ITEM_LINKED) != 0) {
it->it_flags &= ~ITEM_LINKED;
STATS_LOCK();
- stats.curr_bytes -= ITEM_ntotal(it);
- stats.curr_items -= 1;
+ stats_state.curr_bytes -= ITEM_ntotal(it);
+ stats_state.curr_items -= 1;
STATS_UNLOCK();
+ item_stats_sizes_remove(it);
assoc_delete(ITEM_key(it), it->nkey, hv);
item_unlink_q(it);
do_item_remove(it);
@@ -361,9 +472,10 @@
if ((it->it_flags & ITEM_LINKED) != 0) {
it->it_flags &= ~ITEM_LINKED;
STATS_LOCK();
- stats.curr_bytes -= ITEM_ntotal(it);
- stats.curr_items -= 1;
+ stats_state.curr_bytes -= ITEM_ntotal(it);
+ stats_state.curr_items -= 1;
STATS_UNLOCK();
+ item_stats_sizes_remove(it);
assoc_delete(ITEM_key(it), it->nkey, hv);
do_item_unlink_q(it);
do_item_remove(it);
@@ -375,7 +487,7 @@
assert((it->it_flags & ITEM_SLABBED) == 0);
assert(it->refcount > 0);
- if (refcount_decr(&it->refcount) == 0) {
+ if (refcount_decr(it) == 0) {
item_free(it);
}
}
@@ -398,16 +510,30 @@
/* Bump the last accessed time, or relink if we're in compat mode */
void do_item_update(item *it) {
MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);
- if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
- assert((it->it_flags & ITEM_SLABBED) == 0);
+ /* Hits to COLD_LRU immediately move to WARM. */
+ if (settings.lru_segmented) {
+ assert((it->it_flags & ITEM_SLABBED) == 0);
if ((it->it_flags & ITEM_LINKED) != 0) {
- it->time = current_time;
- if (!settings.lru_maintainer_thread) {
+ if (ITEM_lruid(it) == COLD_LRU && (it->it_flags & ITEM_ACTIVE)) {
+ it->time = current_time;
item_unlink_q(it);
- item_link_q(it);
+ it->slabs_clsid = ITEM_clsid(it);
+ it->slabs_clsid |= WARM_LRU;
+ it->it_flags &= ~ITEM_ACTIVE;
+ item_link_q_warm(it);
+ } else if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
+ it->time = current_time;
}
}
+ } else if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
+ assert((it->it_flags & ITEM_SLABBED) == 0);
+
+ if ((it->it_flags & ITEM_LINKED) != 0) {
+ it->time = current_time;
+ item_unlink_q(it);
+ item_link_q(it);
+ }
}
}
@@ -438,7 +564,7 @@
char key_temp[KEY_MAX_LENGTH + 1];
char temp[512];
unsigned int id = slabs_clsid;
- if (!settings.lru_maintainer_thread)
+ if (!settings.lru_segmented)
id |= COLD_LRU;
pthread_mutex_lock(&lru_locks[id]);
@@ -459,9 +585,10 @@
/* Copy the key since it may not be null-terminated in the struct */
strncpy(key_temp, ITEM_key(it), it->nkey);
key_temp[it->nkey] = 0x00; /* terminate */
- len = snprintf(temp, sizeof(temp), "ITEM %s [%d b; %lu s]\r\n",
+ len = snprintf(temp, sizeof(temp), "ITEM %s [%d b; %llu s]\r\n",
key_temp, it->nbytes - 2,
- (unsigned long)it->exptime + process_started);
+ it->exptime == 0 ? 0 :
+ (unsigned long long)it->exptime + process_started);
if (bufcurr + len + 6 > memlimit) /* 6 is END\r\n\0 */
break;
memcpy(buffer + bufcurr, temp, len);
@@ -478,20 +605,6 @@
return buffer;
}
-void item_stats_evictions(uint64_t *evicted) {
- int n;
- for (n = 0; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
- int i;
- int x;
- for (x = 0; x < 4; x++) {
- i = n | lru_type_map[x];
- pthread_mutex_lock(&lru_locks[i]);
- evicted[n] += itemstats[i].evicted;
- pthread_mutex_unlock(&lru_locks[i]);
- }
- }
-}
-
void item_stats_totals(ADD_STAT add_stats, void *c) {
itemstats_t totals;
memset(&totals, 0, sizeof(itemstats_t));
@@ -504,6 +617,7 @@
pthread_mutex_lock(&lru_locks[i]);
totals.expired_unfetched += itemstats[i].expired_unfetched;
totals.evicted_unfetched += itemstats[i].evicted_unfetched;
+ totals.evicted_active += itemstats[i].evicted_active;
totals.evicted += itemstats[i].evicted;
totals.reclaimed += itemstats[i].reclaimed;
totals.crawler_reclaimed += itemstats[i].crawler_reclaimed;
@@ -520,6 +634,10 @@
(unsigned long long)totals.expired_unfetched);
APPEND_STAT("evicted_unfetched", "%llu",
(unsigned long long)totals.evicted_unfetched);
+ if (settings.lru_maintainer_thread) {
+ APPEND_STAT("evicted_active", "%llu",
+ (unsigned long long)totals.evicted_active);
+ }
APPEND_STAT("evictions", "%llu",
(unsigned long long)totals.evicted);
APPEND_STAT("reclaimed", "%llu",
@@ -539,6 +657,8 @@
(unsigned long long)totals.moves_within_lru);
APPEND_STAT("direct_reclaims", "%llu",
(unsigned long long)totals.direct_reclaims);
+ APPEND_STAT("lru_bumps_dropped", "%llu",
+ (unsigned long long)lru_total_bumps_dropped());
}
}
@@ -551,6 +671,8 @@
int i;
unsigned int size = 0;
unsigned int age = 0;
+ unsigned int age_hot = 0;
+ unsigned int age_warm = 0;
unsigned int lru_size_map[4];
const char *fmt = "items:%d:%s";
char key_str[STAT_KEY_LEN];
@@ -566,6 +688,7 @@
totals.reclaimed += itemstats[i].reclaimed;
totals.expired_unfetched += itemstats[i].expired_unfetched;
totals.evicted_unfetched += itemstats[i].evicted_unfetched;
+ totals.evicted_active += itemstats[i].evicted_active;
totals.crawler_reclaimed += itemstats[i].crawler_reclaimed;
totals.crawler_items_checked += itemstats[i].crawler_items_checked;
totals.lrutail_reflocked += itemstats[i].lrutail_reflocked;
@@ -575,8 +698,15 @@
totals.direct_reclaims += itemstats[i].direct_reclaims;
size += sizes[i];
lru_size_map[x] = sizes[i];
- if (lru_type_map[x] == COLD_LRU && tails[i] != NULL)
+ if (lru_type_map[x] == COLD_LRU && tails[i] != NULL) {
age = current_time - tails[i]->time;
+ } else if (lru_type_map[x] == HOT_LRU && tails[i] != NULL) {
+ age_hot = current_time - tails[i]->time;
+ } else if (lru_type_map[x] == WARM_LRU && tails[i] != NULL) {
+ age_warm = current_time - tails[i]->time;
+ }
+ if (lru_type_map[x] == COLD_LRU)
+ totals.evicted_time = itemstats[i].evicted_time;
pthread_mutex_unlock(&lru_locks[i]);
}
if (size == 0)
@@ -586,8 +716,11 @@
APPEND_NUM_FMT_STAT(fmt, n, "number_hot", "%u", lru_size_map[0]);
APPEND_NUM_FMT_STAT(fmt, n, "number_warm", "%u", lru_size_map[1]);
APPEND_NUM_FMT_STAT(fmt, n, "number_cold", "%u", lru_size_map[2]);
- if (settings.expirezero_does_not_evict)
- APPEND_NUM_FMT_STAT(fmt, n, "number_noexp", "%u", lru_size_map[3]);
+ if (settings.temp_lru) {
+ APPEND_NUM_FMT_STAT(fmt, n, "number_temp", "%u", lru_size_map[3]);
+ }
+ APPEND_NUM_FMT_STAT(fmt, n, "age_hot", "%u", age_hot);
+ APPEND_NUM_FMT_STAT(fmt, n, "age_warm", "%u", age_warm);
}
APPEND_NUM_FMT_STAT(fmt, n, "age", "%u", age);
APPEND_NUM_FMT_STAT(fmt, n, "evicted",
@@ -606,6 +739,10 @@
"%llu", (unsigned long long)totals.expired_unfetched);
APPEND_NUM_FMT_STAT(fmt, n, "evicted_unfetched",
"%llu", (unsigned long long)totals.evicted_unfetched);
+ if (settings.lru_maintainer_thread) {
+ APPEND_NUM_FMT_STAT(fmt, n, "evicted_active",
+ "%llu", (unsigned long long)totals.evicted_active);
+ }
APPEND_NUM_FMT_STAT(fmt, n, "crawler_reclaimed",
"%llu", (unsigned long long)totals.crawler_reclaimed);
APPEND_NUM_FMT_STAT(fmt, n, "crawler_items_checked",
@@ -628,6 +765,73 @@
add_stats(NULL, 0, NULL, 0, c);
}
+bool item_stats_sizes_status(void) {
+ bool ret = false;
+ mutex_lock(&stats_sizes_lock);
+ if (stats_sizes_hist != NULL)
+ ret = true;
+ mutex_unlock(&stats_sizes_lock);
+ return ret;
+}
+
+void item_stats_sizes_init(void) {
+ if (stats_sizes_hist != NULL)
+ return;
+ stats_sizes_buckets = settings.item_size_max / 32 + 1;
+ stats_sizes_hist = calloc(stats_sizes_buckets, sizeof(int));
+ stats_sizes_cas_min = (settings.use_cas) ? get_cas_id() : 0;
+}
+
+void item_stats_sizes_enable(ADD_STAT add_stats, void *c) {
+ mutex_lock(&stats_sizes_lock);
+ if (!settings.use_cas) {
+ APPEND_STAT("sizes_status", "error", "");
+ APPEND_STAT("sizes_error", "cas_support_disabled", "");
+ } else if (stats_sizes_hist == NULL) {
+ item_stats_sizes_init();
+ if (stats_sizes_hist != NULL) {
+ APPEND_STAT("sizes_status", "enabled", "");
+ } else {
+ APPEND_STAT("sizes_status", "error", "");
+ APPEND_STAT("sizes_error", "no_memory", "");
+ }
+ } else {
+ APPEND_STAT("sizes_status", "enabled", "");
+ }
+ mutex_unlock(&stats_sizes_lock);
+}
+
+void item_stats_sizes_disable(ADD_STAT add_stats, void *c) {
+ mutex_lock(&stats_sizes_lock);
+ if (stats_sizes_hist != NULL) {
+ free(stats_sizes_hist);
+ stats_sizes_hist = NULL;
+ }
+ APPEND_STAT("sizes_status", "disabled", "");
+ mutex_unlock(&stats_sizes_lock);
+}
+
+void item_stats_sizes_add(item *it) {
+ if (stats_sizes_hist == NULL || stats_sizes_cas_min > ITEM_get_cas(it))
+ return;
+ int ntotal = ITEM_ntotal(it);
+ int bucket = ntotal / 32;
+ if ((ntotal % 32) != 0) bucket++;
+ if (bucket < stats_sizes_buckets) stats_sizes_hist[bucket]++;
+}
+
+/* I think there's no way for this to be accurate without using the CAS value.
+ * Since items getting their time value bumped will pass this validation.
+ */
+void item_stats_sizes_remove(item *it) {
+ if (stats_sizes_hist == NULL || stats_sizes_cas_min > ITEM_get_cas(it))
+ return;
+ int ntotal = ITEM_ntotal(it);
+ int bucket = ntotal / 32;
+ if ((ntotal % 32) != 0) bucket++;
+ if (bucket < stats_sizes_buckets) stats_sizes_hist[bucket]--;
+}
+
/** dumps out a list of objects of each size, with granularity of 32 bytes */
/*@null@*/
/* Locks are correct based on a technicality. Holds LRU lock while doing the
@@ -635,46 +839,30 @@
* which don't change.
*/
void item_stats_sizes(ADD_STAT add_stats, void *c) {
+ mutex_lock(&stats_sizes_lock);
- /* max 1MB object, divided into 32 bytes size buckets */
- const int num_buckets = 32768;
- unsigned int *histogram = calloc(num_buckets, sizeof(int));
-
- if (histogram != NULL) {
+ if (stats_sizes_hist != NULL) {
int i;
-
- /* build the histogram */
- for (i = 0; i < LARGEST_ID; i++) {
- pthread_mutex_lock(&lru_locks[i]);
- item *iter = heads[i];
- while (iter) {
- int ntotal = ITEM_ntotal(iter);
- int bucket = ntotal / 32;
- if ((ntotal % 32) != 0) bucket++;
- if (bucket < num_buckets) histogram[bucket]++;
- iter = iter->next;
- }
- pthread_mutex_unlock(&lru_locks[i]);
- }
-
- /* write the buffer */
- for (i = 0; i < num_buckets; i++) {
- if (histogram[i] != 0) {
+ for (i = 0; i < stats_sizes_buckets; i++) {
+ if (stats_sizes_hist[i] != 0) {
char key[8];
snprintf(key, sizeof(key), "%d", i * 32);
- APPEND_STAT(key, "%u", histogram[i]);
+ APPEND_STAT(key, "%u", stats_sizes_hist[i]);
}
}
- free(histogram);
+ } else {
+ APPEND_STAT("sizes_status", "disabled", "");
}
+
add_stats(NULL, 0, NULL, 0, c);
+ mutex_unlock(&stats_sizes_lock);
}
/** wrapper around assoc_find which does the lazy expiration logic */
-item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {
+item *do_item_get(const char *key, const size_t nkey, const uint32_t hv, conn *c, const bool do_update) {
item *it = assoc_find(key, nkey, hv);
if (it != NULL) {
- refcount_incr(&it->refcount);
+ refcount_incr(it);
/* Optimization for slab reassignment. prevents popular items from
* jamming in busy wait. Can only do this here to satisfy lock order
* of item_lock, slabs_lock. */
@@ -704,7 +892,6 @@
fprintf(stderr, "> NOT FOUND ");
} else {
fprintf(stderr, "> FOUND KEY ");
- was_found++;
}
for (ii = 0; ii < nkey; ++ii) {
fprintf(stderr, "%c", key[ii]);
@@ -712,35 +899,72 @@
}
if (it != NULL) {
- if (is_flushed(it)) {
+ was_found = 1;
+ if (item_is_flushed(it)) {
do_item_unlink(it, hv);
do_item_remove(it);
it = NULL;
- if (was_found) {
+ pthread_mutex_lock(&c->thread->stats.mutex);
+ c->thread->stats.get_flushed++;
+ pthread_mutex_unlock(&c->thread->stats.mutex);
+ if (settings.verbose > 2) {
fprintf(stderr, " -nuked by flush");
}
+ was_found = 2;
} else if (it->exptime != 0 && it->exptime <= current_time) {
do_item_unlink(it, hv);
do_item_remove(it);
it = NULL;
- if (was_found) {
+ pthread_mutex_lock(&c->thread->stats.mutex);
+ c->thread->stats.get_expired++;
+ pthread_mutex_unlock(&c->thread->stats.mutex);
+ if (settings.verbose > 2) {
fprintf(stderr, " -nuked by expire");
}
+ was_found = 3;
} else {
- it->it_flags |= ITEM_FETCHED|ITEM_ACTIVE;
+ if (do_update) {
+ /* We update the hit markers only during fetches.
+ * An item needs to be hit twice overall to be considered
+ * ACTIVE, but only needs a single hit to maintain activity
+ * afterward.
+ * FETCHED tells if an item has ever been active.
+ */
+ if (settings.lru_segmented) {
+ if ((it->it_flags & ITEM_ACTIVE) == 0) {
+ if ((it->it_flags & ITEM_FETCHED) == 0) {
+ it->it_flags |= ITEM_FETCHED;
+ } else {
+ it->it_flags |= ITEM_ACTIVE;
+ if (ITEM_lruid(it) != COLD_LRU) {
+ do_item_update(it); // bump LA time
+ } else if (!lru_bump_async(c->thread->lru_bump_buf, it, hv)) {
+ // add flag before async bump to avoid race.
+ it->it_flags &= ~ITEM_ACTIVE;
+ }
+ }
+ }
+ } else {
+ it->it_flags |= ITEM_FETCHED;
+ do_item_update(it);
+ }
+ }
DEBUG_REFCNT(it, '+');
}
}
if (settings.verbose > 2)
fprintf(stderr, "\n");
+ /* For now this is in addition to the above verbose logging. */
+ LOGGER_LOG(c->thread->l, LOG_FETCHERS, LOGGER_ITEM_GET, NULL, was_found, key, nkey,
+ (it) ? ITEM_clsid(it) : 0);
return it;
}
item *do_item_touch(const char *key, size_t nkey, uint32_t exptime,
- const uint32_t hv) {
- item *it = do_item_get(key, nkey, hv);
+ const uint32_t hv, conn *c) {
+ item *it = do_item_get(key, nkey, hv, c, DO_UPDATE);
if (it != NULL) {
it->exptime = exptime;
}
@@ -752,7 +976,7 @@
/* Returns number of items remove, expired, or evicted.
* Callable from worker threads or the LRU maintainer thread */
static int lru_pull_tail(const int orig_id, const int cur_lru,
- const unsigned int total_chunks, const bool do_evict, const uint32_t cur_hv) {
+ const uint64_t total_bytes, const uint8_t flags, const rel_time_t max_age) {
item *it = NULL;
int id = orig_id;
int removed = 0;
@@ -764,7 +988,7 @@
item *next_it;
void *hold_lock = NULL;
unsigned int move_to_lru = 0;
- uint64_t limit;
+ uint64_t limit = 0;
id |= cur_lru;
pthread_mutex_lock(&lru_locks[id]);
@@ -775,16 +999,20 @@
next_it = search->prev;
if (search->nbytes == 0 && search->nkey == 0 && search->it_flags == 1) {
/* We are a crawler, ignore it. */
+ if (flags & LRU_PULL_CRAWL_BLOCKS) {
+ pthread_mutex_unlock(&lru_locks[id]);
+ return 0;
+ }
tries++;
continue;
}
uint32_t hv = hash(ITEM_key(search), search->nkey);
/* Attempt to hash item lock the "search" item. If locked, no
* other callers can incr the refcount. Also skip ourselves. */
- if (hv == cur_hv || (hold_lock = item_trylock(hv)) == NULL)
+ if ((hold_lock = item_trylock(hv)) == NULL)
continue;
/* Now see if the item is refcount locked */
- if (refcount_incr(&search->refcount) != 2) {
+ if (refcount_incr(search) != 2) {
/* Note pathological case with ref'ed items in tail.
* Can still unlink the item, but it won't be reusable yet */
itemstats[id].lrutail_reflocked++;
@@ -803,7 +1031,7 @@
/* Expired or flushed */
if ((search->exptime != 0 && search->exptime < current_time)
- || is_flushed(search)) {
+ || item_is_flushed(search)) {
itemstats[id].reclaimed++;
if ((search->it_flags & ITEM_FETCHED) == 0) {
itemstats[id].expired_unfetched++;
@@ -824,23 +1052,34 @@
*/
switch (cur_lru) {
case HOT_LRU:
- limit = total_chunks * settings.hot_lru_pct / 100;
+ limit = total_bytes * settings.hot_lru_pct / 100;
case WARM_LRU:
- limit = total_chunks * settings.warm_lru_pct / 100;
- if (sizes[id] > limit) {
+ if (limit == 0)
+ limit = total_bytes * settings.warm_lru_pct / 100;
+ /* Rescue ACTIVE items aggressively */
+ if ((search->it_flags & ITEM_ACTIVE) != 0) {
+ search->it_flags &= ~ITEM_ACTIVE;
+ if (cur_lru == WARM_LRU) {
+ itemstats[id].moves_within_lru++;
+ do_item_update_nolock(search);
+ do_item_remove(search);
+ item_trylock_unlock(hold_lock);
+ } else {
+ /* Active HOT_LRU items flow to WARM */
+ itemstats[id].moves_to_warm++;
+ move_to_lru = WARM_LRU;
+ do_item_unlink_q(search);
+ it = search;
+ removed++;
+ }
+ } else if (sizes_bytes[id] > limit ||
+ current_time - search->time > max_age) {
itemstats[id].moves_to_cold++;
move_to_lru = COLD_LRU;
do_item_unlink_q(search);
it = search;
removed++;
break;
- } else if ((search->it_flags & ITEM_ACTIVE) != 0) {
- /* Only allow ACTIVE relinking if we're not too large. */
- itemstats[id].moves_within_lru++;
- search->it_flags &= ~ITEM_ACTIVE;
- do_item_update_nolock(search);
- do_item_remove(search);
- item_trylock_unlock(hold_lock);
} else {
/* Don't want to move to COLD, not active, bail out */
it = search;
@@ -848,7 +1087,7 @@
break;
case COLD_LRU:
it = search; /* No matter what, we're stopping */
- if (do_evict) {
+ if (flags & LRU_PULL_EVICT) {
if (settings.evict_to_free == 0) {
/* Don't think we need a counter for this. It'll OOM. */
break;
@@ -860,10 +1099,17 @@
if ((search->it_flags & ITEM_FETCHED) == 0) {
itemstats[id].evicted_unfetched++;
}
+ if ((search->it_flags & ITEM_ACTIVE)) {
+ itemstats[id].evicted_active++;
+ }
+ LOGGER_LOG(NULL, LOG_EVICTIONS, LOGGER_EVICTION, search);
do_item_unlink_nolock(search, hv);
removed++;
+ if (settings.slab_automove == 2) {
+ slabs_reassign(-1, orig_id);
+ }
} else if ((search->it_flags & ITEM_ACTIVE) != 0
- && settings.lru_maintainer_thread) {
+ && settings.lru_segmented) {
itemstats[id].moves_to_warm++;
search->it_flags &= ~ITEM_ACTIVE;
move_to_lru = WARM_LRU;
@@ -871,6 +1117,9 @@
removed++;
}
break;
+ case TEMP_LRU:
+ it = search; /* Kill the loop. Parent only interested in reclaims */
+ break;
}
if (it != NULL)
break;
@@ -891,6 +1140,116 @@
return removed;
}
+
+/* TODO: Third place this code needs to be deduped */
+static void lru_bump_buf_link_q(lru_bump_buf *b) {
+ pthread_mutex_lock(&bump_buf_lock);
+ assert(b != bump_buf_head);
+
+ b->prev = 0;
+ b->next = bump_buf_head;
+ if (b->next) b->next->prev = b;
+ bump_buf_head = b;
+ if (bump_buf_tail == 0) bump_buf_tail = b;
+ pthread_mutex_unlock(&bump_buf_lock);
+ return;
+}
+
+void *item_lru_bump_buf_create(void) {
+ lru_bump_buf *b = calloc(1, sizeof(lru_bump_buf));
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->buf = bipbuf_new(sizeof(lru_bump_entry) * LRU_BUMP_BUF_SIZE);
+ if (b->buf == NULL) {
+ free(b);
+ return NULL;
+ }
+
+ pthread_mutex_init(&b->mutex, NULL);
+
+ lru_bump_buf_link_q(b);
+ return b;
+}
+
+static bool lru_bump_async(lru_bump_buf *b, item *it, uint32_t hv) {
+ bool ret = true;
+ refcount_incr(it);
+ pthread_mutex_lock(&b->mutex);
+ lru_bump_entry *be = (lru_bump_entry *) bipbuf_request(b->buf, sizeof(lru_bump_entry));
+ if (be != NULL) {
+ be->it = it;
+ be->hv = hv;
+ if (bipbuf_push(b->buf, sizeof(lru_bump_entry)) == 0) {
+ ret = false;
+ b->dropped++;
+ }
+ } else {
+ ret = false;
+ b->dropped++;
+ }
+ if (!ret) {
+ refcount_decr(it);
+ }
+ pthread_mutex_unlock(&b->mutex);
+ return ret;
+}
+
+/* TODO: Might be worth a micro-optimization of having bump buffers link
+ * themselves back into the central queue when queue goes from zero to
+ * non-zero, then remove from list if zero more than N times.
+ * If very few hits on cold this would avoid extra memory barriers from LRU
+ * maintainer thread. If many hits, they'll just stay in the list.
+ */
+static bool lru_maintainer_bumps(void) {
+ lru_bump_buf *b;
+ lru_bump_entry *be;
+ unsigned int size;
+ unsigned int todo;
+ bool bumped = false;
+ pthread_mutex_lock(&bump_buf_lock);
+ for (b = bump_buf_head; b != NULL; b=b->next) {
+ pthread_mutex_lock(&b->mutex);
+ be = (lru_bump_entry *) bipbuf_peek_all(b->buf, &size);
+ pthread_mutex_unlock(&b->mutex);
+
+ if (be == NULL) {
+ continue;
+ }
+ todo = size;
+ bumped = true;
+
+ while (todo) {
+ item_lock(be->hv);
+ do_item_update(be->it);
+ do_item_remove(be->it);
+ item_unlock(be->hv);
+ be++;
+ todo -= sizeof(lru_bump_entry);
+ }
+
+ pthread_mutex_lock(&b->mutex);
+ be = (lru_bump_entry *) bipbuf_poll(b->buf, size);
+ pthread_mutex_unlock(&b->mutex);
+ }
+ pthread_mutex_unlock(&bump_buf_lock);
+ return bumped;
+}
+
+static uint64_t lru_total_bumps_dropped(void) {
+ uint64_t total = 0;
+ lru_bump_buf *b;
+ pthread_mutex_lock(&bump_buf_lock);
+ for (b = bump_buf_head; b != NULL; b=b->next) {
+ pthread_mutex_lock(&b->mutex);
+ total += b->dropped;
+ pthread_mutex_unlock(&b->mutex);
+ }
+ pthread_mutex_unlock(&bump_buf_lock);
+ return total;
+}
+
/* Loop up to N times:
* If too many items are in HOT_LRU, push to COLD_LRU
* If too many items are in WARM_LRU, push to COLD_LRU
@@ -903,20 +1262,54 @@
int i;
int did_moves = 0;
bool mem_limit_reached = false;
- unsigned int total_chunks = 0;
+ uint64_t total_bytes = 0;
+ unsigned int chunks_perslab = 0;
+ unsigned int chunks_free = 0;
/* TODO: if free_chunks below high watermark, increase aggressiveness */
- slabs_available_chunks(slabs_clsid, &mem_limit_reached, &total_chunks);
- if (settings.expirezero_does_not_evict)
- total_chunks -= noexp_lru_size(slabs_clsid);
+ chunks_free = slabs_available_chunks(slabs_clsid, &mem_limit_reached,
+ &total_bytes, &chunks_perslab);
+ if (settings.temp_lru) {
+ /* Only looking for reclaims. Run before we size the LRU. */
+ for (i = 0; i < 500; i++) {
+ if (lru_pull_tail(slabs_clsid, TEMP_LRU, 0, 0, 0) <= 0) {
+ break;
+ } else {
+ did_moves++;
+ }
+ }
+ total_bytes -= temp_lru_size(slabs_clsid);
+ }
+
+ /* If slab automove is enabled on any level, and we have more than 2 pages
+ * worth of chunks free in this class, ask (gently) to reassign a page
+ * from this class back into the global pool (0)
+ */
+ if (settings.slab_automove > 0 && chunks_free > (chunks_perslab * 2.5)) {
+ slabs_reassign(slabs_clsid, SLAB_GLOBAL_PAGE_POOL);
+ }
+
+ rel_time_t cold_age = 0;
+ rel_time_t hot_age = 0;
+ /* If LRU is in flat mode, force items to drain into COLD via max age */
+ if (settings.lru_segmented) {
+ hot_age = settings.hot_max_age;
+ pthread_mutex_lock(&lru_locks[slabs_clsid|COLD_LRU]);
+ if (tails[slabs_clsid|COLD_LRU]) {
+ cold_age = current_time - tails[slabs_clsid|COLD_LRU]->time;
+ }
+ pthread_mutex_unlock(&lru_locks[slabs_clsid|COLD_LRU]);
+ }
/* Juggle HOT/WARM up to N times */
- for (i = 0; i < 1000; i++) {
+ for (i = 0; i < 500; i++) {
int do_more = 0;
- if (lru_pull_tail(slabs_clsid, HOT_LRU, total_chunks, false, 0) ||
- lru_pull_tail(slabs_clsid, WARM_LRU, total_chunks, false, 0)) {
+ if (lru_pull_tail(slabs_clsid, HOT_LRU, total_bytes, LRU_PULL_CRAWL_BLOCKS, hot_age) ||
+ lru_pull_tail(slabs_clsid, WARM_LRU, total_bytes, LRU_PULL_CRAWL_BLOCKS, cold_age * settings.warm_max_factor)) {
do_more++;
}
- do_more += lru_pull_tail(slabs_clsid, COLD_LRU, total_chunks, false, 0);
+ if (settings.lru_segmented) {
+ do_more += lru_pull_tail(slabs_clsid, COLD_LRU, total_bytes, LRU_PULL_CRAWL_BLOCKS, 0);
+ }
if (do_more == 0)
break;
did_moves++;
@@ -938,20 +1331,22 @@
* The latter is to avoid newly started daemons from waiting too long before
* retrying a crawl.
*/
-static void lru_maintainer_crawler_check(void) {
+static void lru_maintainer_crawler_check(struct crawler_expired_data *cdata, logger *l) {
int i;
- static rel_time_t last_crawls[MAX_NUMBER_OF_SLAB_CLASSES];
+ static rel_time_t next_crawls[MAX_NUMBER_OF_SLAB_CLASSES];
static rel_time_t next_crawl_wait[MAX_NUMBER_OF_SLAB_CLASSES];
+ uint8_t todo[MAX_NUMBER_OF_SLAB_CLASSES];
+ memset(todo, 0, sizeof(uint8_t) * MAX_NUMBER_OF_SLAB_CLASSES);
+ bool do_run = false;
+ if (!cdata->crawl_complete) {
+ return;
+ }
+
for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) {
- crawlerstats_t *s = &crawlerstats[i];
+ crawlerstats_t *s = &cdata->crawlerstats[i];
/* We've not successfully kicked off a crawl yet. */
- if (last_crawls[i] == 0) {
- if (lru_crawler_start(i, 0) > 0) {
- last_crawls[i] = current_time;
- }
- }
- pthread_mutex_lock(&lru_crawler_stats_lock);
if (s->run_complete) {
+ pthread_mutex_lock(&cdata->lock);
int x;
/* Should we crawl again? */
uint64_t possible_reclaims = s->seen - s->noexp;
@@ -959,77 +1354,123 @@
/* Need to think we can free at least 1% of the items before
* crawling. */
/* FIXME: Configurable? */
- uint64_t low_watermark = (s->seen / 100) + 1;
+ uint64_t low_watermark = (possible_reclaims / 100) + 1;
rel_time_t since_run = current_time - s->end_time;
/* Don't bother if the payoff is too low. */
- if (settings.verbose > 1)
- fprintf(stderr, "maint crawler: low_watermark: %llu, possible_reclaims: %llu, since_run: %u\n",
- (unsigned long long)low_watermark, (unsigned long long)possible_reclaims,
- (unsigned int)since_run);
for (x = 0; x < 60; x++) {
- if (since_run < (x * 60) + 60)
- break;
available_reclaims += s->histo[x];
+ if (available_reclaims > low_watermark) {
+ if (next_crawl_wait[i] < (x * 60)) {
+ next_crawl_wait[i] += 60;
+ } else if (next_crawl_wait[i] >= 60) {
+ next_crawl_wait[i] -= 60;
+ }
+ break;
+ }
}
- if (available_reclaims > low_watermark) {
- last_crawls[i] = 0;
- if (next_crawl_wait[i] > 60)
- next_crawl_wait[i] -= 60;
- } else if (since_run > 5 && since_run > next_crawl_wait[i]) {
- last_crawls[i] = 0;
- if (next_crawl_wait[i] < MAX_MAINTCRAWL_WAIT)
- next_crawl_wait[i] += 60;
+
+ if (available_reclaims == 0) {
+ next_crawl_wait[i] += 60;
}
- if (settings.verbose > 1)
- fprintf(stderr, "maint crawler: available reclaims: %llu, next_crawl: %u\n", (unsigned long long)available_reclaims, next_crawl_wait[i]);
+
+ if (next_crawl_wait[i] > MAX_MAINTCRAWL_WAIT) {
+ next_crawl_wait[i] = MAX_MAINTCRAWL_WAIT;
+ }
+
+ next_crawls[i] = current_time + next_crawl_wait[i] + 5;
+ LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_CRAWLER_STATUS, NULL, i, (unsigned long long)low_watermark,
+ (unsigned long long)available_reclaims,
+ (unsigned int)since_run,
+ next_crawls[i] - current_time,
+ s->end_time - s->start_time,
+ s->seen,
+ s->reclaimed);
+ // Got our calculation, avoid running until next actual run.
+ s->run_complete = false;
+ pthread_mutex_unlock(&cdata->lock);
+ }
+ if (current_time > next_crawls[i]) {
+ todo[i] = 1;
+ do_run = true;
+ next_crawls[i] = current_time + 5; // minimum retry wait.
}
- pthread_mutex_unlock(&lru_crawler_stats_lock);
+ }
+ if (do_run) {
+ lru_crawler_start(todo, settings.lru_crawler_tocrawl, CRAWLER_EXPIRED, cdata, NULL, 0);
}
}
static pthread_t lru_maintainer_tid;
-#define MAX_LRU_MAINTAINER_SLEEP 1000000
+#define MAX_LRU_MAINTAINER_SLEEP 500000
#define MIN_LRU_MAINTAINER_SLEEP 1000
static void *lru_maintainer_thread(void *arg) {
int i;
useconds_t to_sleep = MIN_LRU_MAINTAINER_SLEEP;
+ useconds_t last_sleep = MIN_LRU_MAINTAINER_SLEEP;
rel_time_t last_crawler_check = 0;
+ useconds_t next_juggles[MAX_NUMBER_OF_SLAB_CLASSES];
+ useconds_t backoff_juggles[MAX_NUMBER_OF_SLAB_CLASSES];
+ struct crawler_expired_data cdata;
+ memset(&cdata, 0, sizeof(struct crawler_expired_data));
+ memset(next_juggles, 0, sizeof(next_juggles));
+ pthread_mutex_init(&cdata.lock, NULL);
+ cdata.crawl_complete = true; // kick off the crawler.
+ logger *l = logger_create();
+ if (l == NULL) {
+ fprintf(stderr, "Failed to allocate logger for LRU maintainer thread\n");
+ abort();
+ }
pthread_mutex_lock(&lru_maintainer_lock);
if (settings.verbose > 2)
fprintf(stderr, "Starting LRU maintainer background thread\n");
while (do_run_lru_maintainer_thread) {
- int did_moves = 0;
pthread_mutex_unlock(&lru_maintainer_lock);
- usleep(to_sleep);
+ if (to_sleep)
+ usleep(to_sleep);
pthread_mutex_lock(&lru_maintainer_lock);
+ /* A sleep of zero counts as a minimum of a 1ms wait */
+ last_sleep = to_sleep > 1000 ? to_sleep : 1000;
+ to_sleep = MAX_LRU_MAINTAINER_SLEEP;
STATS_LOCK();
stats.lru_maintainer_juggles++;
STATS_UNLOCK();
- /* We were asked to immediately wake up and poke a particular slab
- * class due to a low watermark being hit */
- if (lru_maintainer_check_clsid != 0) {
- did_moves = lru_maintainer_juggle(lru_maintainer_check_clsid);
- lru_maintainer_check_clsid = 0;
- } else {
- for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) {
- did_moves += lru_maintainer_juggle(i);
+
+ /* Each slab class gets its own sleep to avoid hammering locks */
+ for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) {
+ next_juggles[i] = next_juggles[i] > last_sleep ? next_juggles[i] - last_sleep : 0;
+
+ // Sleep the thread just for the minimum amount (or not at all)
+ if (next_juggles[i] < to_sleep) {
+ to_sleep = next_juggles[i];
}
+ if (next_juggles[i] > 0)
+ continue;
+
+ int did_moves = lru_maintainer_juggle(i);
+ if (did_moves == 0) {
+ if (backoff_juggles[i] < MAX_LRU_MAINTAINER_SLEEP)
+ backoff_juggles[i] += 1000;
+ } else if (backoff_juggles[i] > 0) {
+ backoff_juggles[i] /= 2;
+ if (backoff_juggles[i] < MIN_LRU_MAINTAINER_SLEEP) {
+ backoff_juggles[i] = 0;
+ }
+ }
+ next_juggles[i] = backoff_juggles[i];
}
- if (did_moves == 0) {
- if (to_sleep < MAX_LRU_MAINTAINER_SLEEP)
- to_sleep += 1000;
- } else {
- to_sleep /= 2;
- if (to_sleep < MIN_LRU_MAINTAINER_SLEEP)
- to_sleep = MIN_LRU_MAINTAINER_SLEEP;
+
+ /* Minimize the sleep if we had async LRU bumps to process */
+ if (settings.lru_segmented && lru_maintainer_bumps() && to_sleep > 1000) {
+ to_sleep = 1000;
}
+
/* Once per second at most */
if (settings.lru_crawler && last_crawler_check != current_time) {
- lru_maintainer_crawler_check();
+ lru_maintainer_crawler_check(&cdata, l);
last_crawler_check = current_time;
}
}
@@ -1039,6 +1480,7 @@
return NULL;
}
+
int stop_lru_maintainer_thread(void) {
int ret;
pthread_mutex_lock(&lru_maintainer_lock);
@@ -1088,16 +1530,15 @@
return 0;
}
-/*** ITEM CRAWLER THREAD ***/
-
-static void crawler_link_q(item *it) { /* item is the new tail */
+/* Tail linkers and crawler for the LRU crawler. */
+void do_item_linktail_q(item *it) { /* item is the new tail */
item **head, **tail;
assert(it->it_flags == 1);
assert(it->nbytes == 0);
head = &heads[it->slabs_clsid];
tail = &tails[it->slabs_clsid];
- assert(*tail != 0);
+ //assert(*tail != 0);
assert(it != *tail);
assert((*head && *tail) || (*head == 0 && *tail == 0));
it->prev = *tail;
@@ -1111,7 +1552,7 @@
return;
}
-static void crawler_unlink_q(item *it) {
+void do_item_unlinktail_q(item *it) {
item **head, **tail;
head = &heads[it->slabs_clsid];
tail = &tails[it->slabs_clsid];
@@ -1134,11 +1575,10 @@
/* This is too convoluted, but it's a difficult shuffle. Try to rewrite it
* more clearly. */
-static item *crawler_crawl_q(item *it) {
+item *do_item_crawl_q(item *it) {
item **head, **tail;
assert(it->it_flags == 1);
assert(it->nbytes == 0);
- assert(it->slabs_clsid < LARGEST_ID);
head = &heads[it->slabs_clsid];
tail = &tails[it->slabs_clsid];
@@ -1188,291 +1628,3 @@
return it->next; /* success */
}
-
-/* I pulled this out to make the main thread clearer, but it reaches into the
- * main thread's values too much. Should rethink again.
- */
-static void item_crawler_evaluate(item *search, uint32_t hv, int i) {
- int slab_id = CLEAR_LRU(i);
- crawlerstats_t *s = &crawlerstats[slab_id];
- itemstats[i].crawler_items_checked++;
- if ((search->exptime != 0 && search->exptime < current_time)
- || is_flushed(search)) {
- itemstats[i].crawler_reclaimed++;
- s->reclaimed++;
-
- if (settings.verbose > 1) {
- int ii;
- char *key = ITEM_key(search);
- fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ",
- search->it_flags, search->slabs_clsid);
- for (ii = 0; ii < search->nkey; ++ii) {
- fprintf(stderr, "%c", key[ii]);
- }
- fprintf(stderr, "\n");
- }
- if ((search->it_flags & ITEM_FETCHED) == 0) {
- itemstats[i].expired_unfetched++;
- }
- do_item_unlink_nolock(search, hv);
- do_item_remove(search);
- assert(search->slabs_clsid == 0);
- } else {
- s->seen++;
- refcount_decr(&search->refcount);
- if (search->exptime == 0) {
- s->noexp++;
- } else if (search->exptime - current_time > 3599) {
- s->ttl_hourplus++;
- } else {
- rel_time_t ttl_remain = search->exptime - current_time;
- int bucket = ttl_remain / 60;
- s->histo[bucket]++;
- }
- }
-}
-
-static void *item_crawler_thread(void *arg) {
- int i;
- int crawls_persleep = settings.crawls_persleep;
-
- pthread_mutex_lock(&lru_crawler_lock);
- if (settings.verbose > 2)
- fprintf(stderr, "Starting LRU crawler background thread\n");
- while (do_run_lru_crawler_thread) {
- pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
-
- while (crawler_count) {
- item *search = NULL;
- void *hold_lock = NULL;
-
- for (i = POWER_SMALLEST; i < LARGEST_ID; i++) {
- if (crawlers[i].it_flags != 1) {
- continue;
- }
- pthread_mutex_lock(&lru_locks[i]);
- search = crawler_crawl_q((item *)&crawlers[i]);
- if (search == NULL ||
- (crawlers[i].remaining && --crawlers[i].remaining < 1)) {
- if (settings.verbose > 2)
- fprintf(stderr, "Nothing left to crawl for %d\n", i);
- crawlers[i].it_flags = 0;
- crawler_count--;
- crawler_unlink_q((item *)&crawlers[i]);
- pthread_mutex_unlock(&lru_locks[i]);
- pthread_mutex_lock(&lru_crawler_stats_lock);
- crawlerstats[CLEAR_LRU(i)].end_time = current_time;
- crawlerstats[CLEAR_LRU(i)].run_complete = true;
- pthread_mutex_unlock(&lru_crawler_stats_lock);
- continue;
- }
- uint32_t hv = hash(ITEM_key(search), search->nkey);
- /* Attempt to hash item lock the "search" item. If locked, no
- * other callers can incr the refcount
- */
- if ((hold_lock = item_trylock(hv)) == NULL) {
- pthread_mutex_unlock(&lru_locks[i]);
- continue;
- }
- /* Now see if the item is refcount locked */
- if (refcount_incr(&search->refcount) != 2) {
- refcount_decr(&search->refcount);
- if (hold_lock)
- item_trylock_unlock(hold_lock);
- pthread_mutex_unlock(&lru_locks[i]);
- continue;
- }
-
- /* Frees the item or decrements the refcount. */
- /* Interface for this could improve: do the free/decr here
- * instead? */
- pthread_mutex_lock(&lru_crawler_stats_lock);
- item_crawler_evaluate(search, hv, i);
- pthread_mutex_unlock(&lru_crawler_stats_lock);
-
- if (hold_lock)
- item_trylock_unlock(hold_lock);
- pthread_mutex_unlock(&lru_locks[i]);
-
- if (crawls_persleep <= 0 && settings.lru_crawler_sleep) {
- usleep(settings.lru_crawler_sleep);
- crawls_persleep = settings.crawls_persleep;
- }
- }
- }
- if (settings.verbose > 2)
- fprintf(stderr, "LRU crawler thread sleeping\n");
- STATS_LOCK();
- stats.lru_crawler_running = false;
- STATS_UNLOCK();
- }
- pthread_mutex_unlock(&lru_crawler_lock);
- if (settings.verbose > 2)
- fprintf(stderr, "LRU crawler thread stopping\n");
-
- return NULL;
-}
-
-static pthread_t item_crawler_tid;
-
-int stop_item_crawler_thread(void) {
- int ret;
- pthread_mutex_lock(&lru_crawler_lock);
- do_run_lru_crawler_thread = 0;
- pthread_cond_signal(&lru_crawler_cond);
- pthread_mutex_unlock(&lru_crawler_lock);
- if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) {
- fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));
- return -1;
- }
- settings.lru_crawler = false;
- return 0;
-}
-
-int start_item_crawler_thread(void) {
- int ret;
-
- if (settings.lru_crawler)
- return -1;
- pthread_mutex_lock(&lru_crawler_lock);
- do_run_lru_crawler_thread = 1;
- settings.lru_crawler = true;
- if ((ret = pthread_create(&item_crawler_tid, NULL,
- item_crawler_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create LRU crawler thread: %s\n",
- strerror(ret));
- pthread_mutex_unlock(&lru_crawler_lock);
- return -1;
- }
- pthread_mutex_unlock(&lru_crawler_lock);
-
- return 0;
-}
-
-/* 'remaining' is passed in so the LRU maintainer thread can scrub the whole
- * LRU every time.
- */
-static int do_lru_crawler_start(uint32_t id, uint32_t remaining) {
- int i;
- uint32_t sid;
- uint32_t tocrawl[3];
- int starts = 0;
- tocrawl[0] = id | HOT_LRU;
- tocrawl[1] = id | WARM_LRU;
- tocrawl[2] = id | COLD_LRU;
-
- for (i = 0; i < 3; i++) {
- sid = tocrawl[i];
- pthread_mutex_lock(&lru_locks[sid]);
- if (tails[sid] != NULL) {
- if (settings.verbose > 2)
- fprintf(stderr, "Kicking LRU crawler off for LRU %d\n", sid);
- crawlers[sid].nbytes = 0;
- crawlers[sid].nkey = 0;
- crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */
- crawlers[sid].next = 0;
- crawlers[sid].prev = 0;
- crawlers[sid].time = 0;
- crawlers[sid].remaining = remaining;
- crawlers[sid].slabs_clsid = sid;
- crawler_link_q((item *)&crawlers[sid]);
- crawler_count++;
- starts++;
- }
- pthread_mutex_unlock(&lru_locks[sid]);
- }
- if (starts) {
- STATS_LOCK();
- stats.lru_crawler_running = true;
- stats.lru_crawler_starts++;
- STATS_UNLOCK();
- pthread_mutex_lock(&lru_crawler_stats_lock);
- memset(&crawlerstats[id], 0, sizeof(crawlerstats_t));
- crawlerstats[id].start_time = current_time;
- pthread_mutex_unlock(&lru_crawler_stats_lock);
- }
- return starts;
-}
-
-static int lru_crawler_start(uint32_t id, uint32_t remaining) {
- int starts;
- if (pthread_mutex_trylock(&lru_crawler_lock) != 0) {
- return 0;
- }
- starts = do_lru_crawler_start(id, remaining);
- if (starts) {
- pthread_cond_signal(&lru_crawler_cond);
- }
- pthread_mutex_unlock(&lru_crawler_lock);
- return starts;
-}
-
-/* FIXME: Split this into two functions: one to kick a crawler for a sid, and one to
- * parse the string. LRU maintainer code is generating a string to set up a
- * sid.
- * Also only clear the crawlerstats once per sid.
- */
-enum crawler_result_type lru_crawler_crawl(char *slabs) {
- char *b = NULL;
- uint32_t sid = 0;
- int starts = 0;
- uint8_t tocrawl[MAX_NUMBER_OF_SLAB_CLASSES];
- if (pthread_mutex_trylock(&lru_crawler_lock) != 0) {
- return CRAWLER_RUNNING;
- }
-
- /* FIXME: I added this while debugging. Don't think it's needed? */
- memset(tocrawl, 0, sizeof(uint8_t) * MAX_NUMBER_OF_SLAB_CLASSES);
- if (strcmp(slabs, "all") == 0) {
- for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- tocrawl[sid] = 1;
- }
- } else {
- for (char *p = strtok_r(slabs, ",", &b);
- p != NULL;
- p = strtok_r(NULL, ",", &b)) {
-
- if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST
- || sid >= MAX_NUMBER_OF_SLAB_CLASSES-1) {
- pthread_mutex_unlock(&lru_crawler_lock);
- return CRAWLER_BADCLASS;
- }
- tocrawl[sid] = 1;
- }
- }
-
- for (sid = POWER_SMALLEST; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- if (tocrawl[sid])
- starts += do_lru_crawler_start(sid, settings.lru_crawler_tocrawl);
- }
- if (starts) {
- pthread_cond_signal(&lru_crawler_cond);
- pthread_mutex_unlock(&lru_crawler_lock);
- return CRAWLER_OK;
- } else {
- pthread_mutex_unlock(&lru_crawler_lock);
- return CRAWLER_NOTSTARTED;
- }
-}
-
-/* If we hold this lock, crawler can't wake up or move */
-void lru_crawler_pause(void) {
- pthread_mutex_lock(&lru_crawler_lock);
-}
-
-void lru_crawler_resume(void) {
- pthread_mutex_unlock(&lru_crawler_lock);
-}
-
-int init_lru_crawler(void) {
- if (lru_crawler_initialized == 0) {
- memset(&crawlerstats, 0, sizeof(crawlerstats_t) * MAX_NUMBER_OF_SLAB_CLASSES);
- if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) {
- fprintf(stderr, "Can't initialize lru crawler condition\n");
- return -1;
- }
- pthread_mutex_init(&lru_crawler_lock, NULL);
- lru_crawler_initialized = 1;
- }
- return 0;
-}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/items.h
^
|
@@ -1,8 +1,16 @@
+#define HOT_LRU 0
+#define WARM_LRU 64
+#define COLD_LRU 128
+#define TEMP_LRU 192
+
+#define CLEAR_LRU(id) (id & ~(3<<6))
+
/* See items.c */
uint64_t get_cas_id(void);
/*@null@*/
-item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes, const uint32_t cur_hv);
+item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags, const rel_time_t exptime, const int nbytes);
+item_chunk *do_item_alloc_chunk(item_chunk *ch, const size_t bytes_remain);
void item_free(item *it);
bool item_size_ok(const size_t nkey, const int flags, const int nbytes);
@@ -14,22 +22,33 @@
void do_item_update_nolock(item *it);
int do_item_replace(item *it, item *new_it, const uint32_t hv);
+int item_is_flushed(item *it);
+
+void do_item_linktail_q(item *it);
+void do_item_unlinktail_q(item *it);
+item *do_item_crawl_q(item *it);
+
+void *item_lru_bump_buf_create(void);
+
/*@null@*/
char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
void item_stats(ADD_STAT add_stats, void *c);
+void do_item_stats_add_crawl(const int i, const uint64_t reclaimed,
+ const uint64_t unfetched, const uint64_t checked);
void item_stats_totals(ADD_STAT add_stats, void *c);
/*@null@*/
void item_stats_sizes(ADD_STAT add_stats, void *c);
+void item_stats_sizes_init(void);
+void item_stats_sizes_enable(ADD_STAT add_stats, void *c);
+void item_stats_sizes_disable(ADD_STAT add_stats, void *c);
+void item_stats_sizes_add(item *it);
+void item_stats_sizes_remove(item *it);
+bool item_stats_sizes_status(void);
-item *do_item_get(const char *key, const size_t nkey, const uint32_t hv);
-item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv);
+item *do_item_get(const char *key, const size_t nkey, const uint32_t hv, conn *c, const bool do_update);
+item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv, conn *c);
void item_stats_reset(void);
extern pthread_mutex_t lru_locks[POWER_LARGEST];
-void item_stats_evictions(uint64_t *evicted);
-
-enum crawler_result_type {
- CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED
-};
int start_lru_maintainer_thread(void);
int stop_lru_maintainer_thread(void);
@@ -37,9 +56,4 @@
void lru_maintainer_pause(void);
void lru_maintainer_resume(void);
-int start_item_crawler_thread(void);
-int stop_item_crawler_thread(void);
-int init_lru_crawler(void);
-enum crawler_result_type lru_crawler_crawl(char *slabs);
-void lru_crawler_pause(void);
-void lru_crawler_resume(void);
+void *lru_bump_buf_create(void);
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/itoa_ljust.c
^
|
@@ -0,0 +1,149 @@
+//=== itoa_ljust.cpp - Fast integer to ascii conversion --*- C++ -*-//
+//
+// Substantially simplified (and slightly faster) version
+// based on the following functions in Google's protocol buffers:
+//
+// FastInt32ToBufferLeft()
+// FastUInt32ToBufferLeft()
+// FastInt64ToBufferLeft()
+// FastUInt64ToBufferLeft()
+//
+// Differences:
+// 1) Greatly simplified
+// 2) Avoids GOTO statements - uses "switch" instead and relies on
+// compiler constant folding and propagation for high performance
+// 3) Avoids unary minus of signed types - undefined behavior if value
+// is INT_MIN in platforms using two's complement representation
+// 4) Uses memcpy to store 2 digits at a time - lets the compiler
+// generate a 2-byte load/store in platforms that support
+// unaligned access, this is faster (and less code) than explicitly
+// loading and storing each byte
+//
+// Copyright (c) 2016 Arturo Martin-de-Nicolas
+// arturomdn@gmail.com
+// https://github.com/amdn/itoa_ljust/
+//
+// Released under the BSD 3-Clause License, see Google's original copyright
+// and license below.
+//===----------------------------------------------------------------------===//
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may 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 "itoa_ljust.h"
+#include <string.h>
+
+static const char lut[201] =
+ "0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+
+#define dd(u) ((const uint16_t)(lut[u]))
+
+static inline char* out2(const int d, char* p) {
+ memcpy(p, &((uint16_t *)lut)[d], 2);
+ return p + 2;
+}
+
+static inline char* out1(const char in, char* p) {
+ memcpy(p, &in, 1);
+ return p + 1;
+}
+
+static inline int digits( uint32_t u, unsigned k, int* d, char** p, int n ) {
+ if (u < k*10) {
+ *d = u / k;
+ *p = out1('0'+*d, *p);
+ --n;
+ }
+ return n;
+}
+
+static inline char* itoa(uint32_t u, char* p, int d, int n) {
+ switch(n) {
+ case 10: d = u / 100000000; p = out2( d, p );
+ case 9: u -= d * 100000000;
+ case 8: d = u / 1000000; p = out2( d, p );
+ case 7: u -= d * 1000000;
+ case 6: d = u / 10000; p = out2( d, p );
+ case 5: u -= d * 10000;
+ case 4: d = u / 100; p = out2( d, p );
+ case 3: u -= d * 100;
+ case 2: d = u / 1; p = out2( d, p );
+ case 1: ;
+ }
+ *p = '\0';
+ return p;
+}
+
+char* itoa_u32(uint32_t u, char* p) {
+ int d = 0,n;
+ if (u >=100000000) n = digits(u, 100000000, &d, &p, 10);
+ else if (u < 100) n = digits(u, 1, &d, &p, 2);
+ else if (u < 10000) n = digits(u, 100, &d, &p, 4);
+ else if (u < 1000000) n = digits(u, 10000, &d, &p, 6);
+ else n = digits(u, 1000000, &d, &p, 8);
+ return itoa( u, p, d, n );
+}
+
+char* itoa_32(int32_t i, char* p) {
+ uint32_t u = i;
+ if (i < 0) {
+ *p++ = '-';
+ u = -u;
+ }
+ return itoa_u32(u, p);
+}
+
+char* itoa_u64(uint64_t u, char* p) {
+ int d;
+
+ uint32_t lower = (uint32_t)u;
+ if (lower == u) return itoa_u32(lower, p);
+
+ uint64_t upper = u / 1000000000;
+ p = itoa_u64(upper, p);
+ lower = u - (upper * 1000000000);
+ d = lower / 100000000;
+ p = out1('0'+d,p);
+ return itoa( lower, p, d, 9 );
+}
+
+char* itoa_64(int64_t i, char* p) {
+ uint64_t u = i;
+ if (i < 0) {
+ *p++ = '-';
+ u = -u;
+ }
+ return itoa_u64(u, p);
+}
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/itoa_ljust.h
^
|
@@ -0,0 +1,28 @@
+#ifndef ITOA_LJUST_H
+#define ITOA_LJUST_H
+
+//=== itoa_ljust.h - Fast integer to ascii conversion
+//
+// Fast and simple integer to ASCII conversion:
+//
+// - 32 and 64-bit integers
+// - signed and unsigned
+// - user supplied buffer must be large enough for all decimal digits
+// in value plus minus sign if negative
+// - left-justified
+// - NUL terminated
+// - return value is pointer to NUL terminator
+//
+// Copyright (c) 2016 Arturo Martin-de-Nicolas
+// arturomdn@gmail.com
+// https://github.com/amdn/itoa_ljust/
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+
+char* itoa_u32(uint32_t u, char* buffer);
+char* itoa_32( int32_t i, char* buffer);
+char* itoa_u64(uint64_t u, char* buffer);
+char* itoa_64( int64_t i, char* buffer);
+
+#endif // ITOA_LJUST_H
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/logger.c
^
|
@@ -0,0 +1,751 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <ctype.h>
+
+#if defined(__sun)
+#include <atomic.h>
+#endif
+
+#include "memcached.h"
+#include "bipbuffer.h"
+
+#ifdef LOGGER_DEBUG
+#define L_DEBUG(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ } while (0)
+#else
+#define L_DEBUG(...)
+#endif
+
+
+/* TODO: put this in a struct and ditch the global vars. */
+static logger *logger_stack_head = NULL;
+static logger *logger_stack_tail = NULL;
+static unsigned int logger_count = 0;
+static volatile int do_run_logger_thread = 1;
+static pthread_t logger_tid;
+pthread_mutex_t logger_stack_lock = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_key_t logger_key;
+
+#if !defined(HAVE_GCC_64ATOMICS) && !defined(__sun)
+pthread_mutex_t logger_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#define WATCHER_LIMIT 20
+logger_watcher *watchers[20];
+struct pollfd watchers_pollfds[20];
+int watcher_count = 0;
+
+/* Should this go somewhere else? */
+static const entry_details default_entries[] = {
+ [LOGGER_ASCII_CMD] = {LOGGER_TEXT_ENTRY, 512, LOG_RAWCMDS, "<%d %s"},
+ [LOGGER_EVICTION] = {LOGGER_EVICTION_ENTRY, 512, LOG_EVICTIONS, NULL},
+ [LOGGER_ITEM_GET] = {LOGGER_ITEM_GET_ENTRY, 512, LOG_FETCHERS, NULL},
+ [LOGGER_ITEM_STORE] = {LOGGER_ITEM_STORE_ENTRY, 512, LOG_MUTATIONS, NULL},
+ [LOGGER_CRAWLER_STATUS] = {LOGGER_TEXT_ENTRY, 512, LOG_SYSEVENTS,
+ "type=lru_crawler crawler=%d low_mark=%llu next_reclaims=%llu since_run=%u next_run=%d elapsed=%u examined=%llu reclaimed=%llu"
+ }
+};
+
+#define WATCHER_ALL -1
+static int logger_thread_poll_watchers(int force_poll, int watcher);
+
+/*************************
+ * Util functions shared between bg thread and workers
+ *************************/
+
+/* Logger GID's can be used by watchers to put logs back into strict order
+ */
+static uint64_t logger_get_gid(void) {
+ static uint64_t logger_gid = 0;
+#ifdef HAVE_GCC_64ATOMICS
+ return __sync_add_and_fetch(&logger_gid, 1);
+#elif defined(__sun)
+ return atomic_inc_64_nv(&logger_gid);
+#else
+ mutex_lock(&logger_atomics_mutex);
+ uint64_t res = ++logger_gid;
+ mutex_unlock(&logger_atomics_mutex);
+ return res;
+#endif
+}
+
+/* TODO: genericize lists. would be nice to import queue.h if the impact is
+ * studied... otherwise can just write a local one.
+ */
+/* Add to the list of threads with a logger object */
+static void logger_link_q(logger *l) {
+ pthread_mutex_lock(&logger_stack_lock);
+ assert(l != logger_stack_head);
+
+ l->prev = 0;
+ l->next = logger_stack_head;
+ if (l->next) l->next->prev = l;
+ logger_stack_head = l;
+ if (logger_stack_tail == 0) logger_stack_tail = l;
+ logger_count++;
+ pthread_mutex_unlock(&logger_stack_lock);
+ return;
+}
+
+/* Remove from the list of threads with a logger object */
+/*static void logger_unlink_q(logger *l) {
+ pthread_mutex_lock(&logger_stack_lock);
+ if (logger_stack_head == l) {
+ assert(l->prev == 0);
+ logger_stack_head = l->next;
+ }
+ if (logger_stack_tail == l) {
+ assert(l->next == 0);
+ logger_stack_tail = l->prev;
+ }
+ assert(l->next != l);
+ assert(l->prev != l);
+
+ if (l->next) l->next->prev = l->prev;
+ if (l->prev) l->prev->next = l->next;
+ logger_count--;
+ pthread_mutex_unlock(&logger_stack_lock);
+ return;
+}*/
+
+/* Called with logger stack locked.
+ * Iterates over every watcher collecting enabled flags.
+ */
+static void logger_set_flags(void) {
+ logger *l = NULL;
+ int x = 0;
+ uint16_t f = 0; /* logger eflags */
+
+ for (x = 0; x < WATCHER_LIMIT; x++) {
+ logger_watcher *w = watchers[x];
+ if (w == NULL)
+ continue;
+
+ f |= w->eflags;
+ }
+ for (l = logger_stack_head; l != NULL; l=l->next) {
+ pthread_mutex_lock(&l->mutex);
+ l->eflags = f;
+ pthread_mutex_unlock(&l->mutex);
+ }
+ return;
+}
+
+/*************************
+ * Logger background thread functions. Aggregates per-worker buffers and
+ * writes to any watchers.
+ *************************/
+
+#define LOGGER_PARSE_SCRATCH 4096
+
+static int _logger_thread_parse_ise(logentry *e, char *scratch) {
+ int total;
+ const char *cmd = "na";
+ char keybuf[KEY_MAX_LENGTH * 3 + 1];
+ struct logentry_item_store *le = (struct logentry_item_store *) e->data;
+ const char * const status_map[] = {
+ "not_stored", "stored", "exists", "not_found", "too_large", "no_memory" };
+ const char * const cmd_map[] = {
+ "null", "add", "set", "replace", "append", "prepend", "cas" };
+
+ if (le->cmd <= 5)
+ cmd = cmd_map[le->cmd];
+
+ uriencode(le->key, keybuf, le->nkey, LOGGER_PARSE_SCRATCH);
+ total = snprintf(scratch, LOGGER_PARSE_SCRATCH,
+ "ts=%d.%d gid=%llu type=item_store key=%s status=%s cmd=%s ttl=%u clsid=%u\n",
+ (int)e->tv.tv_sec, (int)e->tv.tv_usec, (unsigned long long) e->gid,
+ keybuf, status_map[le->status], cmd, le->ttl, le->clsid);
+ return total;
+}
+
+static int _logger_thread_parse_ige(logentry *e, char *scratch) {
+ int total;
+ struct logentry_item_get *le = (struct logentry_item_get *) e->data;
+ char keybuf[KEY_MAX_LENGTH * 3 + 1];
+ const char * const was_found_map[] = {
+ "not_found", "found", "flushed", "expired" };
+
+ uriencode(le->key, keybuf, le->nkey, LOGGER_PARSE_SCRATCH);
+ total = snprintf(scratch, LOGGER_PARSE_SCRATCH,
+ "ts=%d.%d gid=%llu type=item_get key=%s status=%s clsid=%u\n",
+ (int)e->tv.tv_sec, (int)e->tv.tv_usec, (unsigned long long) e->gid,
+ keybuf, was_found_map[le->was_found], le->clsid);
+ return total;
+}
+
+static int _logger_thread_parse_ee(logentry *e, char *scratch) {
+ int total;
+ char keybuf[KEY_MAX_LENGTH * 3 + 1];
+ struct logentry_eviction *le = (struct logentry_eviction *) e->data;
+ uriencode(le->key, keybuf, le->nkey, LOGGER_PARSE_SCRATCH);
+ total = snprintf(scratch, LOGGER_PARSE_SCRATCH,
+ "ts=%d.%d gid=%llu type=eviction key=%s fetch=%s ttl=%lld la=%d clsid=%u\n",
+ (int)e->tv.tv_sec, (int)e->tv.tv_usec, (unsigned long long) e->gid,
+ keybuf, (le->it_flags & ITEM_FETCHED) ? "yes" : "no",
+ (long long int)le->exptime, le->latime, le->clsid);
+
+ return total;
+}
+
+/* Completes rendering of log line. */
+static enum logger_parse_entry_ret logger_thread_parse_entry(logentry *e, struct logger_stats *ls,
+ char *scratch, int *scratch_len) {
+ int total = 0;
+
+ switch (e->event) {
+ case LOGGER_TEXT_ENTRY:
+ total = snprintf(scratch, LOGGER_PARSE_SCRATCH, "ts=%d.%d gid=%llu %s\n",
+ (int)e->tv.tv_sec, (int)e->tv.tv_usec,
+ (unsigned long long) e->gid, (char *) e->data);
+ break;
+ case LOGGER_EVICTION_ENTRY:
+ total = _logger_thread_parse_ee(e, scratch);
+ break;
+ case LOGGER_ITEM_GET_ENTRY:
+ total = _logger_thread_parse_ige(e, scratch);
+ break;
+ case LOGGER_ITEM_STORE_ENTRY:
+ total = _logger_thread_parse_ise(e, scratch);
+ break;
+
+ }
+
+ if (total >= LOGGER_PARSE_SCRATCH || total <= 0) {
+ L_DEBUG("LOGGER: Failed to flatten log entry!\n");
+ return LOGGER_PARSE_ENTRY_FAILED;
+ } else {
+ *scratch_len = total;
+ }
+
+ return LOGGER_PARSE_ENTRY_OK;
+}
+
+/* Writes flattened entry to available watchers */
+static void logger_thread_write_entry(logentry *e, struct logger_stats *ls,
+ char *scratch, int scratch_len) {
+ int x, total;
+ /* Write the line into available watchers with matching flags */
+ for (x = 0; x < WATCHER_LIMIT; x++) {
+ logger_watcher *w = watchers[x];
+ char *skip_scr = NULL;
+ if (w == NULL || (e->eflags & w->eflags) == 0)
+ continue;
+
+ /* Avoid poll()'ing constantly when buffer is full by resetting a
+ * flag periodically.
+ */
+ while (!w->failed_flush &&
+ (skip_scr = (char *) bipbuf_request(w->buf, scratch_len + 128)) == NULL) {
+ if (logger_thread_poll_watchers(0, x) <= 0) {
+ L_DEBUG("LOGGER: Watcher had no free space for line of size (%d)\n", scratch_len + 128);
+ w->failed_flush = true;
+ }
+ }
+
+ if (w->failed_flush) {
+ L_DEBUG("LOGGER: Fast skipped for watcher [%d] due to failed_flush\n", w->sfd);
+ w->skipped++;
+ ls->watcher_skipped++;
+ continue;
+ }
+
+ if (w->skipped > 0) {
+ total = snprintf(skip_scr, 128, "skipped=%llu\n", (unsigned long long) w->skipped);
+ if (total >= 128 || total <= 0) {
+ L_DEBUG("LOGGER: Failed to flatten skipped message into watcher [%d]\n", w->sfd);
+ w->skipped++;
+ ls->watcher_skipped++;
+ continue;
+ }
+ bipbuf_push(w->buf, total);
+ w->skipped = 0;
+ }
+ /* Can't fail because bipbuf_request succeeded. */
+ bipbuf_offer(w->buf, (unsigned char *) scratch, scratch_len);
+ ls->watcher_sent++;
+ }
+}
+
+/* Called with logger stack locked.
+ * Releases every chunk associated with a watcher and closes the connection.
+ * We can't presently send a connection back to the worker for further
+ * processing.
+ */
+static void logger_thread_close_watcher(logger_watcher *w) {
+ L_DEBUG("LOGGER: Closing dead watcher\n");
+ watchers[w->id] = NULL;
+ sidethread_conn_close(w->c);
+ watcher_count--;
+ bipbuf_free(w->buf);
+ free(w);
+ logger_set_flags();
+}
+
+/* Reads a particular worker thread's available bipbuf bytes. Parses each log
+ * entry into the watcher buffers.
+ */
+static int logger_thread_read(logger *l, struct logger_stats *ls) {
+ unsigned int size;
+ unsigned int pos = 0;
+ unsigned char *data;
+ char scratch[LOGGER_PARSE_SCRATCH];
+ logentry *e;
+ pthread_mutex_lock(&l->mutex);
+ data = bipbuf_peek_all(l->buf, &size);
+ pthread_mutex_unlock(&l->mutex);
+
+ if (data == NULL) {
+ return 0;
+ }
+ L_DEBUG("LOGGER: Got %d bytes from bipbuffer\n", size);
+
+ /* parse buffer */
+ while (pos < size && watcher_count > 0) {
+ enum logger_parse_entry_ret ret;
+ int scratch_len = 0;
+ e = (logentry *) (data + pos);
+ ret = logger_thread_parse_entry(e, ls, scratch, &scratch_len);
+ if (ret != LOGGER_PARSE_ENTRY_OK) {
+ /* TODO: stats counter */
+ fprintf(stderr, "LOGGER: Failed to parse log entry\n");
+ } else {
+ logger_thread_write_entry(e, ls, scratch, scratch_len);
+ }
+ pos += sizeof(logentry) + e->size;
+ }
+ assert(pos <= size);
+
+ pthread_mutex_lock(&l->mutex);
+ data = bipbuf_poll(l->buf, size);
+ ls->worker_written += l->written;
+ ls->worker_dropped += l->dropped;
+ l->written = 0;
+ l->dropped = 0;
+ pthread_mutex_unlock(&l->mutex);
+ if (data == NULL) {
+ fprintf(stderr, "LOGGER: unexpectedly couldn't advance buf pointer\n");
+ assert(0);
+ }
+ return size; /* maybe the count of objects iterated? */
+}
+
+/* Since the event loop code isn't reusable without a refactor, and we have a
+ * limited number of potential watchers, we run our own poll loop.
+ * This calls poll() unnecessarily during write flushes, should be possible to
+ * micro-optimize later.
+ *
+ * This flushes buffers attached to watchers, iterating through the bytes set
+ * to each worker. Also checks for readability in case client connection was
+ * closed.
+ *
+ * Allows a specific watcher to be flushed (if buf full)
+ */
+static int logger_thread_poll_watchers(int force_poll, int watcher) {
+ int x;
+ int nfd = 0;
+ unsigned char *data;
+ unsigned int data_size = 0;
+ int flushed = 0;
+
+ for (x = 0; x < WATCHER_LIMIT; x++) {
+ logger_watcher *w = watchers[x];
+ if (w == NULL || (watcher != WATCHER_ALL && x != watcher))
+ continue;
+
+ data = bipbuf_peek_all(w->buf, &data_size);
+ if (data != NULL) {
+ watchers_pollfds[nfd].fd = w->sfd;
+ watchers_pollfds[nfd].events = POLLOUT;
+ nfd++;
+ } else if (force_poll) {
+ watchers_pollfds[nfd].fd = w->sfd;
+ watchers_pollfds[nfd].events = POLLIN;
+ nfd++;
+ }
+ /* This gets set after a call to poll, and should be used to gate on
+ * calling poll again.
+ */
+ w->failed_flush = false;
+ }
+
+ if (nfd == 0)
+ return 0;
+
+ //L_DEBUG("LOGGER: calling poll() [data_size: %d]\n", data_size);
+ int ret = poll(watchers_pollfds, nfd, 0);
+
+ if (ret < 0) {
+ perror("something failed with logger thread watcher fd polling");
+ return -1;
+ }
+
+ nfd = 0;
+ for (x = 0; x < WATCHER_LIMIT; x++) {
+ logger_watcher *w = watchers[x];
+ if (w == NULL)
+ continue;
+
+ data_size = 0;
+ /* Early detection of a disconnect. Otherwise we have to wait until
+ * the next write
+ */
+ if (watchers_pollfds[nfd].revents & POLLIN) {
+ char buf[1];
+ int res = read(w->sfd, buf, 1);
+ if (res == 0 || (res == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))) {
+ L_DEBUG("LOGGER: watcher closed remotely\n");
+ logger_thread_close_watcher(w);
+ nfd++;
+ continue;
+ }
+ }
+ if ((data = bipbuf_peek_all(w->buf, &data_size)) != NULL) {
+ if (watchers_pollfds[nfd].revents & (POLLHUP|POLLERR)) {
+ L_DEBUG("LOGGER: watcher closed during poll() call\n");
+ logger_thread_close_watcher(w);
+ } else if (watchers_pollfds[nfd].revents & POLLOUT) {
+ int total = 0;
+
+ /* We can write a bit. */
+ switch (w->t) {
+ case LOGGER_WATCHER_STDERR:
+ total = fwrite(data, 1, data_size, stderr);
+ break;
+ case LOGGER_WATCHER_CLIENT:
+ total = write(w->sfd, data, data_size);
+ break;
+ }
+
+ L_DEBUG("LOGGER: poll() wrote %d to %d (data_size: %d) (bipbuf_used: %d)\n", total, w->sfd,
+ data_size, bipbuf_used(w->buf));
+ if (total == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ logger_thread_close_watcher(w);
+ }
+ L_DEBUG("LOGGER: watcher hit EAGAIN\n");
+ } else if (total == 0) {
+ logger_thread_close_watcher(w);
+ } else {
+ bipbuf_poll(w->buf, total);
+ flushed += total;
+ }
+ }
+ }
+ nfd++;
+ }
+ return flushed;
+}
+
+static void logger_thread_sum_stats(struct logger_stats *ls) {
+ STATS_LOCK();
+ stats.log_worker_dropped += ls->worker_dropped;
+ stats.log_worker_written += ls->worker_written;
+ stats.log_watcher_skipped += ls->watcher_skipped;
+ stats.log_watcher_sent += ls->watcher_sent;
+ STATS_UNLOCK();
+}
+
+#define MAX_LOGGER_SLEEP 100000
+#define MIN_LOGGER_SLEEP 0
+
+/* Primary logger thread routine */
+static void *logger_thread(void *arg) {
+ useconds_t to_sleep = MIN_LOGGER_SLEEP;
+ L_DEBUG("LOGGER: Starting logger thread\n");
+ while (do_run_logger_thread) {
+ int found_logs = 0;
+ logger *l;
+ struct logger_stats ls;
+ memset(&ls, 0, sizeof(struct logger_stats));
+ if (to_sleep)
+ usleep(to_sleep);
+
+ /* Call function to iterate each logger. */
+ pthread_mutex_lock(&logger_stack_lock);
+ for (l = logger_stack_head; l != NULL; l=l->next) {
+ /* lock logger, call function to manipulate it */
+ found_logs += logger_thread_read(l, &ls);
+ }
+
+ logger_thread_poll_watchers(1, WATCHER_ALL);
+ pthread_mutex_unlock(&logger_stack_lock);
+
+ /* TODO: abstract into a function and share with lru_crawler */
+ if (!found_logs) {
+ if (to_sleep < MAX_LOGGER_SLEEP)
+ to_sleep += 50;
+ } else {
+ to_sleep /= 2;
+ if (to_sleep < 50)
+ to_sleep = MIN_LOGGER_SLEEP;
+ }
+ logger_thread_sum_stats(&ls);
+ }
+
+ return NULL;
+}
+
+static int start_logger_thread(void) {
+ int ret;
+ do_run_logger_thread = 1;
+ if ((ret = pthread_create(&logger_tid, NULL,
+ logger_thread, NULL)) != 0) {
+ fprintf(stderr, "Can't start logger thread: %s\n", strerror(ret));
+ return -1;
+ }
+ return 0;
+}
+
+// future.
+/*static int stop_logger_thread(void) {
+ do_run_logger_thread = 0;
+ pthread_join(logger_tid, NULL);
+ return 0;
+}*/
+
+/*************************
+ * Public functions for submitting logs and starting loggers from workers.
+ *************************/
+
+/* Global logger thread start/init */
+void logger_init(void) {
+ /* TODO: auto destructor when threads exit */
+ /* TODO: error handling */
+
+ /* init stack for iterating loggers */
+ logger_stack_head = 0;
+ logger_stack_tail = 0;
+ pthread_key_create(&logger_key, NULL);
+
+ if (start_logger_thread() != 0) {
+ abort();
+ }
+
+ /* This can be removed once the global stats initializer is improved */
+ STATS_LOCK();
+ stats.log_worker_dropped = 0;
+ stats.log_worker_written = 0;
+ stats.log_watcher_skipped = 0;
+ stats.log_watcher_sent = 0;
+ STATS_UNLOCK();
+ /* This is what adding a STDERR watcher looks like. should replace old
+ * "verbose" settings. */
+ //logger_add_watcher(NULL, 0);
+ return;
+}
+
+/* called *from* the thread using a logger.
+ * initializes the per-thread bipbuf, links it into the list of loggers
+ */
+logger *logger_create(void) {
+ L_DEBUG("LOGGER: Creating and linking new logger instance\n");
+ logger *l = calloc(1, sizeof(logger));
+ if (l == NULL) {
+ return NULL;
+ }
+
+ l->buf = bipbuf_new(settings.logger_buf_size);
+ if (l->buf == NULL) {
+ free(l);
+ return NULL;
+ }
+
+ l->entry_map = default_entries;
+
+ pthread_mutex_init(&l->mutex, NULL);
+ pthread_setspecific(logger_key, l);
+
+ /* add to list of loggers */
+ logger_link_q(l);
+ return l;
+}
+
+/* helpers for logger_log */
+
+static void _logger_log_evictions(logentry *e, item *it) {
+ struct logentry_eviction *le = (struct logentry_eviction *) e->data;
+ le->exptime = (it->exptime > 0) ? (long long int)(it->exptime - current_time) : (long long int) -1;
+ le->latime = current_time - it->time;
+ le->it_flags = it->it_flags;
+ le->nkey = it->nkey;
+ le->clsid = ITEM_clsid(it);
+ memcpy(le->key, ITEM_key(it), it->nkey);
+ e->size = sizeof(struct logentry_eviction) + le->nkey;
+}
+
+/* 0 == nf, 1 == found. 2 == flushed. 3 == expired.
+ * might be useful to store/print the flags an item has?
+ * could also collapse this and above code into an "item status" struct. wait
+ * for more endpoints to be written before making it generic, though.
+ * TODO: This and below should track and reprint the client fd.
+ */
+static void _logger_log_item_get(logentry *e, const int was_found, const char *key,
+ const int nkey, const uint8_t clsid) {
+ struct logentry_item_get *le = (struct logentry_item_get *) e->data;
+ le->was_found = was_found;
+ le->nkey = nkey;
+ le->clsid = clsid;
+ memcpy(le->key, key, nkey);
+ e->size = sizeof(struct logentry_item_get) + nkey;
+}
+
+static void _logger_log_item_store(logentry *e, const enum store_item_type status,
+ const int comm, char *key, const int nkey, rel_time_t ttl, const uint8_t clsid) {
+ struct logentry_item_store *le = (struct logentry_item_store *) e->data;
+ le->status = status;
+ le->cmd = comm;
+ le->nkey = nkey;
+ le->clsid = clsid;
+ if (ttl != 0) {
+ le->ttl = ttl - current_time;
+ } else {
+ le->ttl = 0;
+ }
+ memcpy(le->key, key, nkey);
+ e->size = sizeof(struct logentry_item_store) + nkey;
+}
+
+/* Public function for logging an entry.
+ * Tries to encapsulate as much of the formatting as possible to simplify the
+ * caller's code.
+ */
+enum logger_ret_type logger_log(logger *l, const enum log_entry_type event, const void *entry, ...) {
+ bipbuf_t *buf = l->buf;
+ bool nospace = false;
+ va_list ap;
+ int total = 0;
+ logentry *e;
+
+ const entry_details *d = &l->entry_map[event];
+ int reqlen = d->reqlen;
+
+ pthread_mutex_lock(&l->mutex);
+ /* Request a maximum length of data to write to */
+ e = (logentry *) bipbuf_request(buf, (sizeof(logentry) + reqlen));
+ if (e == NULL) {
+ pthread_mutex_unlock(&l->mutex);
+ l->dropped++;
+ return LOGGER_RET_NOSPACE;
+ }
+ e->gid = logger_get_gid();
+ e->event = d->subtype;
+ /* TODO: Could pass this down as an argument now that we're using
+ * LOGGER_LOG() macro.
+ */
+ e->eflags = d->eflags;
+ /* Noting time isn't optional. A feature may be added to avoid rendering
+ * time and/or gid to a logger.
+ */
+ gettimeofday(&e->tv, NULL);
+
+ switch (d->subtype) {
+ case LOGGER_TEXT_ENTRY:
+ va_start(ap, entry);
+ total = vsnprintf((char *) e->data, reqlen, d->format, ap);
+ va_end(ap);
+ if (total >= reqlen || total <= 0) {
+ fprintf(stderr, "LOGGER: Failed to vsnprintf a text entry: (total) %d\n", total);
+ break;
+ }
+ e->size = total + 1; /* null byte */
+
+ break;
+ case LOGGER_EVICTION_ENTRY:
+ _logger_log_evictions(e, (item *)entry);
+ break;
+ case LOGGER_ITEM_GET_ENTRY:
+ va_start(ap, entry);
+ int was_found = va_arg(ap, int);
+ char *key = va_arg(ap, char *);
+ size_t nkey = va_arg(ap, size_t);
+ uint8_t gclsid = va_arg(ap, int);
+ _logger_log_item_get(e, was_found, key, nkey, gclsid);
+ va_end(ap);
+ break;
+ case LOGGER_ITEM_STORE_ENTRY:
+ va_start(ap, entry);
+ enum store_item_type status = va_arg(ap, enum store_item_type);
+ int comm = va_arg(ap, int);
+ char *skey = va_arg(ap, char *);
+ size_t snkey = va_arg(ap, size_t);
+ rel_time_t sttl = va_arg(ap, rel_time_t);
+ uint8_t sclsid = va_arg(ap, int);
+ _logger_log_item_store(e, status, comm, skey, snkey, sttl, sclsid);
+ break;
+ }
+
+ /* Push pointer forward by the actual amount required */
+ if (bipbuf_push(buf, (sizeof(logentry) + e->size)) == 0) {
+ fprintf(stderr, "LOGGER: Failed to bipbuf push a text entry\n");
+ pthread_mutex_unlock(&l->mutex);
+ return LOGGER_RET_ERR;
+ }
+ l->written++;
+ L_DEBUG("LOGGER: Requested %d bytes, wrote %lu bytes\n", reqlen,
+ (sizeof(logentry) + e->size));
+
+ pthread_mutex_unlock(&l->mutex);
+
+ if (nospace) {
+ return LOGGER_RET_NOSPACE;
+ } else {
+ return LOGGER_RET_OK;
+ }
+}
+
+/* Passes a client connection socket from a primary worker thread to the
+ * logger thread. Caller *must* event_del() the client before handing it over.
+ * Presently there's no way to hand the client back to the worker thread.
+ */
+enum logger_add_watcher_ret logger_add_watcher(void *c, const int sfd, uint16_t f) {
+ int x;
+ logger_watcher *w = NULL;
+ pthread_mutex_lock(&logger_stack_lock);
+ if (watcher_count >= WATCHER_LIMIT) {
+ return LOGGER_ADD_WATCHER_TOO_MANY;
+ }
+
+ for (x = 0; x < WATCHER_LIMIT; x++) {
+ if (watchers[x] == NULL)
+ break;
+ }
+
+ w = calloc(1, sizeof(logger_watcher));
+ if (w == NULL) {
+ pthread_mutex_unlock(&logger_stack_lock);
+ return LOGGER_ADD_WATCHER_FAILED;
+ }
+ w->c = c;
+ w->sfd = sfd;
+ if (sfd == 0 && c == NULL) {
+ w->t = LOGGER_WATCHER_STDERR;
+ } else {
+ w->t = LOGGER_WATCHER_CLIENT;
+ }
+ w->id = x;
+ w->eflags = f;
+ w->buf = bipbuf_new(settings.logger_watcher_buf_size);
+ if (w->buf == NULL) {
+ free(w);
+ pthread_mutex_unlock(&logger_stack_lock);
+ return LOGGER_ADD_WATCHER_FAILED;
+ }
+ bipbuf_offer(w->buf, (unsigned char *) "OK\r\n", 4);
+
+ watchers[x] = w;
+ watcher_count++;
+ /* Update what flags the global logs will watch */
+ logger_set_flags();
+
+ pthread_mutex_unlock(&logger_stack_lock);
+ return LOGGER_ADD_WATCHER_OK;
+}
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/logger.h
^
|
@@ -0,0 +1,163 @@
+/* logging functions */
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include "bipbuffer.h"
+
+/* TODO: starttime tunable */
+#define LOGGER_BUF_SIZE 1024 * 64
+#define LOGGER_WATCHER_BUF_SIZE 1024 * 256
+#define LOGGER_ENTRY_MAX_SIZE 2048
+#define GET_LOGGER() ((logger *) pthread_getspecific(logger_key));
+
+/* Inlined from memcached.h - should go into sub header */
+typedef unsigned int rel_time_t;
+
+enum log_entry_type {
+ LOGGER_ASCII_CMD = 0,
+ LOGGER_EVICTION,
+ LOGGER_ITEM_GET,
+ LOGGER_ITEM_STORE,
+ LOGGER_CRAWLER_STATUS,
+};
+
+enum log_entry_subtype {
+ LOGGER_TEXT_ENTRY = 0,
+ LOGGER_EVICTION_ENTRY,
+ LOGGER_ITEM_GET_ENTRY,
+ LOGGER_ITEM_STORE_ENTRY
+};
+
+enum logger_ret_type {
+ LOGGER_RET_OK = 0,
+ LOGGER_RET_NOSPACE,
+ LOGGER_RET_ERR
+};
+
+enum logger_parse_entry_ret {
+ LOGGER_PARSE_ENTRY_OK = 0,
+ LOGGER_PARSE_ENTRY_FULLBUF,
+ LOGGER_PARSE_ENTRY_FAILED
+};
+
+typedef const struct {
+ enum log_entry_subtype subtype;
+ int reqlen;
+ uint16_t eflags;
+ char *format;
+} entry_details;
+
+/* log entry intermediary structures */
+struct logentry_eviction {
+ long long int exptime;
+ uint32_t latime;
+ uint16_t it_flags;
+ uint8_t nkey;
+ uint8_t clsid;
+ char key[];
+};
+
+struct logentry_item_get {
+ uint8_t was_found;
+ uint8_t nkey;
+ uint8_t clsid;
+ char key[];
+};
+
+struct logentry_item_store {
+ int status;
+ int cmd;
+ rel_time_t ttl;
+ uint8_t nkey;
+ uint8_t clsid;
+ char key[];
+};
+
+/* end intermediary structures */
+
+typedef struct _logentry {
+ enum log_entry_subtype event;
+ uint16_t eflags;
+ uint64_t gid;
+ struct timeval tv; /* not monotonic! */
+ int size;
+ union {
+ void *entry; /* probably an item */
+ char end;
+ } data[];
+} logentry;
+
+#define LOG_SYSEVENTS (1<<1) /* threads start/stop/working */
+#define LOG_FETCHERS (1<<2) /* get/gets/etc */
+#define LOG_MUTATIONS (1<<3) /* set/append/incr/etc */
+#define LOG_SYSERRORS (1<<4) /* malloc/etc errors */
+#define LOG_CONNEVENTS (1<<5) /* new client, closed, etc */
+#define LOG_EVICTIONS (1<<6) /* defailts of evicted items */
+#define LOG_STRICT (1<<7) /* block worker instead of drop */
+#define LOG_RAWCMDS (1<<9) /* raw ascii commands */
+
+typedef struct _logger {
+ struct _logger *prev;
+ struct _logger *next;
+ pthread_mutex_t mutex; /* guard for this + *buf */
+ uint64_t written; /* entries written to the buffer */
+ uint64_t dropped; /* entries dropped */
+ uint64_t blocked; /* times blocked instead of dropped */
+ uint16_t fetcher_ratio; /* log one out of every N fetches */
+ uint16_t mutation_ratio; /* log one out of every N mutations */
+ uint16_t eflags; /* flags this logger should log */
+ bipbuf_t *buf;
+ const entry_details *entry_map;
+} logger;
+
+enum logger_watcher_type {
+ LOGGER_WATCHER_STDERR = 0,
+ LOGGER_WATCHER_CLIENT = 1
+};
+
+typedef struct {
+ void *c; /* original connection structure. still with source thread attached */
+ int sfd; /* client fd */
+ int id; /* id number for watcher list */
+ uint64_t skipped; /* lines skipped since last successful print */
+ bool failed_flush; /* recently failed to write out (EAGAIN), wait before retry */
+ enum logger_watcher_type t; /* stderr, client, syslog, etc */
+ uint16_t eflags; /* flags we are interested in */
+ bipbuf_t *buf; /* per-watcher output buffer */
+} logger_watcher;
+
+
+struct logger_stats {
+ uint64_t worker_dropped;
+ uint64_t worker_written;
+ uint64_t watcher_skipped;
+ uint64_t watcher_sent;
+};
+
+extern pthread_key_t logger_key;
+
+/* public functions */
+
+void logger_init(void);
+logger *logger_create(void);
+
+#define LOGGER_LOG(l, flag, type, ...) \
+ do { \
+ logger *myl = l; \
+ if (l == NULL) \
+ myl = GET_LOGGER(); \
+ if (myl->eflags & flag) \
+ logger_log(myl, type, __VA_ARGS__); \
+ } while (0)
+
+enum logger_ret_type logger_log(logger *l, const enum log_entry_type event, const void *entry, ...);
+
+enum logger_add_watcher_ret {
+ LOGGER_ADD_WATCHER_TOO_MANY = 0,
+ LOGGER_ADD_WATCHER_OK,
+ LOGGER_ADD_WATCHER_FAILED
+};
+
+enum logger_add_watcher_ret logger_add_watcher(void *c, const int sfd, uint16_t f);
+
+#endif
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/memcached.c
^
|
@@ -50,8 +50,13 @@
/* FreeBSD 4.x doesn't have IOV_MAX exposed. */
#ifndef IOV_MAX
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GNU__)
# define IOV_MAX 1024
+/* GNU/Hurd don't set MAXPATHLEN
+ * http://www.gnu.org/software/hurd/hurd/porting/guidelines.html#PATH_MAX_tt_MAX_PATH_tt_MAXPATHL */
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 4096
+#endif
#endif
#endif
@@ -73,6 +78,7 @@
static enum try_read_result try_read_udp(conn *c);
static void conn_set_state(conn *c, enum conn_states state);
+static int start_conn_timeout_thread();
/* stats */
static void stats_init(void);
@@ -94,6 +100,7 @@
static void write_and_free(conn *c, char *buf, int bytes);
static int ensure_iov_space(conn *c);
static int add_iov(conn *c, const void *buf, int len);
+static int add_chunked_item_iovs(conn *c, item *it, int len);
static int add_msghdr(conn *c);
static void write_bin_error(conn *c, protocol_binary_response_status err,
const char *errstr, int swallow);
@@ -102,6 +109,7 @@
/** exported globals **/
struct stats stats;
+struct stats_state stats_state;
struct settings settings;
time_t process_started; /* when the process was started */
conn **conns;
@@ -172,19 +180,9 @@
}
static void stats_init(void) {
- stats.curr_items = stats.total_items = stats.curr_conns = stats.total_conns = stats.conn_structs = 0;
- stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = stats.reclaimed = 0;
- stats.touch_cmds = stats.touch_misses = stats.touch_hits = stats.rejected_conns = 0;
- stats.malloc_fails = 0;
- stats.curr_bytes = stats.listen_disabled_num = 0;
- stats.hash_power_level = stats.hash_bytes = stats.hash_is_expanding = 0;
- stats.expired_unfetched = stats.evicted_unfetched = 0;
- stats.slabs_moved = 0;
- stats.lru_maintainer_juggles = 0;
- stats.accepting_conns = true; /* assuming we start in this state. */
- stats.slab_reassign_running = false;
- stats.lru_crawler_running = false;
- stats.lru_crawler_starts = 0;
+ memset(&stats, 0, sizeof(struct stats));
+ memset(&stats_state, 0, sizeof(struct stats_state));
+ stats_state.accepting_conns = true; /* assuming we start in this state. */
/* make the time we started always be 2 seconds before we really
did, so time(0) - time.started is never zero. if so, things
@@ -196,12 +194,7 @@
static void stats_reset(void) {
STATS_LOCK();
- stats.total_items = stats.total_conns = 0;
- stats.rejected_conns = 0;
- stats.malloc_fails = 0;
- stats.evictions = 0;
- stats.reclaimed = 0;
- stats.listen_disabled_num = 0;
+ memset(&stats, 0, sizeof(struct stats));
stats_prefix_clear();
STATS_UNLOCK();
threadlocal_stats_reset();
@@ -232,21 +225,33 @@
settings.backlog = 1024;
settings.binding_protocol = negotiating_prot;
settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */
+ settings.slab_page_size = 1024 * 1024; /* chunks are split from 1MB pages. */
+ settings.slab_chunk_size_max = settings.slab_page_size;
+ settings.sasl = false;
settings.maxconns_fast = false;
settings.lru_crawler = false;
settings.lru_crawler_sleep = 100;
settings.lru_crawler_tocrawl = 0;
settings.lru_maintainer_thread = false;
+ settings.lru_segmented = false;
settings.hot_lru_pct = 32;
settings.warm_lru_pct = 32;
- settings.expirezero_does_not_evict = false;
+ settings.hot_max_age = 3600;
+ settings.warm_max_factor = 2.0;
+ settings.inline_ascii_response = true;
+ settings.temp_lru = false;
+ settings.temporary_ttl = 61;
+ settings.idle_timeout = 0; /* disabled */
settings.hashpower_init = 0;
settings.slab_reassign = false;
settings.slab_automove = 0;
settings.shutdown_command = false;
settings.tail_repair_time = TAIL_REPAIR_TIME_DEFAULT;
settings.flush_enabled = true;
+ settings.dump_enabled = true;
settings.crawls_persleep = 1000;
+ settings.logger_watcher_buf_size = LOGGER_WATCHER_BUF_SIZE;
+ settings.logger_buf_size = LOGGER_BUF_SIZE;
}
/*
@@ -298,6 +303,87 @@
extern pthread_mutex_t conn_lock;
+/* Connection timeout thread bits */
+static pthread_t conn_timeout_tid;
+
+#define CONNS_PER_SLICE 100
+#define TIMEOUT_MSG_SIZE (1 + sizeof(int))
+static void *conn_timeout_thread(void *arg) {
+ int i;
+ conn *c;
+ char buf[TIMEOUT_MSG_SIZE];
+ rel_time_t oldest_last_cmd;
+ int sleep_time;
+ useconds_t timeslice = 1000000 / (max_fds / CONNS_PER_SLICE);
+
+ while(1) {
+ if (settings.verbose > 2)
+ fprintf(stderr, "idle timeout thread at top of connection list\n");
+
+ oldest_last_cmd = current_time;
+
+ for (i = 0; i < max_fds; i++) {
+ if ((i % CONNS_PER_SLICE) == 0) {
+ if (settings.verbose > 2)
+ fprintf(stderr, "idle timeout thread sleeping for %ulus\n",
+ timeslice);
+ usleep(timeslice);
+ }
+
+ if (!conns[i])
+ continue;
+
+ c = conns[i];
+
+ if (!IS_TCP(c->transport))
+ continue;
+
+ if (c->state != conn_new_cmd && c->state != conn_read)
+ continue;
+
+ if ((current_time - c->last_cmd_time) > settings.idle_timeout) {
+ buf[0] = 't';
+ memcpy(&buf[1], &i, sizeof(int));
+ if (write(c->thread->notify_send_fd, buf, TIMEOUT_MSG_SIZE)
+ != TIMEOUT_MSG_SIZE)
+ perror("Failed to write timeout to notify pipe");
+ } else {
+ if (c->last_cmd_time < oldest_last_cmd)
+ oldest_last_cmd = c->last_cmd_time;
+ }
+ }
+
+ /* This is the soonest we could have another connection time out */
+ sleep_time = settings.idle_timeout - (current_time - oldest_last_cmd) + 1;
+ if (sleep_time <= 0)
+ sleep_time = 1;
+
+ if (settings.verbose > 2)
+ fprintf(stderr,
+ "idle timeout thread finished pass, sleeping for %ds\n",
+ sleep_time);
+ usleep((useconds_t) sleep_time * 1000000);
+ }
+
+ return NULL;
+}
+
+static int start_conn_timeout_thread() {
+ int ret;
+
+ if (settings.idle_timeout == 0)
+ return -1;
+
+ if ((ret = pthread_create(&conn_timeout_tid, NULL,
+ conn_timeout_thread, NULL)) != 0) {
+ fprintf(stderr, "Can't create idle connection timeout thread: %s\n",
+ strerror(ret));
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Initializes the connections array. We don't actually allocate connection
* structures until they're needed, so as to avoid wasting memory when the
@@ -349,6 +435,38 @@
return rv;
}
+void conn_close_idle(conn *c) {
+ if (settings.idle_timeout > 0 &&
+ (current_time - c->last_cmd_time) > settings.idle_timeout) {
+ if (c->state != conn_new_cmd && c->state != conn_read) {
+ if (settings.verbose > 1)
+ fprintf(stderr,
+ "fd %d wants to timeout, but isn't in read state", c->sfd);
+ return;
+ }
+
+ if (settings.verbose > 1)
+ fprintf(stderr, "Closing idle fd %d\n", c->sfd);
+
+ c->thread->stats.idle_kicks++;
+
+ conn_set_state(c, conn_closing);
+ drive_machine(c);
+ }
+}
+
+/* bring conn back from a sidethread. could have had its event base moved. */
+void conn_worker_readd(conn *c) {
+ c->ev_flags = EV_READ | EV_PERSIST;
+ event_set(&c->event, c->sfd, c->ev_flags, event_handler, (void *)c);
+ event_base_set(c->thread->base, &c->event);
+ c->state = conn_new_cmd;
+
+ if (event_add(&c->event, 0) == -1) {
+ perror("event_add");
+ }
+}
+
conn *conn_new(const int sfd, enum conn_states init_state,
const int event_flags,
const int read_buffer_size, enum network_transport transport,
@@ -401,7 +519,7 @@
}
STATS_LOCK();
- stats.conn_structs++;
+ stats_state.conn_structs++;
STATS_UNLOCK();
c->sfd = sfd;
@@ -463,6 +581,7 @@
c->msgcurr = 0;
c->msgused = 0;
c->authenticated = false;
+ c->last_cmd_time = current_time; /* initialize for idle kicker */
c->write_and_go = init_state;
c->write_and_free = 0;
@@ -480,7 +599,7 @@
}
STATS_LOCK();
- stats.curr_conns++;
+ stats_state.curr_conns++;
stats.total_conns++;
STATS_UNLOCK();
@@ -507,7 +626,7 @@
if (c->suffixleft != 0) {
for (; c->suffixleft > 0; c->suffixleft--, c->suffixcurr++) {
- cache_free(c->thread->suffix_cache, *(c->suffixcurr));
+ do_cache_free(c->thread->suffix_cache, *(c->suffixcurr));
}
}
@@ -584,7 +703,7 @@
pthread_mutex_unlock(&conn_lock);
STATS_LOCK();
- stats.curr_conns--;
+ stats_state.curr_conns--;
STATS_UNLOCK();
return;
@@ -662,7 +781,8 @@
"conn_swallow",
"conn_closing",
"conn_mwrite",
- "conn_closed" };
+ "conn_closed",
+ "conn_watch" };
return statenames[state];
}
@@ -727,27 +847,58 @@
* connection.
*
* Returns 0 on success, -1 on out-of-memory.
+ * Note: This is a hot path for at least ASCII protocol. While there is
+ * redundant code in splitting TCP/UDP handling, any reduction in steps has a
+ * large impact for TCP connections.
*/
static int add_iov(conn *c, const void *buf, int len) {
struct msghdr *m;
int leftover;
- bool limit_to_mtu;
assert(c != NULL);
- do {
- m = &c->msglist[c->msgused - 1];
+ if (IS_UDP(c->transport)) {
+ do {
+ m = &c->msglist[c->msgused - 1];
- /*
- * Limit UDP packets, and the first payloads of TCP replies, to
- * UDP_MAX_PAYLOAD_SIZE bytes.
- */
- limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused);
+ /*
+ * Limit UDP packets to UDP_MAX_PAYLOAD_SIZE bytes.
+ */
+
+ /* We may need to start a new msghdr if this one is full. */
+ if (m->msg_iovlen == IOV_MAX ||
+ (c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
+ add_msghdr(c);
+ m = &c->msglist[c->msgused - 1];
+ }
+
+ if (ensure_iov_space(c) != 0)
+ return -1;
- /* We may need to start a new msghdr if this one is full. */
- if (m->msg_iovlen == IOV_MAX ||
- (limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
+ /* If the fragment is too big to fit in the datagram, split it up */
+ if (len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
+ leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
+ len -= leftover;
+ } else {
+ leftover = 0;
+ }
+
+ m = &c->msglist[c->msgused - 1];
+ m->msg_iov[m->msg_iovlen].iov_base = (void *)buf;
+ m->msg_iov[m->msg_iovlen].iov_len = len;
+
+ c->msgbytes += len;
+ c->iovused++;
+ m->msg_iovlen++;
+
+ buf = ((char *)buf) + len;
+ len = leftover;
+ } while (leftover > 0);
+ } else {
+ /* Optimized path for TCP connections */
+ m = &c->msglist[c->msgused - 1];
+ if (m->msg_iovlen == IOV_MAX) {
add_msghdr(c);
m = &c->msglist[c->msgused - 1];
}
@@ -755,29 +906,29 @@
if (ensure_iov_space(c) != 0)
return -1;
- /* If the fragment is too big to fit in the datagram, split it up */
- if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
- leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
- len -= leftover;
- } else {
- leftover = 0;
- }
-
- m = &c->msglist[c->msgused - 1];
m->msg_iov[m->msg_iovlen].iov_base = (void *)buf;
m->msg_iov[m->msg_iovlen].iov_len = len;
-
c->msgbytes += len;
c->iovused++;
m->msg_iovlen++;
-
- buf = ((char *)buf) + len;
- len = leftover;
- } while (leftover > 0);
+ }
return 0;
}
+static int add_chunked_item_iovs(conn *c, item *it, int len) {
+ assert(it->it_flags & ITEM_CHUNKED);
+ item_chunk *ch = (item_chunk *) ITEM_data(it);
+ while (ch) {
+ int todo = (len > ch->used) ? ch->used : len;
+ if (add_iov(c, ch->data, todo) != 0) {
+ return -1;
+ }
+ ch = ch->next;
+ len -= todo;
+ }
+ return 0;
+}
/*
* Constructs a set of UDP headers and attaches them to the outgoing messages.
@@ -893,12 +1044,41 @@
item *it = c->item;
int comm = c->cmd;
enum store_item_type ret;
+ bool is_valid = false;
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
- if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {
+ if ((it->it_flags & ITEM_CHUNKED) == 0) {
+ if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) == 0) {
+ is_valid = true;
+ }
+ } else {
+ char buf[2];
+ /* should point to the final item chunk */
+ item_chunk *ch = (item_chunk *) c->ritem;
+ assert(ch->used != 0);
+ /* :( We need to look at the last two bytes. This could span two
+ * chunks.
+ */
+ if (ch->used > 1) {
+ buf[0] = ch->data[ch->used - 2];
+ buf[1] = ch->data[ch->used - 1];
+ } else {
+ assert(ch->prev);
+ assert(ch->used == 1);
+ buf[0] = ch->prev->data[ch->prev->used - 1];
+ buf[1] = ch->data[ch->used - 1];
+ }
+ if (strncmp(buf, "\r\n", 2) == 0) {
+ is_valid = true;
+ } else {
+ assert(1 == 0);
+ }
+ }
+
+ if (!is_valid) {
out_string(c, "CLIENT_ERROR bad data chunk");
} else {
ret = store_item(it, comm, c);
@@ -1210,8 +1390,19 @@
/* We don't actually receive the trailing two characters in the bin
* protocol, so we're going to just set them here */
- *(ITEM_data(it) + it->nbytes - 2) = '\r';
- *(ITEM_data(it) + it->nbytes - 1) = '\n';
+ if ((it->it_flags & ITEM_CHUNKED) == 0) {
+ *(ITEM_data(it) + it->nbytes - 2) = '\r';
+ *(ITEM_data(it) + it->nbytes - 1) = '\n';
+ } else {
+ assert(c->ritem);
+ item_chunk *ch = (item_chunk *) c->ritem;
+ if (ch->size == ch->used)
+ ch = ch->next;
+ assert(ch->size - ch->used >= 2);
+ ch->data[ch->used + 1] = '\r';
+ ch->data[ch->used + 2] = '\n';
+ ch->used += 2;
+ }
ret = store_item(it, c->cmd, c);
@@ -1253,6 +1444,8 @@
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
break;
case NOT_STORED:
+ case TOO_LARGE:
+ case NO_MEMORY:
if (c->cmd == NREAD_ADD) {
eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
} else if(c->cmd == NREAD_REPLACE) {
@@ -1282,7 +1475,7 @@
if (settings.verbose > 1) {
fprintf(stderr, "<%d %s ", c->sfd, should_touch ? "TOUCH" : "GET");
- fwrite(key, 1, nkey, stderr);
+ if (fwrite(key, 1, nkey, stderr)) {}
fputc('\n', stderr);
}
@@ -1290,9 +1483,9 @@
protocol_binary_request_touch *t = binary_get_request(c);
time_t exptime = ntohl(t->message.body.expiration);
- it = item_touch(key, nkey, realtime(exptime));
+ it = item_touch(key, nkey, realtime(exptime), c);
} else {
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DO_UPDATE);
}
if (it) {
@@ -1300,7 +1493,6 @@
uint16_t keylen = 0;
uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
- item_update(it);
pthread_mutex_lock(&c->thread->stats.mutex);
if (should_touch) {
c->thread->stats.touch_cmds++;
@@ -1330,7 +1522,11 @@
rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
// add the flags
- rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
+ if (settings.inline_ascii_response) {
+ rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
+ } else {
+ rsp->message.body.flags = htonl(*((uint32_t *)ITEM_suffix(it)));
+ }
add_iov(c, &rsp->message.body, sizeof(rsp->message.body));
if (should_return_key) {
@@ -1339,7 +1535,11 @@
if (should_return_value) {
/* Add the data minus the CRLF */
- add_iov(c, ITEM_data(it), it->nbytes - 2);
+ if ((it->it_flags & ITEM_CHUNKED) == 0) {
+ add_iov(c, ITEM_data(it), it->nbytes - 2);
+ } else {
+ add_chunked_item_iovs(c, it, it->nbytes - 2);
+ }
}
conn_set_state(c, conn_mwrite);
@@ -1698,9 +1898,10 @@
char *key = binary_get_key(c);
assert(key);
- item *it = item_alloc(key, nkey, 0, 0, vlen);
+ item *it = item_alloc(key, nkey, 0, 0, vlen+2);
- if (it == 0) {
+ /* Can't use a chunked item for SASL authentication. */
+ if (it == 0 || (it->it_flags & ITEM_CHUNKED)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, NULL, vlen);
c->write_and_go = conn_swallow;
return;
@@ -1724,6 +1925,13 @@
int nkey = c->binary_header.request.keylen;
int vlen = c->binary_header.request.bodylen - nkey;
+ if (nkey > ((item*) c->item)->nkey) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
+ c->write_and_go = conn_swallow;
+ item_unlink(c->item);
+ return;
+ }
+
char mech[nkey+1];
memcpy(mech, ITEM_key((item*)c->item), nkey);
mech[nkey] = 0x00;
@@ -1733,6 +1941,13 @@
const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
+ if (vlen > ((item*) c->item)->nbytes) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
+ c->write_and_go = conn_swallow;
+ item_unlink(c->item);
+ return;
+ }
+
int result=-1;
switch (c->cmd) {
@@ -1816,10 +2031,16 @@
static void dispatch_bin_command(conn *c) {
int protocol_error = 0;
- int extlen = c->binary_header.request.extlen;
- int keylen = c->binary_header.request.keylen;
+ uint8_t extlen = c->binary_header.request.extlen;
+ uint16_t keylen = c->binary_header.request.keylen;
uint32_t bodylen = c->binary_header.request.bodylen;
+ if (keylen > bodylen || keylen + extlen > bodylen) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, 0);
+ c->write_and_go = conn_closing;
+ return;
+ }
+
if (settings.sasl && !authenticated(c)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
c->write_and_go = conn_closing;
@@ -2042,16 +2263,24 @@
realtime(req->message.body.expiration), vlen+2);
if (it == 0) {
+ enum store_item_type status;
if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
+ status = TOO_LARGE;
} else {
out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
- }
+ /* This error generating method eats the swallow value. Add here. */
+ c->sbytes = vlen;
+ status = NO_MEMORY;
+ }
+ /* FIXME: losing c->cmd since it's translated below. refactor? */
+ LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
+ NULL, status, 0, key, nkey, it->exptime, ITEM_clsid(it));
/* Avoid stale data persisting in cache because we failed alloc.
* Unacceptable for SET. Anywhere else too? */
if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DONT_UPDATE);
if (it) {
item_unlink(it);
item_remove(it);
@@ -2117,6 +2346,8 @@
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
} else {
out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
+ /* OOM calls eat the swallow value. Add here. */
+ c->sbytes = vlen;
}
/* swallow the data line */
c->write_and_go = conn_swallow;
@@ -2201,7 +2432,7 @@
stats_prefix_record_delete(key, nkey);
}
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DONT_UPDATE);
if (it) {
uint64_t cas = ntohll(req->message.header.request.cas);
if (cas == 0 || cas == ITEM_get_cas(it)) {
@@ -2294,6 +2525,104 @@
}
}
+/* Destination must always be chunked */
+/* This should be part of item.c */
+static int _store_item_copy_chunks(item *d_it, item *s_it, const int len) {
+ item_chunk *dch = (item_chunk *) ITEM_data(d_it);
+ /* Advance dch until we find free space */
+ while (dch->size == dch->used) {
+ if (dch->next) {
+ dch = dch->next;
+ } else {
+ break;
+ }
+ }
+
+ if (s_it->it_flags & ITEM_CHUNKED) {
+ int remain = len;
+ item_chunk *sch = (item_chunk *) ITEM_data(s_it);
+ int copied = 0;
+ /* Fills dch's to capacity, not straight copy sch in case data is
+ * being added or removed (ie append/prepend)
+ */
+ while (sch && dch && remain) {
+ assert(dch->used <= dch->size);
+ int todo = (dch->size - dch->used < sch->used - copied)
+ ? dch->size - dch->used : sch->used - copied;
+ if (remain < todo)
+ todo = remain;
+ memcpy(dch->data + dch->used, sch->data + copied, todo);
+ dch->used += todo;
+ copied += todo;
+ remain -= todo;
+ assert(dch->used <= dch->size);
+ if (dch->size == dch->used) {
+ item_chunk *tch = do_item_alloc_chunk(dch, remain);
+ if (tch) {
+ dch = tch;
+ } else {
+ return -1;
+ }
+ }
+ assert(copied <= sch->used);
+ if (copied == sch->used) {
+ copied = 0;
+ sch = sch->next;
+ }
+ }
+ /* assert that the destination had enough space for the source */
+ assert(remain == 0);
+ } else {
+ int done = 0;
+ /* Fill dch's via a non-chunked item. */
+ while (len > done && dch) {
+ int todo = (dch->size - dch->used < len - done)
+ ? dch->size - dch->used : len - done;
+ //assert(dch->size - dch->used != 0);
+ memcpy(dch->data + dch->used, ITEM_data(s_it) + done, todo);
+ done += todo;
+ dch->used += todo;
+ assert(dch->used <= dch->size);
+ if (dch->size == dch->used) {
+ item_chunk *tch = do_item_alloc_chunk(dch, len - done);
+ if (tch) {
+ dch = tch;
+ } else {
+ return -1;
+ }
+ }
+ }
+ assert(len == done);
+ }
+ return 0;
+}
+
+static int _store_item_copy_data(int comm, item *old_it, item *new_it, item *add_it) {
+ if (comm == NREAD_APPEND) {
+ if (new_it->it_flags & ITEM_CHUNKED) {
+ if (_store_item_copy_chunks(new_it, old_it, old_it->nbytes - 2) == -1 ||
+ _store_item_copy_chunks(new_it, add_it, add_it->nbytes) == -1) {
+ return -1;
+ }
+ } else {
+ memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
+ memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(add_it), add_it->nbytes);
+ }
+ } else {
+ /* NREAD_PREPEND */
+ if (new_it->it_flags & ITEM_CHUNKED) {
+ if (_store_item_copy_chunks(new_it, add_it, add_it->nbytes - 2) == -1 ||
+ _store_item_copy_chunks(new_it, old_it, old_it->nbytes) == -1) {
+ return -1;
+ }
+ } else {
+ memcpy(ITEM_data(new_it), ITEM_data(add_it), add_it->nbytes);
+ memcpy(ITEM_data(new_it) + add_it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
+ }
+ }
+ return 0;
+}
+
/*
* Stores an item in the cache according to the semantics of one of the set
* commands. In threaded mode, this is protected by the cache lock.
@@ -2302,11 +2631,11 @@
*/
enum store_item_type do_store_item(item *it, int comm, conn *c, const uint32_t hv) {
char *key = ITEM_key(it);
- item *old_it = do_item_get(key, it->nkey, hv);
+ item *old_it = do_item_get(key, it->nkey, hv, c, DONT_UPDATE);
enum store_item_type stored = NOT_STORED;
item *new_it = NULL;
- int flags;
+ uint32_t flags;
if (old_it != NULL && comm == NREAD_ADD) {
/* add only adds a nonexistent item, but promote to head of LRU */
@@ -2347,6 +2676,7 @@
stored = EXISTS;
}
} else {
+ int failed_alloc = 0;
/*
* Append - combine new and old record into single one. Here it's
* atomic and thread-safe.
@@ -2366,34 +2696,28 @@
/* we have it and old_it here - alloc memory to hold both */
/* flags was already lost - so recover them from ITEM_suffix(it) */
- flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);
-
- new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */, hv);
-
- if (new_it == NULL) {
- /* SERVER_ERROR out of memory */
- if (old_it != NULL)
- do_item_remove(old_it);
-
- return NOT_STORED;
+ if (settings.inline_ascii_response) {
+ flags = (uint32_t) strtoul(ITEM_suffix(old_it), (char **) NULL, 10);
+ } else {
+ flags = *((uint32_t *)ITEM_suffix(old_it));
}
- /* copy data from it and old_it to new_it */
+ new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);
- if (comm == NREAD_APPEND) {
- memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
- memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
+ /* copy data from it and old_it to new_it */
+ if (new_it == NULL || _store_item_copy_data(comm, old_it, new_it, it) == -1) {
+ failed_alloc = 1;
+ stored = NOT_STORED;
+ // failed data copy, free up.
+ if (new_it != NULL)
+ item_remove(new_it);
} else {
- /* NREAD_PREPEND */
- memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
- memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
+ it = new_it;
}
-
- it = new_it;
}
}
- if (stored == NOT_STORED) {
+ if (stored == NOT_STORED && failed_alloc == 0) {
if (old_it != NULL)
item_replace(old_it, it, hv);
else
@@ -2413,6 +2737,8 @@
if (stored == STORED) {
c->cas = ITEM_get_cas(it);
}
+ LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE, NULL,
+ stored, comm, ITEM_key(it), it->nkey, it->exptime, ITEM_clsid(it));
return stored;
}
@@ -2592,19 +2918,21 @@
(long)usage.ru_stime.tv_usec);
#endif /* !WIN32 */
- APPEND_STAT("curr_connections", "%u", stats.curr_conns - 1);
- APPEND_STAT("total_connections", "%u", stats.total_conns);
+ APPEND_STAT("curr_connections", "%llu", (unsigned long long)stats_state.curr_conns - 1);
+ APPEND_STAT("total_connections", "%llu", (unsigned long long)stats.total_conns);
if (settings.maxconns_fast) {
APPEND_STAT("rejected_connections", "%llu", (unsigned long long)stats.rejected_conns);
}
- APPEND_STAT("connection_structures", "%u", stats.conn_structs);
- APPEND_STAT("reserved_fds", "%u", stats.reserved_fds);
+ APPEND_STAT("connection_structures", "%u", stats_state.conn_structs);
+ APPEND_STAT("reserved_fds", "%u", stats_state.reserved_fds);
APPEND_STAT("cmd_get", "%llu", (unsigned long long)thread_stats.get_cmds);
APPEND_STAT("cmd_set", "%llu", (unsigned long long)slab_stats.set_cmds);
APPEND_STAT("cmd_flush", "%llu", (unsigned long long)thread_stats.flush_cmds);
APPEND_STAT("cmd_touch", "%llu", (unsigned long long)thread_stats.touch_cmds);
APPEND_STAT("get_hits", "%llu", (unsigned long long)slab_stats.get_hits);
APPEND_STAT("get_misses", "%llu", (unsigned long long)thread_stats.get_misses);
+ APPEND_STAT("get_expired", "%llu", (unsigned long long)thread_stats.get_expired);
+ APPEND_STAT("get_flushed", "%llu", (unsigned long long)thread_stats.get_flushed);
APPEND_STAT("delete_misses", "%llu", (unsigned long long)thread_stats.delete_misses);
APPEND_STAT("delete_hits", "%llu", (unsigned long long)slab_stats.delete_hits);
APPEND_STAT("incr_misses", "%llu", (unsigned long long)thread_stats.incr_misses);
@@ -2618,22 +2946,31 @@
APPEND_STAT("touch_misses", "%llu", (unsigned long long)thread_stats.touch_misses);
APPEND_STAT("auth_cmds", "%llu", (unsigned long long)thread_stats.auth_cmds);
APPEND_STAT("auth_errors", "%llu", (unsigned long long)thread_stats.auth_errors);
+ if (settings.idle_timeout) {
+ APPEND_STAT("idle_kicks", "%llu", (unsigned long long)thread_stats.idle_kicks);
+ }
APPEND_STAT("bytes_read", "%llu", (unsigned long long)thread_stats.bytes_read);
APPEND_STAT("bytes_written", "%llu", (unsigned long long)thread_stats.bytes_written);
APPEND_STAT("limit_maxbytes", "%llu", (unsigned long long)settings.maxbytes);
- APPEND_STAT("accepting_conns", "%u", stats.accepting_conns);
+ APPEND_STAT("accepting_conns", "%u", stats_state.accepting_conns);
APPEND_STAT("listen_disabled_num", "%llu", (unsigned long long)stats.listen_disabled_num);
+ APPEND_STAT("time_in_listen_disabled_us", "%llu", stats.time_in_listen_disabled_us);
APPEND_STAT("threads", "%d", settings.num_threads);
APPEND_STAT("conn_yields", "%llu", (unsigned long long)thread_stats.conn_yields);
- APPEND_STAT("hash_power_level", "%u", stats.hash_power_level);
- APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats.hash_bytes);
- APPEND_STAT("hash_is_expanding", "%u", stats.hash_is_expanding);
+ APPEND_STAT("hash_power_level", "%u", stats_state.hash_power_level);
+ APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats_state.hash_bytes);
+ APPEND_STAT("hash_is_expanding", "%u", stats_state.hash_is_expanding);
if (settings.slab_reassign) {
- APPEND_STAT("slab_reassign_running", "%u", stats.slab_reassign_running);
+ APPEND_STAT("slab_reassign_rescues", "%llu", stats.slab_reassign_rescues);
+ APPEND_STAT("slab_reassign_chunk_rescues", "%llu", stats.slab_reassign_chunk_rescues);
+ APPEND_STAT("slab_reassign_evictions_nomem", "%llu", stats.slab_reassign_evictions_nomem);
+ APPEND_STAT("slab_reassign_inline_reclaim", "%llu", stats.slab_reassign_inline_reclaim);
+ APPEND_STAT("slab_reassign_busy_items", "%llu", stats.slab_reassign_busy_items);
+ APPEND_STAT("slab_reassign_running", "%u", stats_state.slab_reassign_running);
APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved);
}
if (settings.lru_crawler) {
- APPEND_STAT("lru_crawler_running", "%u", stats.lru_crawler_running);
+ APPEND_STAT("lru_crawler_running", "%u", stats_state.lru_crawler_running);
APPEND_STAT("lru_crawler_starts", "%u", stats.lru_crawler_starts);
}
if (settings.lru_maintainer_thread) {
@@ -2641,6 +2978,10 @@
}
APPEND_STAT("malloc_fails", "%llu",
(unsigned long long)stats.malloc_fails);
+ APPEND_STAT("log_worker_dropped", "%llu", (unsigned long long)stats.log_worker_dropped);
+ APPEND_STAT("log_worker_written", "%llu", (unsigned long long)stats.log_worker_written);
+ APPEND_STAT("log_watcher_skipped", "%llu", (unsigned long long)stats.log_watcher_skipped);
+ APPEND_STAT("log_watcher_sent", "%llu", (unsigned long long)stats.log_watcher_sent);
STATS_UNLOCK();
}
@@ -2675,16 +3016,27 @@
APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);
APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");
APPEND_STAT("slab_automove", "%d", settings.slab_automove);
+ APPEND_STAT("slab_chunk_max", "%d", settings.slab_chunk_size_max);
APPEND_STAT("lru_crawler", "%s", settings.lru_crawler ? "yes" : "no");
APPEND_STAT("lru_crawler_sleep", "%d", settings.lru_crawler_sleep);
APPEND_STAT("lru_crawler_tocrawl", "%lu", (unsigned long)settings.lru_crawler_tocrawl);
APPEND_STAT("tail_repair_time", "%d", settings.tail_repair_time);
APPEND_STAT("flush_enabled", "%s", settings.flush_enabled ? "yes" : "no");
+ APPEND_STAT("dump_enabled", "%s", settings.dump_enabled ? "yes" : "no");
APPEND_STAT("hash_algorithm", "%s", settings.hash_algorithm);
APPEND_STAT("lru_maintainer_thread", "%s", settings.lru_maintainer_thread ? "yes" : "no");
+ APPEND_STAT("lru_segmented", "%s", settings.lru_segmented ? "yes" : "no");
APPEND_STAT("hot_lru_pct", "%d", settings.hot_lru_pct);
- APPEND_STAT("warm_lru_pct", "%d", settings.hot_lru_pct);
- APPEND_STAT("expirezero_does_not_evict", "%s", settings.expirezero_does_not_evict ? "yes" : "no");
+ APPEND_STAT("warm_lru_pct", "%d", settings.warm_lru_pct);
+ APPEND_STAT("hot_max_age", "%u", settings.hot_max_age);
+ APPEND_STAT("warm_max_factor", "%.2f", settings.warm_max_factor);
+ APPEND_STAT("temp_lru", "%s", settings.temp_lru ? "yes" : "no");
+ APPEND_STAT("temporary_ttl", "%u", settings.temporary_ttl);
+ APPEND_STAT("idle_timeout", "%d", settings.idle_timeout);
+ APPEND_STAT("watcher_logbuf_size", "%u", settings.logger_watcher_buf_size);
+ APPEND_STAT("worker_logbuf_size", "%u", settings.logger_buf_size);
+ APPEND_STAT("track_sizes", "%s", item_stats_sizes_status() ? "yes" : "no");
+ APPEND_STAT("inline_ascii_response", "%s", settings.inline_ascii_response ? "yes" : "no");
}
static void conn_to_str(const conn *c, char *buf) {
@@ -2824,6 +3176,11 @@
char *buf;
unsigned int bytes, id, limit = 0;
+ if (!settings.dump_enabled) {
+ out_string(c, "CLIENT_ERROR stats cachedump not allowed");
+ return;
+ }
+
if (ntokens < 5) {
out_string(c, "CLIENT_ERROR bad command line");
return;
@@ -2835,7 +3192,7 @@
return;
}
- if (id >= MAX_NUMBER_OF_SLAB_CLASSES-1) {
+ if (id >= MAX_NUMBER_OF_SLAB_CLASSES) {
out_string(c, "CLIENT_ERROR Illegal slab id");
return;
}
@@ -2872,6 +3229,26 @@
}
}
+static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas) {
+ char *p;
+ if (!settings.inline_ascii_response) {
+ *suffix = ' ';
+ p = itoa_u32(*((uint32_t *) ITEM_suffix(it)), suffix+1);
+ *p = ' ';
+ p = itoa_u32(it->nbytes-2, p+1);
+ } else {
+ p = suffix;
+ }
+ if (return_cas) {
+ *p = ' ';
+ p = itoa_u64(ITEM_get_cas(it), p+1);
+ }
+ *p = '\r';
+ *(p+1) = '\n';
+ *(p+2) = '\0';
+ return (p - suffix) + 2;
+}
+
/* ntokens is overwritten here... shrug.. */
static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
char *key;
@@ -2896,7 +3273,7 @@
return;
}
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DO_UPDATE);
if (settings.detail_enabled) {
stats_prefix_record_get(key, nkey, NULL != it);
}
@@ -2923,7 +3300,7 @@
* " " + flags + " " + data length + "\r\n" + data (with \r\n)
*/
- if (return_cas)
+ if (return_cas || !settings.inline_ascii_response)
{
MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
@@ -2943,7 +3320,7 @@
}
}
- suffix = cache_alloc(c->thread->suffix_cache);
+ suffix = do_cache_alloc(c->thread->suffix_cache);
if (suffix == NULL) {
STATS_LOCK();
stats.malloc_fails++;
@@ -2956,27 +3333,41 @@
return;
}
*(c->suffixlist + i) = suffix;
- int suffix_len = snprintf(suffix, SUFFIX_SIZE,
- " %llu\r\n",
- (unsigned long long)ITEM_get_cas(it));
+ int suffix_len = make_ascii_get_suffix(suffix, it, return_cas);
if (add_iov(c, "VALUE ", 6) != 0 ||
add_iov(c, ITEM_key(it), it->nkey) != 0 ||
- add_iov(c, ITEM_suffix(it), it->nsuffix - 2) != 0 ||
- add_iov(c, suffix, suffix_len) != 0 ||
- add_iov(c, ITEM_data(it), it->nbytes) != 0)
+ (settings.inline_ascii_response && add_iov(c, ITEM_suffix(it), it->nsuffix - 2) != 0) ||
+ add_iov(c, suffix, suffix_len) != 0)
{
item_remove(it);
break;
}
+ if ((it->it_flags & ITEM_CHUNKED) == 0) {
+ add_iov(c, ITEM_data(it), it->nbytes);
+ } else if (add_chunked_item_iovs(c, it, it->nbytes) != 0) {
+ item_remove(it);
+ break;
+ }
}
else
{
MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
if (add_iov(c, "VALUE ", 6) != 0 ||
- add_iov(c, ITEM_key(it), it->nkey) != 0 ||
- add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0)
+ add_iov(c, ITEM_key(it), it->nkey) != 0)
+ {
+ item_remove(it);
+ break;
+ }
+ if ((it->it_flags & ITEM_CHUNKED) == 0)
{
+ if (add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0)
+ {
+ item_remove(it);
+ break;
+ }
+ } else if (add_iov(c, ITEM_suffix(it), it->nsuffix) != 0 ||
+ add_chunked_item_iovs(c, it, it->nbytes) != 0) {
item_remove(it);
break;
}
@@ -2997,7 +3388,6 @@
c->thread->stats.slab_stats[ITEM_clsid(it)].get_hits++;
c->thread->stats.get_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
- item_update(it);
*(c->ilist + i) = it;
i++;
@@ -3025,7 +3415,7 @@
c->icurr = c->ilist;
c->ileft = i;
- if (return_cas) {
+ if (return_cas || !settings.inline_ascii_response) {
c->suffixcurr = c->suffixlist;
c->suffixleft = i;
}
@@ -3094,11 +3484,11 @@
}
}
- vlen += 2;
- if (vlen < 0 || vlen - 2 < 0) {
+ if (vlen < 0 || vlen > (INT_MAX - 2)) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
+ vlen += 2;
if (settings.detail_enabled) {
stats_prefix_record_set(key, nkey);
@@ -3107,10 +3497,16 @@
it = item_alloc(key, nkey, flags, realtime(exptime), vlen);
if (it == 0) {
- if (! item_size_ok(nkey, flags, vlen))
+ enum store_item_type status;
+ if (! item_size_ok(nkey, flags, vlen)) {
out_string(c, "SERVER_ERROR object too large for cache");
- else
+ status = TOO_LARGE;
+ } else {
out_of_memory(c, "SERVER_ERROR out of memory storing object");
+ status = NO_MEMORY;
+ }
+ LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
+ NULL, status, comm, key, nkey, 0, 0);
/* swallow the data line */
c->write_and_go = conn_swallow;
c->sbytes = vlen;
@@ -3118,7 +3514,7 @@
/* Avoid stale data persisting in cache because we failed alloc.
* Unacceptable for SET. Anywhere else too? */
if (comm == NREAD_SET) {
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DONT_UPDATE);
if (it) {
item_unlink(it);
item_remove(it);
@@ -3159,9 +3555,8 @@
return;
}
- it = item_touch(key, nkey, realtime(exptime_int));
+ it = item_touch(key, nkey, realtime(exptime_int), c);
if (it) {
- item_update(it);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.touch_cmds++;
c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
@@ -3248,13 +3643,14 @@
int res;
item *it;
- it = do_item_get(key, nkey, hv);
+ it = do_item_get(key, nkey, hv, c, DONT_UPDATE);
if (!it) {
return DELTA_ITEM_NOT_FOUND;
}
/* Can't delta zero byte values. 2-byte are the "\r\n" */
- if (it->nbytes <= 2) {
+ /* Also can't delta for chunked items. Too large to be a number */
+ if (it->nbytes <= 2 || (it->it_flags & ITEM_CHUNKED) != 0) {
return NON_NUMERIC;
}
@@ -3298,14 +3694,24 @@
if (res + 2 <= it->nbytes && it->refcount == 2) { /* replace in-place */
/* When changing the value without replacing the item, we
need to update the CAS on the existing item. */
+ /* We also need to fiddle it in the sizes tracker in case the tracking
+ * was enabled at runtime, since it relies on the CAS value to know
+ * whether to remove an item or not. */
+ item_stats_sizes_remove(it);
ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
-
+ item_stats_sizes_add(it);
memcpy(ITEM_data(it), buf, res);
memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
do_item_update(it);
} else if (it->refcount > 1) {
item *new_it;
- new_it = do_item_alloc(ITEM_key(it), it->nkey, atoi(ITEM_suffix(it) + 1), it->exptime, res + 2, hv);
+ uint32_t flags;
+ if (settings.inline_ascii_response) {
+ flags = (uint32_t) strtoul(ITEM_suffix(it)+1, (char **) NULL, 10);
+ } else {
+ flags = *((uint32_t *)ITEM_suffix(it));
+ }
+ new_it = do_item_alloc(ITEM_key(it), it->nkey, flags, it->exptime, res + 2);
if (new_it == 0) {
do_item_remove(it);
return EOM;
@@ -3367,7 +3773,7 @@
stats_prefix_record_delete(key, nkey);
}
- it = item_get(key, nkey);
+ it = item_get(key, nkey, c, DONT_UPDATE);
if (it) {
MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
@@ -3420,6 +3826,130 @@
return;
}
+/* TODO: decide on syntax for sampling? */
+static void process_watch_command(conn *c, token_t *tokens, const size_t ntokens) {
+ uint16_t f = 0;
+ int x;
+ assert(c != NULL);
+
+ set_noreply_maybe(c, tokens, ntokens);
+ if (ntokens > 2) {
+ for (x = COMMAND_TOKEN + 1; x < ntokens - 1; x++) {
+ if ((strcmp(tokens[x].value, "rawcmds") == 0)) {
+ f |= LOG_RAWCMDS;
+ } else if ((strcmp(tokens[x].value, "evictions") == 0)) {
+ f |= LOG_EVICTIONS;
+ } else if ((strcmp(tokens[x].value, "fetchers") == 0)) {
+ f |= LOG_FETCHERS;
+ } else if ((strcmp(tokens[x].value, "mutations") == 0)) {
+ f |= LOG_MUTATIONS;
+ } else if ((strcmp(tokens[x].value, "sysevents") == 0)) {
+ f |= LOG_SYSEVENTS;
+ } else {
+ out_string(c, "ERROR");
+ return;
+ }
+ }
+ } else {
+ f |= LOG_FETCHERS;
+ }
+
+ switch(logger_add_watcher(c, c->sfd, f)) {
+ case LOGGER_ADD_WATCHER_TOO_MANY:
+ out_string(c, "WATCHER_TOO_MANY log watcher limit reached");
+ break;
+ case LOGGER_ADD_WATCHER_FAILED:
+ out_string(c, "WATCHER_FAILED failed to add log watcher");
+ break;
+ case LOGGER_ADD_WATCHER_OK:
+ conn_set_state(c, conn_watch);
+ event_del(&c->event);
+ break;
+ }
+}
+
+static void process_memlimit_command(conn *c, token_t *tokens, const size_t ntokens) {
+ uint32_t memlimit;
+ assert(c != NULL);
+
+ set_noreply_maybe(c, tokens, ntokens);
+
+ if (!safe_strtoul(tokens[1].value, &memlimit)) {
+ out_string(c, "ERROR");
+ } else {
+ if (memlimit < 8) {
+ out_string(c, "MEMLIMIT_TOO_SMALL cannot set maxbytes to less than 8m");
+ } else {
+ if (slabs_adjust_mem_limit((size_t) memlimit * 1024 * 1024)) {
+ if (settings.verbose > 0) {
+ fprintf(stderr, "maxbytes adjusted to %llum\n", (unsigned long long)memlimit);
+ }
+
+ out_string(c, "OK");
+ } else {
+ out_string(c, "MEMLIMIT_ADJUST_FAILED out of bounds or unable to adjust");
+ }
+ }
+ }
+}
+
+static void process_lru_command(conn *c, token_t *tokens, const size_t ntokens) {
+ uint32_t pct_hot;
+ uint32_t pct_warm;
+ uint32_t hot_age;
+ int32_t ttl;
+ double factor;
+
+ set_noreply_maybe(c, tokens, ntokens);
+
+ if (strcmp(tokens[1].value, "tune") == 0 && ntokens >= 7) {
+ if (!safe_strtoul(tokens[2].value, &pct_hot) ||
+ !safe_strtoul(tokens[3].value, &pct_warm) ||
+ !safe_strtoul(tokens[4].value, &hot_age) ||
+ !safe_strtod(tokens[5].value, &factor)) {
+ out_string(c, "ERROR");
+ } else {
+ if (pct_hot + pct_warm > 80) {
+ out_string(c, "ERROR hot and warm pcts must not exceed 80");
+ } else if (factor <= 0) {
+ out_string(c, "ERROR cold age factor must be greater than 0");
+ } else {
+ settings.hot_lru_pct = pct_hot;
+ settings.warm_lru_pct = pct_warm;
+ settings.hot_max_age = hot_age;
+ settings.warm_max_factor = factor;
+ out_string(c, "OK");
+ }
+ }
+ } else if (strcmp(tokens[1].value, "mode") == 0 && ntokens >= 3 &&
+ settings.lru_maintainer_thread) {
+ if (strcmp(tokens[2].value, "flat") == 0) {
+ settings.lru_segmented = false;
+ out_string(c, "OK");
+ } else if (strcmp(tokens[2].value, "segmented") == 0) {
+ settings.lru_segmented = true;
+ out_string(c, "OK");
+ } else {
+ out_string(c, "ERROR");
+ }
+ } else if (strcmp(tokens[1].value, "temp_ttl") == 0 && ntokens >= 3 &&
+ settings.lru_maintainer_thread) {
+ if (!safe_strtol(tokens[2].value, &ttl)) {
+ out_string(c, "ERROR");
+ } else {
+ if (ttl < 0) {
+ settings.temp_lru = false;
+ } else {
+ settings.temp_lru = true;
+ settings.temporary_ttl = ttl;
+ }
+ out_string(c, "OK");
+ }
+ } else {
+ out_string(c, "ERROR");
+ }
+}
+
static void process_command(conn *c, char *command) {
token_t tokens[MAX_TOKENS];
@@ -3603,7 +4133,7 @@
return;
}
- rv = lru_crawler_crawl(tokens[2].value);
+ rv = lru_crawler_crawl(tokens[2].value, CRAWLER_EXPIRED, NULL, 0);
switch(rv) {
case CRAWLER_OK:
out_string(c, "OK");
@@ -3617,6 +4147,42 @@
case CRAWLER_NOTSTARTED:
out_string(c, "NOTSTARTED no items to crawl");
break;
+ case CRAWLER_ERROR:
+ out_string(c, "ERROR an unknown error happened");
+ break;
+ }
+ return;
+ } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "metadump") == 0) {
+ if (settings.lru_crawler == false) {
+ out_string(c, "CLIENT_ERROR lru crawler disabled");
+ return;
+ }
+ if (!settings.dump_enabled) {
+ out_string(c, "ERROR metadump not allowed");
+ return;
+ }
+
+ int rv = lru_crawler_crawl(tokens[2].value, CRAWLER_METADUMP,
+ c, c->sfd);
+ switch(rv) {
+ case CRAWLER_OK:
+ out_string(c, "OK");
+ // TODO: Don't reuse conn_watch here.
+ conn_set_state(c, conn_watch);
+ event_del(&c->event);
+ break;
+ case CRAWLER_RUNNING:
+ out_string(c, "BUSY currently processing crawler request");
+ break;
+ case CRAWLER_BADCLASS:
+ out_string(c, "BADCLASS invalid class id");
+ break;
+ case CRAWLER_NOTSTARTED:
+ out_string(c, "NOTSTARTED no items to crawl");
+ break;
+ case CRAWLER_ERROR:
+ out_string(c, "ERROR an unknown error happened");
+ break;
}
return;
} else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "tocrawl") == 0) {
@@ -3661,8 +4227,14 @@
} else {
out_string(c, "ERROR");
}
+ } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "watch") == 0) {
+ process_watch_command(c, tokens, ntokens);
+ } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "cache_memlimit") == 0)) {
+ process_memlimit_command(c, tokens, ntokens);
} else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0)) {
process_verbosity_command(c, tokens, ntokens);
+ } else if (ntokens >= 3 && strcmp(tokens[COMMAND_TOKEN].value, "lru") == 0) {
+ process_lru_command(c, tokens, ntokens);
} else {
out_string(c, "ERROR");
}
@@ -3952,12 +4524,20 @@
}
if (do_accept) {
+ struct timeval maxconns_exited;
+ uint64_t elapsed_us;
+ gettimeofday(&maxconns_exited,NULL);
STATS_LOCK();
- stats.accepting_conns = true;
+ elapsed_us =
+ (maxconns_exited.tv_sec - stats.maxconns_entered.tv_sec) * 1000000
+ + (maxconns_exited.tv_usec - stats.maxconns_entered.tv_usec);
+ stats.time_in_listen_disabled_us += elapsed_us;
+ stats_state.accepting_conns = true;
STATS_UNLOCK();
} else {
STATS_LOCK();
- stats.accepting_conns = false;
+ stats_state.accepting_conns = false;
+ gettimeofday(&stats.maxconns_entered,NULL);
stats.listen_disabled_num++;
STATS_UNLOCK();
allow_new_conns = false;
@@ -4032,6 +4612,90 @@
}
}
+/* Does a looped read to fill data chunks */
+/* TODO: restrict number of times this can loop.
+ * Also, benchmark using readv's.
+ */
+static int read_into_chunked_item(conn *c) {
+ int total = 0;
+ int res;
+ assert(c->rcurr != c->ritem);
+
+ while (c->rlbytes > 0) {
+ item_chunk *ch = (item_chunk *)c->ritem;
+ assert(ch->used <= ch->size);
+ if (ch->size == ch->used) {
+ // FIXME: ch->next is currently always 0. remove this?
+ if (ch->next) {
+ c->ritem = (char *) ch->next;
+ } else {
+ /* Allocate next chunk. Binary protocol needs 2b for \r\n */
+ c->ritem = (char *) do_item_alloc_chunk(ch, c->rlbytes +
+ ((c->protocol == binary_prot) ? 2 : 0));
+ if (!c->ritem) {
+ // We failed an allocation. Let caller handle cleanup.
+ total = -2;
+ break;
+ }
+ // ritem has new chunk, restart the loop.
+ continue;
+ //assert(c->rlbytes == 0);
+ }
+ }
+
+ int unused = ch->size - ch->used;
+ /* first check if we have leftovers in the conn_read buffer */
+ if (c->rbytes > 0) {
+ total = 0;
+ int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
+ tocopy = tocopy > unused ? unused : tocopy;
+ if (c->ritem != c->rcurr) {
+ memmove(ch->data + ch->used, c->rcurr, tocopy);
+ }
+ total += tocopy;
+ c->rlbytes -= tocopy;
+ c->rcurr += tocopy;
+ c->rbytes -= tocopy;
+ ch->used += tocopy;
+ if (c->rlbytes == 0) {
+ break;
+ }
+ } else {
+ /* now try reading from the socket */
+ res = read(c->sfd, ch->data + ch->used,
+ (unused > c->rlbytes ? c->rlbytes : unused));
+ if (res > 0) {
+ pthread_mutex_lock(&c->thread->stats.mutex);
+ c->thread->stats.bytes_read += res;
+ pthread_mutex_unlock(&c->thread->stats.mutex);
+ ch->used += res;
+ total += res;
+ c->rlbytes -= res;
+ } else {
+ /* Reset total to the latest result so caller can handle it */
+ total = res;
+ break;
+ }
+ }
+ }
+
+ /* At some point I will be able to ditch the \r\n from item storage and
+ remove all of these kludges.
+ The above binprot check ensures inline space for \r\n, but if we do
+ exactly enough allocs there will be no additional chunk for \r\n.
+ */
+ if (c->rlbytes == 0 && c->protocol == binary_prot && total >= 0) {
+ item_chunk *ch = (item_chunk *)c->ritem;
+ if (ch->size - ch->used < 2) {
+ c->ritem = (char *) do_item_alloc_chunk(ch, 2);
+ if (!c->ritem) {
+ total = -2;
+ }
+ }
+ }
+ return total;
+}
+
static void drive_machine(conn *c) {
bool stop = false;
int sfd;
@@ -4091,7 +4755,7 @@
}
if (settings.maxconns_fast &&
- stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
+ stats_state.curr_conns + stats_state.reserved_fds >= settings.maxconns - 1) {
str = "ERROR Too many open connections\r\n";
res = write(sfd, str, strlen(str));
close(sfd);
@@ -4100,7 +4764,7 @@
STATS_UNLOCK();
} else {
dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
- DATA_BUFFER_SIZE, tcp_transport);
+ DATA_BUFFER_SIZE, c->transport);
}
stop = true;
@@ -4189,38 +4853,46 @@
break;
}
- /* first check if we have leftovers in the conn_read buffer */
- if (c->rbytes > 0) {
- int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
- if (c->ritem != c->rcurr) {
- memmove(c->ritem, c->rcurr, tocopy);
+ if (!c->item || (((item *)c->item)->it_flags & ITEM_CHUNKED) == 0) {
+ /* first check if we have leftovers in the conn_read buffer */
+ if (c->rbytes > 0) {
+ int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
+ if (c->ritem != c->rcurr) {
+ memmove(c->ritem, c->rcurr, tocopy);
+ }
+ c->ritem += tocopy;
+ c->rlbytes -= tocopy;
+ c->rcurr += tocopy;
+ c->rbytes -= tocopy;
+ if (c->rlbytes == 0) {
+ break;
+ }
}
- c->ritem += tocopy;
- c->rlbytes -= tocopy;
- c->rcurr += tocopy;
- c->rbytes -= tocopy;
- if (c->rlbytes == 0) {
+
+ /* now try reading from the socket */
+ res = read(c->sfd, c->ritem, c->rlbytes);
+ if (res > 0) {
+ pthread_mutex_lock(&c->thread->stats.mutex);
+ c->thread->stats.bytes_read += res;
+ pthread_mutex_unlock(&c->thread->stats.mutex);
+ if (c->rcurr == c->ritem) {
+ c->rcurr += res;
+ }
+ c->ritem += res;
+ c->rlbytes -= res;
break;
}
+ } else {
+ res = read_into_chunked_item(c);
+ if (res > 0)
+ break;
}
- /* now try reading from the socket */
- res = read(c->sfd, c->ritem, c->rlbytes);
- if (res > 0) {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_read += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- if (c->rcurr == c->ritem) {
- c->rcurr += res;
- }
- c->ritem += res;
- c->rlbytes -= res;
- break;
- }
if (res == 0) { /* end of stream */
conn_set_state(c, conn_closing);
break;
}
+
if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (!update_event(c, EV_READ | EV_PERSIST)) {
if (settings.verbose > 0)
@@ -4231,6 +4903,14 @@
stop = true;
break;
}
+
+ /* Memory allocation failure */
+ if (res == -2) {
+ out_of_memory(c, "SERVER_ERROR Out of memory during read");
+ c->sbytes = c->rlbytes;
+ c->write_and_go = conn_swallow;
+ break;
+ }
/* otherwise we have a real error, on which we close the connection */
if (settings.verbose > 0) {
fprintf(stderr, "Failed to read, and not due to blocking:\n"
@@ -4358,6 +5038,10 @@
abort();
break;
+ case conn_watch:
+ /* We handed off our connection to the logger thread. */
+ stop = true;
+ break;
case conn_max_state:
assert(false);
break;
@@ -4614,15 +5298,40 @@
p != NULL;
p = strtok_r(NULL, ";,", &b)) {
int the_port = port;
+
+ char *h = NULL;
+ if (*p == '[') {
+ // expecting it to be an IPv6 address enclosed in []
+ // i.e. RFC3986 style recommended by RFC5952
+ char *e = strchr(p, ']');
+ if (e == NULL) {
+ fprintf(stderr, "Invalid IPV6 address: \"%s\"", p);
+ return 1;
+ }
+ h = ++p; // skip the opening '['
+ *e = '\0';
+ p = ++e; // skip the closing ']'
+ }
+
char *s = strchr(p, ':');
if (s != NULL) {
- *s = '\0';
- ++s;
- if (!safe_strtol(s, &the_port)) {
- fprintf(stderr, "Invalid port number: \"%s\"", s);
- return 1;
+ // If no more semicolons - attempt to treat as port number.
+ // Otherwise the only valid option is an unenclosed IPv6 without port, until
+ // of course there was an RFC3986 IPv6 address previously specified -
+ // in such a case there is no good option, will just send it to fail as port number.
+ if (strchr(s + 1, ':') == NULL || h != NULL) {
+ *s = '\0';
+ ++s;
+ if (!safe_strtol(s, &the_port)) {
+ fprintf(stderr, "Invalid port number: \"%s\"", s);
+ return 1;
+ }
}
}
+
+ if (h != NULL)
+ p = h;
+
if (strcmp(p, "*") == 0) {
p = NULL;
}
@@ -4819,7 +5528,7 @@
" requests process for a given connection to prevent \n"
" starvation (default: 20)\n");
printf("-C Disable use of CAS\n");
- printf("-b Set the backlog queue limit (default: 1024)\n");
+ printf("-b <num> Set the backlog queue limit (default: 1024)\n");
printf("-B Binding protocol - one of ascii, binary, or auto (default)\n");
printf("-I Override the size of each slab page. Adjusts max item size\n"
" (default: 1mb, min: 1k, max: 128m)\n");
@@ -4827,8 +5536,9 @@
printf("-S Turn on Sasl authentication\n");
#endif
printf("-F Disable flush_all command\n");
+ printf("-X Disable stats cachedump and lru_crawler metadump commands\n");
printf("-o Comma separated list of extended or experimental options\n"
- " - (EXPERIMENTAL) maxconns_fast: immediately close new\n"
+ " - maxconns_fast: immediately close new\n"
" connections if over maxconns limit\n"
" - hashpower: An integer multiplier for how large the hash\n"
" table should be. Can be grown at runtime if not big enough.\n"
@@ -4849,8 +5559,21 @@
" (requires lru_maintainer)\n"
" - warm_lru_pct: Pct of slab memory to reserve for warm lru.\n"
" (requires lru_maintainer)\n"
- " - expirezero_does_not_evict: Items set to not expire, will not evict.\n"
+ " - hot_max_age: Items idle longer than this drop from hot lru.\n"
+ " - cold_max_factor: Items idle longer than cold lru age * this drop from warm.\n"
+ " - temporary_ttl: TTL's below this use separate LRU, cannot be evicted.\n"
" (requires lru_maintainer)\n"
+ " - idle_timeout: Timeout for idle connections\n"
+ " - (EXPERIMENTAL) slab_chunk_max: Maximum slab size. Do not change without extreme care.\n"
+ " - watcher_logbuf_size: Size in kilobytes of per-watcher write buffer.\n"
+ " - worker_logbuf_Size: Size in kilobytes of per-worker-thread buffer\n"
+ " read by background thread. Which is then written to watchers.\n"
+ " - track_sizes: Enable dynamic reports for 'stats sizes' command.\n"
+ " - no_inline_ascii_resp: Save up to 24 bytes per item. Small perf hit in ASCII,\n"
+ " no perf difference in binary protocol. Speeds up sets.\n"
+ " - modern: Enables 'modern' defaults. Options that will be default in future.\n"
+ " enables: slab_chunk_max:512k,slab_reassign,slab_automove=1,maxconns_fast,\n"
+ " hash_algorithm=murmur3,lru_crawler,lru_maintainer,no_inline_ascii_resp\n"
);
return;
}
@@ -5045,7 +5768,7 @@
if (ever != NULL) {
if (strncmp(ever, "1.", 2) == 0) {
/* Require at least 1.3 (that's still a couple of years old) */
- if ((ever[2] == '1' || ever[2] == '2') && !isdigit(ever[3])) {
+ if (('0' <= ever[2] && ever[2] < '3') && !isdigit(ever[3])) {
fprintf(stderr, "You are using libevent %s.\nPlease upgrade to"
" a more recent version (1.3 or newer)\n",
event_get_version());
@@ -5057,6 +5780,44 @@
return true;
}
+static bool _parse_slab_sizes(char *s, uint32_t *slab_sizes) {
+ char *b = NULL;
+ uint32_t size = 0;
+ int i = 0;
+ uint32_t last_size = 0;
+
+ if (strlen(s) < 1)
+ return false;
+
+ for (char *p = strtok_r(s, "-", &b);
+ p != NULL;
+ p = strtok_r(NULL, "-", &b)) {
+ if (!safe_strtoul(p, &size) || size < settings.chunk_size
+ || size > settings.slab_chunk_size_max) {
+ fprintf(stderr, "slab size %u is out of valid range\n", size);
+ return false;
+ }
+ if (last_size >= size) {
+ fprintf(stderr, "slab size %u cannot be lower than or equal to a previous class size\n", size);
+ return false;
+ }
+ if (size <= last_size + CHUNK_ALIGN_BYTES) {
+ fprintf(stderr, "slab size %u must be at least %d bytes larger than previous class\n",
+ size, CHUNK_ALIGN_BYTES);
+ return false;
+ }
+ slab_sizes[i++] = size;
+ last_size = size;
+ if (i >= MAX_NUMBER_OF_SLAB_CLASSES-1) {
+ fprintf(stderr, "too many slab classes specified\n");
+ return false;
+ }
+ }
+
+ slab_sizes[i] = 0;
+ return true;
+}
+
int main (int argc, char **argv) {
int c;
bool lock_memory = false;
@@ -5083,8 +5844,12 @@
bool start_lru_crawler = false;
enum hashfunc_type hash_type = JENKINS_HASH;
uint32_t tocrawl;
+ uint32_t slab_sizes[MAX_NUMBER_OF_SLAB_CLASSES];
+ bool use_slab_sizes = false;
+ char *slab_sizes_unparsed = NULL;
+ bool slab_chunk_size_changed = false;
- char *subopts;
+ char *subopts, *subopts_orig;
char *subopts_value;
enum {
MAXCONNS_FAST = 0,
@@ -5099,7 +5864,17 @@
LRU_MAINTAINER,
HOT_LRU_PCT,
WARM_LRU_PCT,
- NOEXP_NOEVICT
+ HOT_MAX_AGE,
+ WARM_MAX_FACTOR,
+ TEMPORARY_TTL,
+ IDLE_TIMEOUT,
+ WATCHER_LOGBUF_SIZE,
+ WORKER_LOGBUF_SIZE,
+ SLAB_SIZES,
+ SLAB_CHUNK_MAX,
+ TRACK_SIZES,
+ NO_INLINE_ASCII_RESP,
+ MODERN
};
char *const subopts_tokens[] = {
[MAXCONNS_FAST] = "maxconns_fast",
@@ -5114,7 +5889,17 @@
[LRU_MAINTAINER] = "lru_maintainer",
[HOT_LRU_PCT] = "hot_lru_pct",
[WARM_LRU_PCT] = "warm_lru_pct",
- [NOEXP_NOEVICT] = "expirezero_does_not_evict",
+ [HOT_MAX_AGE] = "hot_max_age",
+ [WARM_MAX_FACTOR] = "warm_max_factor",
+ [TEMPORARY_TTL] = "temporary_ttl",
+ [IDLE_TIMEOUT] = "idle_timeout",
+ [WATCHER_LOGBUF_SIZE] = "watcher_logbuf_size",
+ [WORKER_LOGBUF_SIZE] = "worker_logbuf_size",
+ [SLAB_SIZES] = "slab_sizes",
+ [SLAB_CHUNK_MAX] = "slab_chunk_max",
+ [TRACK_SIZES] = "track_sizes",
+ [NO_INLINE_ASCII_RESP] = "no_inline_ascii_resp",
+ [MODERN] = "modern",
NULL
};
@@ -5166,6 +5951,7 @@
"I:" /* Max item size */
"S" /* Sasl ON */
"F" /* Disable flush_all */
+ "X" /* Disable dump commands */
"o:" /* Extended generic options */
))) {
switch (c) {
@@ -5198,6 +5984,10 @@
break;
case 'c':
settings.maxconns = atoi(optarg);
+ if (settings.maxconns <= 0) {
+ fprintf(stderr, "Maximum connections must be greater than 0\n");
+ return 1;
+ }
break;
case 'h':
usage();
@@ -5216,6 +6006,9 @@
break;
case 'l':
if (settings.inter != NULL) {
+ if (strstr(settings.inter, optarg) != NULL) {
+ break;
+ }
size_t len = strlen(settings.inter) + strlen(optarg) + 2;
char *p = malloc(len);
if (p == NULL) {
@@ -5331,22 +6124,25 @@
} else {
settings.item_size_max = atoi(buf);
}
+ free(buf);
if (settings.item_size_max < 1024) {
fprintf(stderr, "Item max size cannot be less than 1024 bytes.\n");
return 1;
}
- if (settings.item_size_max > 1024 * 1024 * 128) {
- fprintf(stderr, "Cannot set item size limit higher than 128 mb.\n");
+ if (settings.item_size_max > (settings.maxbytes / 4)) {
+ fprintf(stderr, "Cannot set item size limit higher than 1/4 of memory max.\n");
+ return 1;
+ }
+ if (settings.item_size_max > (1024 * 1024 * 1024)) {
+ fprintf(stderr, "Cannot set item size limit higher than a gigabyte.\n");
return 1;
}
if (settings.item_size_max > 1024 * 1024) {
- fprintf(stderr, "WARNING: Setting item max size above 1MB is not"
- " recommended!\n"
- " Raising this limit increases the minimum memory requirements\n"
- " and will decrease your memory efficiency.\n"
- );
+ if (!slab_chunk_size_changed) {
+ // Ideal new default is 16k, but needs stitching.
+ settings.slab_chunk_size_max = 524288;
+ }
}
- free(buf);
break;
case 'S': /* set Sasl authentication to true. Default is false */
#ifndef ENABLE_SASL
@@ -5358,8 +6154,11 @@
case 'F' :
settings.flush_enabled = false;
break;
+ case 'X' :
+ settings.dump_enabled = false;
+ break;
case 'o': /* It's sub-opts time! */
- subopts = optarg;
+ subopts_orig = subopts = strdup(optarg); /* getsubopt() changes the original args */
while (*subopts != '\0') {
@@ -5427,6 +6226,10 @@
start_lru_crawler = true;
break;
case LRU_CRAWLER_SLEEP:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing lru_crawler_sleep value\n");
+ return 1;
+ }
settings.lru_crawler_sleep = atoi(subopts_value);
if (settings.lru_crawler_sleep > 1000000 || settings.lru_crawler_sleep < 0) {
fprintf(stderr, "LRU crawler sleep must be between 0 and 1 second\n");
@@ -5434,6 +6237,10 @@
}
break;
case LRU_CRAWLER_TOCRAWL:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing lru_crawler_tocrawl value\n");
+ return 1;
+ }
if (!safe_strtoul(subopts_value, &tocrawl)) {
fprintf(stderr, "lru_crawler_tocrawl takes a numeric 32bit value\n");
return 1;
@@ -5442,12 +6249,13 @@
break;
case LRU_MAINTAINER:
start_lru_maintainer = true;
+ settings.lru_segmented = true;
break;
case HOT_LRU_PCT:
if (subopts_value == NULL) {
fprintf(stderr, "Missing hot_lru_pct argument\n");
return 1;
- };
+ }
settings.hot_lru_pct = atoi(subopts_value);
if (settings.hot_lru_pct < 1 || settings.hot_lru_pct >= 80) {
fprintf(stderr, "hot_lru_pct must be > 1 and < 80\n");
@@ -5458,15 +6266,104 @@
if (subopts_value == NULL) {
fprintf(stderr, "Missing warm_lru_pct argument\n");
return 1;
- };
+ }
settings.warm_lru_pct = atoi(subopts_value);
if (settings.warm_lru_pct < 1 || settings.warm_lru_pct >= 80) {
fprintf(stderr, "warm_lru_pct must be > 1 and < 80\n");
return 1;
}
break;
- case NOEXP_NOEVICT:
- settings.expirezero_does_not_evict = true;
+ case HOT_MAX_AGE:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing hot_max_age argument\n");
+ return 1;
+ }
+ if (!safe_strtoul(subopts_value, &settings.hot_max_age)) {
+ fprintf(stderr, "invalid argument to hot_max_age\n");
+ return 1;
+ }
+ break;
+ case WARM_MAX_FACTOR:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing warm_max_factor argument\n");
+ return 1;
+ }
+ settings.warm_max_factor = atof(subopts_value);
+ if (settings.warm_max_factor <= 0) {
+ fprintf(stderr, "warm_max_factor must be > 0\n");
+ return 1;
+ }
+ break;
+ case TEMPORARY_TTL:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing temporary_ttl argument\n");
+ return 1;
+ }
+ settings.temp_lru = true;
+ settings.temporary_ttl = atoi(subopts_value);
+ break;
+ case IDLE_TIMEOUT:
+ settings.idle_timeout = atoi(subopts_value);
+ break;
+ case WATCHER_LOGBUF_SIZE:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing watcher_logbuf_size argument\n");
+ return 1;
+ }
+ if (!safe_strtoul(subopts_value, &settings.logger_watcher_buf_size)) {
+ fprintf(stderr, "could not parse argument to watcher_logbuf_size\n");
+ return 1;
+ }
+ settings.logger_watcher_buf_size *= 1024; /* kilobytes */
+ break;
+ case WORKER_LOGBUF_SIZE:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing worker_logbuf_size argument\n");
+ return 1;
+ }
+ if (!safe_strtoul(subopts_value, &settings.logger_buf_size)) {
+ fprintf(stderr, "could not parse argument to worker_logbuf_size\n");
+ return 1;
+ }
+ settings.logger_buf_size *= 1024; /* kilobytes */
+ case SLAB_SIZES:
+ slab_sizes_unparsed = subopts_value;
+ break;
+ case SLAB_CHUNK_MAX:
+ if (subopts_value == NULL) {
+ fprintf(stderr, "Missing slab_chunk_max argument\n");
+ }
+ if (!safe_strtol(subopts_value, &settings.slab_chunk_size_max)) {
+ fprintf(stderr, "could not parse argument to slab_chunk_max\n");
+ }
+ slab_chunk_size_changed = true;
+ break;
+ case TRACK_SIZES:
+ item_stats_sizes_init();
+ break;
+ case NO_INLINE_ASCII_RESP:
+ settings.inline_ascii_response = false;
+ break;
+ case MODERN:
+ /* Modernized defaults. Need to add equivalent no_* flags
+ * before making truly default. */
+ // chunk default should come after stitching is fixed.
+ //settings.slab_chunk_size_max = 16384;
+
+ // With slab_ressign, pages are always 1MB, so anything larger
+ // than .5m ends up using 1m anyway. With this we at least
+ // avoid having several slab classes that use 1m.
+ if (!slab_chunk_size_changed) {
+ settings.slab_chunk_size_max = 524288;
+ }
+ settings.slab_reassign = true;
+ settings.slab_automove = 1;
+ settings.maxconns_fast = true;
+ settings.inline_ascii_response = false;
+ settings.lru_segmented = true;
+ hash_type = MURMUR3_HASH;
+ start_lru_crawler = true;
+ start_lru_maintainer = true;
break;
default:
printf("Illegal suboption \"%s\"\n", subopts_value);
@@ -5474,6 +6371,7 @@
}
}
+ free(subopts_orig);
break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
@@ -5481,11 +6379,48 @@
}
}
- if (settings.lru_maintainer_thread && settings.hot_lru_pct + settings.warm_lru_pct > 80) {
+ if (settings.slab_chunk_size_max > settings.item_size_max) {
+ fprintf(stderr, "slab_chunk_max (bytes: %d) cannot be larger than -I (item_size_max %d)\n",
+ settings.slab_chunk_size_max, settings.item_size_max);
+ exit(EX_USAGE);
+ }
+
+ if (settings.item_size_max % settings.slab_chunk_size_max != 0) {
+ fprintf(stderr, "-I (item_size_max: %d) must be evenly divisible by slab_chunk_max (bytes: %d)\n",
+ settings.item_size_max, settings.slab_chunk_size_max);
+ exit(EX_USAGE);
+ }
+
+ if (settings.slab_page_size % settings.slab_chunk_size_max != 0) {
+ fprintf(stderr, "slab_chunk_max (bytes: %d) must divide evenly into %d (slab_page_size)\n",
+ settings.slab_chunk_size_max, settings.slab_page_size);
+ exit(EX_USAGE);
+ }
+
+ // Reserve this for the new default. If factor size hasn't changed, use
+ // new default.
+ /*if (settings.slab_chunk_size_max == 16384 && settings.factor == 1.25) {
+ settings.factor = 1.08;
+ }*/
+
+ if (slab_sizes_unparsed != NULL) {
+ if (_parse_slab_sizes(slab_sizes_unparsed, slab_sizes)) {
+ use_slab_sizes = true;
+ } else {
+ exit(EX_USAGE);
+ }
+ }
+
+ if (settings.hot_lru_pct + settings.warm_lru_pct > 80) {
fprintf(stderr, "hot_lru_pct + warm_lru_pct cannot be more than 80%% combined\n");
exit(EX_USAGE);
}
+ if (settings.temp_lru && !start_lru_maintainer) {
+ fprintf(stderr, "temporary_ttl requires lru_maintainer to be enabled\n");
+ exit(EX_USAGE);
+ }
+
if (hash_init(hash_type) != 0) {
fprintf(stderr, "Failed to initialize hash_algorithm!\n");
exit(EX_USAGE);
@@ -5611,10 +6546,12 @@
main_base = event_init();
/* initialize other stuff */
+ logger_init();
stats_init();
assoc_init(settings.hashpower_init);
conn_init();
- slabs_init(settings.maxbytes, settings.factor, preallocate);
+ slabs_init(settings.maxbytes, settings.factor, preallocate,
+ use_slab_sizes ? slab_sizes : NULL);
/*
* ignore SIGPIPE signals; we can use errno == EPIPE if we
@@ -5625,7 +6562,7 @@
exit(EX_OSERR);
}
/* start up worker threads if MT mode */
- memcached_thread_init(settings.num_threads, main_base);
+ memcached_thread_init(settings.num_threads);
if (start_assoc_maintenance_thread() == -1) {
exit(EXIT_FAILURE);
@@ -5646,6 +6583,10 @@
exit(EXIT_FAILURE);
}
+ if (settings.idle_timeout && start_conn_timeout_thread() == -1) {
+ exit(EXIT_FAILURE);
+ }
+
/* initialise clock event */
clock_handler(0, 0, 0);
@@ -5661,12 +6602,15 @@
/* create the listening socket, bind it, and init */
if (settings.socketpath == NULL) {
const char *portnumber_filename = getenv("MEMCACHED_PORT_FILENAME");
- char temp_portnumber_filename[PATH_MAX];
+ char *temp_portnumber_filename = NULL;
+ size_t len;
FILE *portnumber_file = NULL;
if (portnumber_filename != NULL) {
+ len = strlen(portnumber_filename)+4+1;
+ temp_portnumber_filename = malloc(len);
snprintf(temp_portnumber_filename,
- sizeof(temp_portnumber_filename),
+ len,
"%s.lck", portnumber_filename);
portnumber_file = fopen(temp_portnumber_filename, "a");
@@ -5701,6 +6645,7 @@
if (portnumber_file) {
fclose(portnumber_file);
rename(temp_portnumber_filename, portnumber_filename);
+ free(temp_portnumber_filename);
}
}
@@ -5708,7 +6653,7 @@
* is only an advisory.
*/
usleep(1000);
- if (stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
+ if (stats_state.curr_conns + stats_state.reserved_fds >= settings.maxconns - 1) {
fprintf(stderr, "Maxconns setting is too low, use -c to increase.\n");
exit(EXIT_FAILURE);
}
@@ -5720,6 +6665,9 @@
/* Drop privileges no longer needed */
drop_privileges();
+ /* Initialize the uriencode lookup table. */
+ uriencode_init();
+
/* enter the event loop */
if (event_base_loop(main_base, 0) != 0) {
retval = EXIT_FAILURE;
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/memcached.h
^
|
@@ -17,9 +17,12 @@
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
+#include <assert.h>
+#include "itoa_ljust.h"
#include "protocol_binary.h"
#include "cache.h"
+#include "logger.h"
#include "sasl_defs.h"
@@ -34,15 +37,14 @@
#define UDP_MAX_PAYLOAD_SIZE 1400
#define UDP_HEADER_SIZE 8
#define MAX_SENDBUF_SIZE (256 * 1024 * 1024)
-/* I'm told the max length of a 64-bit num converted to string is 20 bytes.
- * Plus a few for spaces, \r\n, \0 */
-#define SUFFIX_SIZE 24
+/* Up to 3 numbers (2 32bit, 1 64bit), spaces, newlines, null 0 */
+#define SUFFIX_SIZE 50
/** Initial size of list of items being returned by "get". */
#define ITEM_LIST_INITIAL 200
/** Initial size of list of CAS suffixes appended to "gets" lines. */
-#define SUFFIX_LIST_INITIAL 20
+#define SUFFIX_LIST_INITIAL 100
/** Initial size of the sendmsg() scatter/gather array. */
#define IOV_LIST_INITIAL 400
@@ -78,6 +80,7 @@
/* Slab sizing definitions. */
#define POWER_SMALLEST 1
#define POWER_LARGEST 256 /* actual cap is 255 */
+#define SLAB_GLOBAL_PAGE_POOL 0 /* magic slab class for storing pages for reassignment */
#define CHUNK_ALIGN_BYTES 8
/* slab class max is a 6-bit number, -1. */
#define MAX_NUMBER_OF_SLAB_CLASSES (63 + 1)
@@ -111,6 +114,7 @@
+ (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
#define ITEM_clsid(item) ((item)->slabs_clsid & ~(3<<6))
+#define ITEM_lruid(item) ((item)->slabs_clsid & (3<<6))
#define STAT_KEY_LEN 128
#define STAT_VAL_LEN 128
@@ -161,6 +165,7 @@
conn_closing, /**< closing this connection */
conn_mwrite, /**< writing out many items sequentially */
conn_closed, /**< connection is closed */
+ conn_watch, /**< held by the logger thread as a watcher */
conn_max_state /**< Max state value (used for assertion) */
};
@@ -198,6 +203,7 @@
RESUME_WORKER_THREADS
};
+#define IS_TCP(x) (x == tcp_transport)
#define IS_UDP(x) (x == udp_transport)
#define NREAD_ADD 1
@@ -208,7 +214,7 @@
#define NREAD_CAS 6
enum store_item_type {
- NOT_STORED=0, STORED, EXISTS, NOT_FOUND
+ NOT_STORED=0, STORED, EXISTS, NOT_FOUND, TOO_LARGE, NO_MEMORY
};
enum delta_result_type {
@@ -216,78 +222,101 @@
};
/** Time relative to server start. Smaller than time_t on 64-bit systems. */
-typedef unsigned int rel_time_t;
+// TODO: Move to sub-header. needed in logger.h
+//typedef unsigned int rel_time_t;
+
+/** Use X macros to avoid iterating over the stats fields during reset and
+ * aggregation. No longer have to add new stats in 3+ places.
+ */
+
+#define SLAB_STATS_FIELDS \
+ X(set_cmds) \
+ X(get_hits) \
+ X(touch_hits) \
+ X(delete_hits) \
+ X(cas_hits) \
+ X(cas_badval) \
+ X(incr_hits) \
+ X(decr_hits)
/** Stats stored per slab (and per thread). */
struct slab_stats {
- uint64_t set_cmds;
- uint64_t get_hits;
- uint64_t touch_hits;
- uint64_t delete_hits;
- uint64_t cas_hits;
- uint64_t cas_badval;
- uint64_t incr_hits;
- uint64_t decr_hits;
-};
+#define X(name) uint64_t name;
+ SLAB_STATS_FIELDS
+#undef X
+};
+
+#define THREAD_STATS_FIELDS \
+ X(get_cmds) \
+ X(get_misses) \
+ X(get_expired) \
+ X(get_flushed) \
+ X(touch_cmds) \
+ X(touch_misses) \
+ X(delete_misses) \
+ X(incr_misses) \
+ X(decr_misses) \
+ X(cas_misses) \
+ X(bytes_read) \
+ X(bytes_written) \
+ X(flush_cmds) \
+ X(conn_yields) /* # of yields for connections (-R option)*/ \
+ X(auth_cmds) \
+ X(auth_errors) \
+ X(idle_kicks) /* idle connections killed */
/**
* Stats stored per-thread.
*/
struct thread_stats {
pthread_mutex_t mutex;
- uint64_t get_cmds;
- uint64_t get_misses;
- uint64_t touch_cmds;
- uint64_t touch_misses;
- uint64_t delete_misses;
- uint64_t incr_misses;
- uint64_t decr_misses;
- uint64_t cas_misses;
- uint64_t bytes_read;
- uint64_t bytes_written;
- uint64_t flush_cmds;
- uint64_t conn_yields; /* # of yields for connections (-R option)*/
- uint64_t auth_cmds;
- uint64_t auth_errors;
+#define X(name) uint64_t name;
+ THREAD_STATS_FIELDS
+#undef X
struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES];
};
/**
- * Global stats.
+ * Global stats. Only resettable stats should go into this structure.
*/
struct stats {
- pthread_mutex_t mutex;
- unsigned int curr_items;
- unsigned int total_items;
- uint64_t curr_bytes;
- unsigned int curr_conns;
- unsigned int total_conns;
+ uint64_t total_items;
+ uint64_t total_conns;
uint64_t rejected_conns;
uint64_t malloc_fails;
- unsigned int reserved_fds;
- unsigned int conn_structs;
- uint64_t get_cmds;
- uint64_t set_cmds;
- uint64_t touch_cmds;
- uint64_t get_hits;
- uint64_t get_misses;
- uint64_t touch_hits;
- uint64_t touch_misses;
- uint64_t evictions;
- uint64_t reclaimed;
- time_t started; /* when the process was started */
- bool accepting_conns; /* whether we are currently accepting */
uint64_t listen_disabled_num;
- unsigned int hash_power_level; /* Better hope it's not over 9000 */
+ uint64_t slabs_moved; /* times slabs were moved around */
+ uint64_t slab_reassign_rescues; /* items rescued during slab move */
+ uint64_t slab_reassign_evictions_nomem; /* valid items lost during slab move */
+ uint64_t slab_reassign_inline_reclaim; /* valid items lost during slab move */
+ uint64_t slab_reassign_chunk_rescues; /* chunked-item chunks recovered */
+ uint64_t slab_reassign_busy_items; /* valid temporarily unmovable */
+ uint64_t lru_crawler_starts; /* Number of item crawlers kicked off */
+ uint64_t lru_maintainer_juggles; /* number of LRU bg pokes */
+ uint64_t time_in_listen_disabled_us; /* elapsed time in microseconds while server unable to process new connections */
+ uint64_t log_worker_dropped; /* logs dropped by worker threads */
+ uint64_t log_worker_written; /* logs written by worker threads */
+ uint64_t log_watcher_skipped; /* logs watchers missed */
+ uint64_t log_watcher_sent; /* logs sent to watcher buffers */
+ struct timeval maxconns_entered; /* last time maxconns entered */
+};
+
+/**
+ * Global "state" stats. Reflects state that shouldn't be wiped ever.
+ * Ordered for some cache line locality for commonly updated counters.
+ */
+struct stats_state {
+ uint64_t curr_items;
+ uint64_t curr_bytes;
+ uint64_t curr_conns;
uint64_t hash_bytes; /* size used for hash tables */
+ unsigned int conn_structs;
+ unsigned int reserved_fds;
+ unsigned int hash_power_level; /* Better hope it's not over 9000 */
bool hash_is_expanding; /* If the hash table is being expanded */
- uint64_t expired_unfetched; /* items reclaimed but never touched */
- uint64_t evicted_unfetched; /* items evicted but never touched */
+ bool accepting_conns; /* whether we are currently accepting */
bool slab_reassign_running; /* slab reassign in progress */
- uint64_t slabs_moved; /* times slabs were moved around */
- uint64_t lru_crawler_starts; /* Number of item crawlers kicked off */
bool lru_crawler_running; /* crawl in progress */
- uint64_t lru_maintainer_juggles; /* number of LRU bg pokes */
};
#define MAX_VERBOSITY_LEVEL 2
@@ -319,27 +348,39 @@
bool use_cas;
enum protocol binding_protocol;
int backlog;
- int item_size_max; /* Maximum item size, and upper end for slabs */
+ int item_size_max; /* Maximum item size */
+ int slab_chunk_size_max; /* Upper end for chunks within slab pages. */
+ int slab_page_size; /* Slab's page units. */
bool sasl; /* SASL on/off */
bool maxconns_fast; /* Whether or not to early close connections */
bool lru_crawler; /* Whether or not to enable the autocrawler thread */
bool lru_maintainer_thread; /* LRU maintainer background thread */
+ bool lru_segmented; /* Use split or flat LRU's */
bool slab_reassign; /* Whether or not slab reassignment is allowed */
int slab_automove; /* Whether or not to automatically move slabs */
int hashpower_init; /* Starting hash power level */
bool shutdown_command; /* allow shutdown command */
int tail_repair_time; /* LRU tail refcount leak repair time */
bool flush_enabled; /* flush_all enabled */
+ bool dump_enabled; /* whether cachedump/metadump commands work */
char *hash_algorithm; /* Hash algorithm in use */
int lru_crawler_sleep; /* Microsecond sleep between items */
uint32_t lru_crawler_tocrawl; /* Number of items to crawl per run */
int hot_lru_pct; /* percentage of slab space for HOT_LRU */
int warm_lru_pct; /* percentage of slab space for WARM_LRU */
+ uint32_t hot_max_age; /* max idle time before move from HOT_LRU */
+ double warm_max_factor; /* WARM tail age relative to COLD tail */
int crawls_persleep; /* Number of LRU crawls to run before sleeping */
- bool expirezero_does_not_evict; /* exptime == 0 goes into NOEXP_LRU */
+ bool inline_ascii_response; /* pre-format the VALUE line for ASCII responses */
+ bool temp_lru; /* TTL < temporary_ttl uses TEMP_LRU */
+ uint32_t temporary_ttl; /* temporary LRU threshold */
+ int idle_timeout; /* Number of seconds to let connections idle */
+ unsigned int logger_watcher_buf_size; /* size of logger's per-watcher buffer */
+ unsigned int logger_buf_size; /* size of per-thread logger buffer */
};
extern struct stats stats;
+extern struct stats_state stats_state;
extern time_t process_started;
extern struct settings settings;
@@ -353,6 +394,9 @@
#define ITEM_FETCHED 8
/* Appended on fetch, removed on LRU shuffling */
#define ITEM_ACTIVE 16
+/* If an item's storage are chained chunks. */
+#define ITEM_CHUNKED 32
+#define ITEM_CHUNK 64
/**
* Structure for storing items within memcached.
@@ -383,6 +427,11 @@
/* then data with terminating \r\n (no terminating null; it's binary!) */
} item;
+// TODO: If we eventually want user loaded modules, we can't use an enum :(
+enum crawler_run_type {
+ CRAWLER_EXPIRED=0, CRAWLER_METADUMP
+};
+
typedef struct {
struct _stritem *next;
struct _stritem *prev;
@@ -396,8 +445,26 @@
uint8_t slabs_clsid;/* which slab class we're in */
uint8_t nkey; /* key length, w/terminating null and padding */
uint32_t remaining; /* Max keys to crawl per slab per invocation */
+ uint64_t reclaimed; /* items reclaimed during this crawl. */
+ uint64_t unfetched; /* items reclaiemd unfetched during this crawl. */
+ uint64_t checked; /* items examined during this crawl. */
} crawler;
+/* Header when an item is actually a chunk of another item. */
+typedef struct _strchunk {
+ struct _strchunk *next; /* points within its own chain. */
+ struct _strchunk *prev; /* can potentially point to the head. */
+ struct _stritem *head; /* always points to the owner chunk */
+ int size; /* available chunk space in bytes */
+ int used; /* chunk space used */
+ int nbytes; /* used. */
+ unsigned short refcount; /* used? */
+ uint8_t orig_clsid; /* For obj hdr chunks slabs_clsid is fake. */
+ uint8_t it_flags; /* ITEM_* above. */
+ uint8_t slabs_clsid; /* Same as above. */
+ char data[];
+} item_chunk;
+
typedef struct {
pthread_t thread_id; /* unique ID of this thread */
struct event_base *base; /* libevent handle this thread uses */
@@ -407,13 +474,10 @@
struct thread_stats stats; /* Stats generated by this thread */
struct conn_queue *new_conn_queue; /* queue of new connections to handle */
cache_t *suffix_cache; /* suffix cache */
+ logger *l; /* logger buffer */
+ void *lru_bump_buf; /* async LRU bump buffer */
} LIBEVENT_THREAD;
-typedef struct {
- pthread_t thread_id; /* unique ID of this thread */
- struct event_base *base; /* libevent handle this thread uses */
-} LIBEVENT_DISPATCHER_THREAD;
-
/**
* The structure representing a connection into memcached.
*/
@@ -523,7 +587,11 @@
void *slab_pos;
int s_clsid;
int d_clsid;
- int busy_items;
+ uint32_t busy_items;
+ uint32_t rescues;
+ uint32_t evictions_nomem;
+ uint32_t inline_reclaim;
+ uint32_t chunk_rescues;
uint8_t done;
};
@@ -539,6 +607,7 @@
uint64_t *cas, const uint32_t hv);
enum store_item_type do_store_item(item *item, int comm, conn* c, const uint32_t hv);
conn *conn_new(const int sfd, const enum conn_states init_state, const int event_flags, const int read_buffer_size, enum network_transport transport, struct event_base *base);
+void conn_worker_readd(conn *c);
extern int daemonize(int nochdir, int noclose);
#define mutex_lock(x) pthread_mutex_lock(x)
@@ -548,6 +617,7 @@
#include "slabs.h"
#include "assoc.h"
#include "items.h"
+#include "crawler.h"
#include "trace.h"
#include "hash.h"
#include "util.h"
@@ -559,9 +629,10 @@
* also #define-d to directly call the underlying code in singlethreaded mode.
*/
-void memcached_thread_init(int nthreads, struct event_base *main_base);
-int dispatch_event_add(int thread, conn *c);
+void memcached_thread_init(int nthreads);
+void redispatch_conn(conn *c);
void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, int read_buffer_size, enum network_transport transport);
+void sidethread_conn_close(conn *c);
/* Lock wrappers for cache functions that are called from main loop. */
enum delta_result_type add_delta(conn *c, const char *key,
@@ -571,23 +642,24 @@
void accept_new_conns(const bool do_accept);
conn *conn_from_freelist(void);
bool conn_add_to_freelist(conn *c);
-int is_listen_thread(void);
+void conn_close_idle(conn *c);
item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes);
-item *item_get(const char *key, const size_t nkey);
-item *item_touch(const char *key, const size_t nkey, uint32_t exptime);
+#define DO_UPDATE true
+#define DONT_UPDATE false
+item *item_get(const char *key, const size_t nkey, conn *c, const bool do_update);
+item *item_touch(const char *key, const size_t nkey, uint32_t exptime, conn *c);
int item_link(item *it);
void item_remove(item *it);
int item_replace(item *it, item *new_it, const uint32_t hv);
void item_unlink(item *it);
-void item_update(item *it);
void item_lock(uint32_t hv);
void *item_trylock(uint32_t hv);
void item_trylock_unlock(void *arg);
void item_unlock(uint32_t hv);
void pause_threads(enum pause_thread_types type);
-unsigned short refcount_incr(unsigned short *refcount);
-unsigned short refcount_decr(unsigned short *refcount);
+#define refcount_incr(it) ++(it->refcount)
+#define refcount_decr(it) --(it->refcount)
void STATS_LOCK(void);
void STATS_UNLOCK(void);
void threadlocal_stats_reset(void);
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/memcached.spec
^
|
@@ -1,12 +1,12 @@
Name: memcached
-Version: 1.4.24
+Version: 1.4.36
Release: 1%{?dist}
Summary: High Performance, Distributed Memory Object Cache
Group: System Environment/Daemons
License: BSD
URL: http://memcached.org
-Source0: http://memcached.org/files/%{name}-1.4.24.tar.gz
+Source0: http://memcached.org/files/%{name}-1.4.36.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: libevent-devel
@@ -23,7 +23,7 @@
web applications by alleviating database load.
%prep
-%setup -q -n %{name}-1.4.24
+%setup -q -n %{name}-1.4.36
%build
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/missing
^
|
@@ -3,7 +3,7 @@
scriptversion=2013-10-28.13; # UTC
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/scripts/memcached-init
^
|
@@ -85,7 +85,18 @@
echo "$NAME."
rm -f $PIDFILE
;;
-
+ status)
+ [ $# -lt 2 ] && NAME=$DAEMONNAME
+ PIDFILE="/var/run/$NAME.pid"
+ set +e
+ start-stop-daemon --status --pidfile $PIDFILE
+ case $? in
+ 0) echo "$DESC: $NAME (pid $(cat $PIDFILE)) is running" && exit 0;;
+ 1) echo "$DESC: $NAME is not running thro' the pid file exists" && rm -f $PIDFILE && exit 1;;
+ 3) echo "$DESC: $NAME is not running" && exit 3;;
+ 4) echo "$DESC: $NAME status is unclear, sorry" && exit 4;;
+ esac
+ ;;
restart|force-reload)
#
# If the "reload" option is implemented, move the "force-reload"
@@ -102,7 +113,7 @@
*)
N=/etc/init.d/$NAME
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
- echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ echo "Usage: $N {start|stop|status|restart|force-reload}" >&2
exit 1
;;
esac
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/scripts/memcached-server.upstart
^
|
@@ -0,0 +1,26 @@
+description "memcached-server - high-performance memory caching daemon (instance)"
+author "Cameron Norman <camerontnorman@gmail.com>"
+
+stop on stop-memcached-servers or runlevel [016]
+
+expect daemon
+respawn
+
+# Instance w/ default value so main server starts without SERVER param
+env SERVER=""
+instance $SERVER
+usage "SERVER - instance of memcached with corresponding /etc/memcached_$SERVER config"
+
+script
+ if test "x$SERVER" = "x"; then
+ name="memcached"
+ else
+ name="memcached_$SERVER"
+ fi
+
+ test -f /etc/${name}.conf || {
+ echo "No config found, not starting."; exit 1
+ }
+
+ exec /usr/share/memcached/scripts/start-memcached /etc/${name}.conf /var/run/${name}.pid
+end script
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/scripts/memcached-tool.1
^
|
@@ -0,0 +1,71 @@
+.TH MEMCACHED-TOOL 1 "Jul 2, 2013"
+.SH NAME
+memcached-tool \- stats and management tool for memcached
+
+.SH SYNOPSIS
+.B memcached-tool
+.RI < host [: port "] | " /path/to/socket "> [" mode ]
+
+.SH DESCRIPTION
+.B memcached-tool
+is a Perl script used to print statistics from a running memcached instance.
+The first parameter specifies the address of the daemon either by a hostname,
+optionally followed by the port number (the default is 11211), or a path to
+UNIX domain socket. The second parameter specifies the mode in which the tool
+should run.
+
+.SH MODES
+.TP
+.B display
+Print slab class statistics. This is the default mode if no mode is specified.
+The printed columns are:
+.RS
+.TP
+.B #
+Number of the slab class.
+.TP
+.B Item_Size
+The amount of space each chunk uses. One item uses one chunk of the
+appropriate size.
+.TP
+.B Max_age
+Age of the oldest item in the LRU.
+.TP
+.B Pages
+Total number of pages allocated to the slab class.
+.TP
+.B Count
+Number of items presently stored in this class. Expired items are not
+automatically excluded.
+.TP
+.B Full?
+Yes if there are no free chunks at the end of the last allocated page.
+.TP
+.B Evicted
+Number of times an item had to be evicted from the LRU before it expired.
+.TP
+.B Evict_Time
+Seconds since the last access for the most recent item evicted from this
+class.
+.TP
+.B OOM
+Number of times the underlying slab class was unable to store a new item.
+.RE
+
+.TP
+.B stats
+Print general-purpose statistics of the daemon. Each line contains the name of
+the statistic and its value.
+.TP
+.B dump
+Make a partial dump of the cache written in the add statements of the
+memcached protocol.
+
+.SH SEE ALSO
+.BR memcached (1),
+.br
+.B http://www.memcached.org
+
+.SH AUTHOR
+The memcached-tool script was written by Brad Fitzpatrick
+.B <brad@danga.com>
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/scripts/memcached.service
^
|
@@ -1,3 +1,14 @@
+# It's not recommended to modify this file in-place, because it will be
+# overwritten during upgrades. If you want to customize, the best
+# way is to use the "systemctl edit" command to create an override unit.
+
+# For example, to pass additional options, create an override unit
+# (as is done by systemctl edit) and enter the following:
+
+# [Service]
+# Environment=OPTIONS="-l 127.0.0.1,::1"
+
+
[Unit]
Description=memcached daemon
After=network.target
@@ -6,5 +17,56 @@
EnvironmentFile=/etc/sysconfig/memcached
ExecStart=/usr/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c ${MAXCONN} $OPTIONS
+# Set up a new file system namespace and mounts private /tmp and /var/tmp directories
+# so this service cannot access the global directories and other processes cannot
+# access this service's directories.
+PrivateTmp=true
+
+# Mounts the /usr, /boot, and /etc directories read-only for processes invoked by this unit.
+ProtectSystem=full
+
+# Ensures that the service process and all its children can never gain new privileges
+NoNewPrivileges=true
+
+# Sets up a new /dev namespace for the executed processes and only adds API pseudo devices
+# such as /dev/null, /dev/zero or /dev/random (as well as the pseudo TTY subsystem) to it,
+# but no physical devices such as /dev/sda.
+PrivateDevices=true
+
+# Required for dropping privileges and running as a different user
+CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE
+
+# Attempts to create memory mappings that are writable and executable at the same time,
+# or to change existing memory mappings to become executable are prohibited.
+MemoryDenyWriteExecute=true
+
+# Explicit module loading will be denied. This allows to turn off module load and unload
+# operations on modular kernels. It is recommended to turn this on for most services that
+# do not need special file systems or extra kernel modules to work.
+ProtectKernelModules=true
+
+# Kernel variables accessible through /proc/sys, /sys, /proc/sysrq-trigger, /proc/latency_stats,
+# /proc/acpi, /proc/timer_stats, /proc/fs and /proc/irq will be made read-only to all processes
+# of the unit. Usually, tunable kernel variables should only be written at boot-time, with the
+# sysctl.d(5) mechanism. Almost no services need to write to these at runtime; it is hence
+# recommended to turn this on for most services.
+ProtectKernelTunables=true
+
+# The Linux Control Groups (cgroups(7)) hierarchies accessible through /sys/fs/cgroup will be
+# made read-only to all processes of the unit. Except for container managers no services should
+# require write access to the control groups hierarchies; it is hence recommended to turn this on
+# for most services
+ProtectControlGroups=true
+
+# Any attempts to enable realtime scheduling in a process of the unit are refused.
+RestrictRealtime=true
+
+# Restricts the set of socket address families accessible to the processes of this unit.
+# Protects against vulnerabilities such as CVE-2016-8655
+RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
+
+# Takes away the ability to create or manage any kind of namespace
+RestrictNamespaces=true
+
[Install]
WantedBy=multi-user.target
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/scripts/memcached.sysv
^
|
@@ -43,7 +43,7 @@
echo
if [ $RETVAL -eq 0 ] ; then
rm -f /var/lock/subsys/memcached
- rm -f /var/run/memcached.pid
+ rm -f /var/run/memcached/memcached.pid
fi
}
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/scripts/memcached.upstart
^
|
@@ -0,0 +1,25 @@
+description "memcached - high performance memory caching daemon"
+author "Cameron Norman <camerontnorman@gmail.com>"
+
+start on filesystem
+stop on runlevel [016]
+
+pre-start script
+ ret=0
+
+ test -x /usr/bin/memcached || { stop; exit 0; }
+
+ # Main
+ start memcached-server || ret=$?
+ # Instances
+ for i in /etc/memcached_*.conf; do
+ i=${i#/etc/memcached_}
+ i=${i%.conf}
+ test "$i" = "*" && continue
+ start memcached-server SERVER=$i || ret=$?
+ done
+
+ exit $ret
+end script
+
+post-stop exec initctl emit stop-memcached-servers
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/scripts/start-memcached
^
|
@@ -62,7 +62,7 @@
foreach my $line (<$etchandle>)
{
$line ||= "";
- $line =~ s/\#.*//g;
+ $line =~ s/(?<!\\)\#[^\#]*//g;
$line =~ s/\s+$//g;
$line =~ s/^\s+//g;
next unless $line;
@@ -97,7 +97,7 @@
print STDERR "memcached is already running.\n";
exit;
}else{
- `rm -f $localpid`;
+ unlink $pidfile;
}
}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/slabs.c
^
|
@@ -10,7 +10,6 @@
#include "memcached.h"
#include <sys/stat.h>
#include <sys/socket.h>
-#include <sys/signal.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <netinet/in.h>
@@ -18,9 +17,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <signal.h>
#include <assert.h>
#include <pthread.h>
+//#define DEBUG_SLAB_MOVER
/* powers-of-N allocation structures */
typedef struct {
@@ -35,7 +36,6 @@
void **slab_list; /* array of slab pointers */
unsigned int list_size; /* size of prev array */
- unsigned int killing; /* index+1 of dying slab, or zero if none */
size_t requested; /* The number of requested bytes */
} slabclass_t;
@@ -83,11 +83,11 @@
unsigned int slabs_clsid(const size_t size) {
int res = POWER_SMALLEST;
- if (size == 0)
+ if (size == 0 || size > settings.item_size_max)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest) /* won't fit in the biggest slab */
- return 0;
+ return power_largest;
return res;
}
@@ -95,7 +95,7 @@
* Determines the chunk sizes and initializes the slab class descriptors
* accordingly.
*/
-void slabs_init(const size_t limit, const double factor, const bool prealloc) {
+void slabs_init(const size_t limit, const double factor, const bool prealloc, const uint32_t *slab_sizes) {
int i = POWER_SMALLEST - 1;
unsigned int size = sizeof(item) + settings.chunk_size;
@@ -115,14 +115,22 @@
memset(slabclass, 0, sizeof(slabclass));
- while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1 && size <= settings.item_size_max / factor) {
+ while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1) {
+ if (slab_sizes != NULL) {
+ if (slab_sizes[i-1] == 0)
+ break;
+ size = slab_sizes[i-1];
+ } else if (size >= settings.slab_chunk_size_max / factor) {
+ break;
+ }
/* Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
slabclass[i].size = size;
- slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
- size *= factor;
+ slabclass[i].perslab = settings.slab_page_size / slabclass[i].size;
+ if (slab_sizes == NULL)
+ size *= factor;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
@@ -130,8 +138,8 @@
}
power_largest = i;
- slabclass[power_largest].size = settings.item_size_max;
- slabclass[power_largest].perslab = 1;
+ slabclass[power_largest].size = settings.slab_chunk_size_max;
+ slabclass[power_largest].perslab = settings.slab_page_size / settings.slab_chunk_size_max;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
@@ -195,20 +203,35 @@
}
}
+/* Fast FIFO queue */
+static void *get_page_from_global_pool(void) {
+ slabclass_t *p = &slabclass[SLAB_GLOBAL_PAGE_POOL];
+ if (p->slabs < 1) {
+ return NULL;
+ }
+ char *ret = p->slab_list[p->slabs - 1];
+ p->slabs--;
+ return ret;
+}
+
static int do_slabs_newslab(const unsigned int id) {
slabclass_t *p = &slabclass[id];
- int len = settings.slab_reassign ? settings.item_size_max
+ slabclass_t *g = &slabclass[SLAB_GLOBAL_PAGE_POOL];
+ int len = (settings.slab_reassign || settings.slab_chunk_size_max != settings.slab_page_size)
+ ? settings.slab_page_size
: p->size * p->perslab;
char *ptr;
- if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0)) {
+ if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0
+ && g->slabs == 0)) {
mem_limit_reached = true;
MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
return 0;
}
if ((grow_slab_list(id) == 0) ||
- ((ptr = memory_allocate((size_t)len)) == 0)) {
+ (((ptr = get_page_from_global_pool()) == NULL) &&
+ ((ptr = memory_allocate((size_t)len)) == 0))) {
MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
return 0;
@@ -218,14 +241,14 @@
split_slab_page_into_freelist(ptr, id);
p->slab_list[p->slabs++] = ptr;
- mem_malloced += len;
MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
return 1;
}
/*@null@*/
-static void *do_slabs_alloc(const size_t size, unsigned int id, unsigned int *total_chunks) {
+static void *do_slabs_alloc(const size_t size, unsigned int id, uint64_t *total_bytes,
+ unsigned int flags) {
slabclass_t *p;
void *ret = NULL;
item *it = NULL;
@@ -236,14 +259,18 @@
}
p = &slabclass[id];
assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);
+ if (total_bytes != NULL) {
+ *total_bytes = p->requested;
+ }
- *total_chunks = p->slabs * p->perslab;
+ assert(size <= p->size);
/* 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->sl_curr != 0 || do_slabs_newslab(id) != 0)) {
- /* We don't have more memory available */
- ret = NULL;
- } else if (p->sl_curr != 0) {
+ if (p->sl_curr == 0 && flags != SLABS_ALLOC_NO_NEWPAGE) {
+ do_slabs_newslab(id);
+ }
+
+ if (p->sl_curr != 0) {
/* return off our freelist */
it = (item *)p->slots;
p->slots = it->next;
@@ -254,6 +281,8 @@
it->refcount = 1;
p->sl_curr--;
ret = (void *)it;
+ } else {
+ ret = NULL;
}
if (ret) {
@@ -266,6 +295,58 @@
return ret;
}
+static void do_slabs_free_chunked(item *it, const size_t size) {
+ item_chunk *chunk = (item_chunk *) ITEM_data(it);
+ slabclass_t *p;
+
+ it->it_flags = ITEM_SLABBED;
+ it->slabs_clsid = 0;
+ it->prev = 0;
+ // header object's original classid is stored in chunk.
+ p = &slabclass[chunk->orig_clsid];
+ if (chunk->next) {
+ chunk = chunk->next;
+ chunk->prev = 0;
+ } else {
+ // header with no attached chunk
+ chunk = NULL;
+ }
+
+ // return the header object.
+ // TODO: This is in three places, here and in do_slabs_free().
+ it->prev = 0;
+ it->next = p->slots;
+ if (it->next) it->next->prev = it;
+ p->slots = it;
+ p->sl_curr++;
+ // TODO: macro
+ p->requested -= it->nkey + 1 + it->nsuffix + sizeof(item) + sizeof(item_chunk);
+ if (settings.use_cas) {
+ p->requested -= sizeof(uint64_t);
+ }
+
+ item_chunk *next_chunk;
+ while (chunk) {
+ assert(chunk->it_flags == ITEM_CHUNK);
+ chunk->it_flags = ITEM_SLABBED;
+ p = &slabclass[chunk->slabs_clsid];
+ chunk->slabs_clsid = 0;
+ next_chunk = chunk->next;
+
+ chunk->prev = 0;
+ chunk->next = p->slots;
+ if (chunk->next) chunk->next->prev = chunk;
+ p->slots = chunk;
+ p->sl_curr++;
+ p->requested -= chunk->size + sizeof(item_chunk);
+
+ chunk = next_chunk;
+ }
+
+ return;
+}
+
+
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
slabclass_t *p;
item *it;
@@ -278,15 +359,19 @@
p = &slabclass[id];
it = (item *)ptr;
- it->it_flags |= ITEM_SLABBED;
- it->slabs_clsid = 0;
- it->prev = 0;
- it->next = p->slots;
- if (it->next) it->next->prev = it;
- p->slots = it;
+ if ((it->it_flags & ITEM_CHUNKED) == 0) {
+ it->it_flags = ITEM_SLABBED;
+ it->slabs_clsid = 0;
+ it->prev = 0;
+ it->next = p->slots;
+ if (it->next) it->next->prev = it;
+ p->slots = it;
- p->sl_curr++;
- p->requested -= size;
+ p->sl_curr++;
+ p->requested -= size;
+ } else {
+ do_slabs_free_chunked(it, size);
+ }
return;
}
@@ -302,10 +387,15 @@
if (!stat_type) {
/* prepare general statistics for the engine */
STATS_LOCK();
- APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes);
- APPEND_STAT("curr_items", "%u", stats.curr_items);
- APPEND_STAT("total_items", "%u", stats.total_items);
+ APPEND_STAT("bytes", "%llu", (unsigned long long)stats_state.curr_bytes);
+ APPEND_STAT("curr_items", "%llu", (unsigned long long)stats_state.curr_items);
+ APPEND_STAT("total_items", "%llu", (unsigned long long)stats.total_items);
STATS_UNLOCK();
+ if (settings.slab_automove > 0) {
+ pthread_mutex_lock(&slabs_lock);
+ APPEND_STAT("slab_global_page_pool", "%u", slabclass[SLAB_GLOBAL_PAGE_POOL].slabs);
+ pthread_mutex_unlock(&slabs_lock);
+ }
item_stats_totals(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "items") == 0) {
item_stats(add_stats, c);
@@ -313,6 +403,10 @@
slabs_stats(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "sizes") == 0) {
item_stats_sizes(add_stats, c);
+ } else if (nz_strcmp(nkey, stat_type, "sizes_enable") == 0) {
+ item_stats_sizes_enable(add_stats, c);
+ } else if (nz_strcmp(nkey, stat_type, "sizes_disable") == 0) {
+ item_stats_sizes_disable(add_stats, c);
} else {
ret = false;
}
@@ -405,15 +499,33 @@
mem_avail = 0;
}
}
+ mem_malloced += size;
return ret;
}
-void *slabs_alloc(size_t size, unsigned int id, unsigned int *total_chunks) {
+/* Must only be used if all pages are item_size_max */
+static void memory_release() {
+ void *p = NULL;
+ if (mem_base != NULL)
+ return;
+
+ if (!settings.slab_reassign)
+ return;
+
+ while (mem_malloced > mem_limit &&
+ (p = get_page_from_global_pool()) != NULL) {
+ free(p);
+ mem_malloced -= settings.item_size_max;
+ }
+}
+
+void *slabs_alloc(size_t size, unsigned int id, uint64_t *total_bytes,
+ unsigned int flags) {
void *ret;
pthread_mutex_lock(&slabs_lock);
- ret = do_slabs_alloc(size, id, total_chunks);
+ ret = do_slabs_alloc(size, id, total_bytes, flags);
pthread_mutex_unlock(&slabs_lock);
return ret;
}
@@ -430,6 +542,25 @@
pthread_mutex_unlock(&slabs_lock);
}
+static bool do_slabs_adjust_mem_limit(size_t new_mem_limit) {
+ /* Cannot adjust memory limit at runtime if prealloc'ed */
+ if (mem_base != NULL)
+ return false;
+ settings.maxbytes = new_mem_limit;
+ mem_limit = new_mem_limit;
+ mem_limit_reached = false; /* Will reset on next alloc */
+ memory_release(); /* free what might already be in the global pool */
+ return true;
+}
+
+bool slabs_adjust_mem_limit(size_t new_mem_limit) {
+ bool ret;
+ pthread_mutex_lock(&slabs_lock);
+ ret = do_slabs_adjust_mem_limit(new_mem_limit);
+ pthread_mutex_unlock(&slabs_lock);
+ return ret;
+}
+
void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)
{
pthread_mutex_lock(&slabs_lock);
@@ -445,7 +576,7 @@
}
unsigned int slabs_available_chunks(const unsigned int id, bool *mem_flag,
- unsigned int *total_chunks) {
+ uint64_t *total_bytes, unsigned int *chunks_perslab) {
unsigned int ret;
slabclass_t *p;
@@ -454,12 +585,28 @@
ret = p->sl_curr;
if (mem_flag != NULL)
*mem_flag = mem_limit_reached;
- if (total_chunks != NULL)
- *total_chunks = p->slabs * p->perslab;
+ if (total_bytes != NULL)
+ *total_bytes = p->requested;
+ if (chunks_perslab != NULL)
+ *chunks_perslab = p->perslab;
pthread_mutex_unlock(&slabs_lock);
return ret;
}
+/* The slabber system could avoid needing to understand much, if anything,
+ * about items if callbacks were strategically used. Due to how the slab mover
+ * works, certain flag bits can only be adjusted while holding the slabs lock.
+ * Using these functions, isolate sections of code needing this and turn them
+ * into callbacks when an interface becomes more obvious.
+ */
+void slabs_mlock(void) {
+ pthread_mutex_lock(&slabs_lock);
+}
+
+void slabs_munlock(void) {
+ pthread_mutex_unlock(&slabs_lock);
+}
+
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;
@@ -475,7 +622,7 @@
if (slab_rebal.s_clsid < POWER_SMALLEST ||
slab_rebal.s_clsid > power_largest ||
- slab_rebal.d_clsid < POWER_SMALLEST ||
+ slab_rebal.d_clsid < SLAB_GLOBAL_PAGE_POOL ||
slab_rebal.d_clsid > power_largest ||
slab_rebal.s_clsid == slab_rebal.d_clsid)
no_go = -2;
@@ -494,9 +641,10 @@
return no_go; /* Should use a wrapper function... */
}
- s_cls->killing = 1;
-
- slab_rebal.slab_start = s_cls->slab_list[s_cls->killing - 1];
+ /* Always kill the first available slab page as it is most likely to
+ * contain the oldest items
+ */
+ slab_rebal.slab_start = s_cls->slab_list[0];
slab_rebal.slab_end = (char *)slab_rebal.slab_start +
(s_cls->size * s_cls->perslab);
slab_rebal.slab_pos = slab_rebal.slab_start;
@@ -512,12 +660,58 @@
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
- stats.slab_reassign_running = true;
+ stats_state.slab_reassign_running = true;
STATS_UNLOCK();
return 0;
}
+/* CALLED WITH slabs_lock HELD */
+static void *slab_rebalance_alloc(const size_t size, unsigned int id) {
+ slabclass_t *s_cls;
+ s_cls = &slabclass[slab_rebal.s_clsid];
+ int x;
+ item *new_it = NULL;
+
+ for (x = 0; x < s_cls->perslab; x++) {
+ new_it = do_slabs_alloc(size, id, NULL, SLABS_ALLOC_NO_NEWPAGE);
+ /* check that memory isn't within the range to clear */
+ if (new_it == NULL) {
+ break;
+ }
+ if ((void *)new_it >= slab_rebal.slab_start
+ && (void *)new_it < slab_rebal.slab_end) {
+ /* Pulled something we intend to free. Mark it as freed since
+ * we've already done the work of unlinking it from the freelist.
+ */
+ s_cls->requested -= size;
+ new_it->refcount = 0;
+ new_it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
+#ifdef DEBUG_SLAB_MOVER
+ memcpy(ITEM_key(new_it), "deadbeef", 8);
+#endif
+ new_it = NULL;
+ slab_rebal.inline_reclaim++;
+ } else {
+ break;
+ }
+ }
+ return new_it;
+}
+
+/* CALLED WITH slabs_lock HELD */
+/* detatches item/chunk from freelist. */
+static void slab_rebalance_cut_free(slabclass_t *s_cls, item *it) {
+ /* Ensure this was on the freelist and nothing else. */
+ assert(it->it_flags == ITEM_SLABBED);
+ if (s_cls->slots == it) {
+ s_cls->slots = it->next;
+ }
+ if (it->next) it->next->prev = it->prev;
+ if (it->prev) it->prev->next = it->next;
+ s_cls->sl_curr--;
+}
+
enum move_status {
MOVE_PASS=0, MOVE_FROM_SLAB, MOVE_FROM_LRU, MOVE_BUSY, MOVE_LOCKED
};
@@ -556,17 +750,26 @@
hv = 0;
hold_lock = NULL;
item *it = slab_rebal.slab_pos;
+ item_chunk *ch = NULL;
status = MOVE_PASS;
- if (it->slabs_clsid != 255) {
+ if (it->it_flags & ITEM_CHUNK) {
+ /* This chunk is a chained part of a larger item. */
+ ch = (item_chunk *) it;
+ /* Instead, we use the head chunk to find the item and effectively
+ * lock the entire structure. If a chunk has ITEM_CHUNK flag, its
+ * head cannot be slabbed, so the normal routine is safe. */
+ it = ch->head;
+ assert(it->it_flags & ITEM_CHUNKED);
+ }
+
+ /* ITEM_FETCHED when ITEM_SLABBED is overloaded to mean we've cleared
+ * the chunk for move. Only these two flags should exist.
+ */
+ if (it->it_flags != (ITEM_SLABBED|ITEM_FETCHED)) {
/* ITEM_SLABBED can only be added/removed under the slabs_lock */
if (it->it_flags & ITEM_SLABBED) {
- /* remove from slab freelist */
- if (s_cls->slots == it) {
- s_cls->slots = it->next;
- }
- if (it->next) it->next->prev = it->prev;
- if (it->prev) it->prev->next = it->next;
- s_cls->sl_curr--;
+ assert(ch == NULL);
+ slab_rebalance_cut_free(s_cls, it);
status = MOVE_FROM_SLAB;
} else if ((it->it_flags & ITEM_LINKED) != 0) {
/* If it doesn't have ITEM_SLABBED, the item could be in any
@@ -578,7 +781,7 @@
if ((hold_lock = item_trylock(hv)) == NULL) {
status = MOVE_LOCKED;
} else {
- refcount = refcount_incr(&it->refcount);
+ refcount = refcount_incr(it);
if (refcount == 2) { /* item is linked but not busy */
/* Double check ITEM_LINKED flag here, since we're
* past a memory barrier from the mutex. */
@@ -599,13 +802,20 @@
}
/* Item lock must be held while modifying refcount */
if (status == MOVE_BUSY) {
- refcount_decr(&it->refcount);
+ refcount_decr(it);
item_trylock_unlock(hold_lock);
}
}
+ } else {
+ /* See above comment. No ITEM_SLABBED or ITEM_LINKED. Mark
+ * busy and wait for item to complete its upload. */
+ status = MOVE_BUSY;
}
}
+ int save_item = 0;
+ item *new_it = NULL;
+ size_t ntotal = 0;
switch (status) {
case MOVE_FROM_LRU:
/* Lock order is LRU locks -> slabs_lock. unlink uses LRU lock.
@@ -614,14 +824,100 @@
* (2) + the item is locked. Drop slabs lock, drop item to
* refcount 1 (just our own, then fall through and wipe it
*/
+ /* Check if expired or flushed */
+ ntotal = ITEM_ntotal(it);
+ /* REQUIRES slabs_lock: CHECK FOR cls->sl_curr > 0 */
+ if (ch == NULL && (it->it_flags & ITEM_CHUNKED)) {
+ /* Chunked should be identical to non-chunked, except we need
+ * to swap out ntotal for the head-chunk-total. */
+ ntotal = s_cls->size;
+ }
+ if ((it->exptime != 0 && it->exptime < current_time)
+ || item_is_flushed(it)) {
+ /* Expired, don't save. */
+ save_item = 0;
+ } else if (ch == NULL &&
+ (new_it = slab_rebalance_alloc(ntotal, slab_rebal.s_clsid)) == NULL) {
+ /* Not a chunk of an item, and nomem. */
+ save_item = 0;
+ slab_rebal.evictions_nomem++;
+ } else if (ch != NULL &&
+ (new_it = slab_rebalance_alloc(s_cls->size, slab_rebal.s_clsid)) == NULL) {
+ /* Is a chunk of an item, and nomem. */
+ save_item = 0;
+ slab_rebal.evictions_nomem++;
+ } else {
+ /* Was whatever it was, and we have memory for it. */
+ save_item = 1;
+ }
pthread_mutex_unlock(&slabs_lock);
- do_item_unlink(it, hv);
+ unsigned int requested_adjust = 0;
+ if (save_item) {
+ if (ch == NULL) {
+ assert((new_it->it_flags & ITEM_CHUNKED) == 0);
+ /* if free memory, memcpy. clear prev/next/h_bucket */
+ memcpy(new_it, it, ntotal);
+ new_it->prev = 0;
+ new_it->next = 0;
+ new_it->h_next = 0;
+ /* These are definitely required. else fails assert */
+ new_it->it_flags &= ~ITEM_LINKED;
+ new_it->refcount = 0;
+ do_item_replace(it, new_it, hv);
+ /* Need to walk the chunks and repoint head */
+ if (new_it->it_flags & ITEM_CHUNKED) {
+ item_chunk *fch = (item_chunk *) ITEM_data(new_it);
+ fch->next->prev = fch;
+ while (fch) {
+ fch->head = new_it;
+ fch = fch->next;
+ }
+ }
+ it->refcount = 0;
+ it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
+#ifdef DEBUG_SLAB_MOVER
+ memcpy(ITEM_key(it), "deadbeef", 8);
+#endif
+ slab_rebal.rescues++;
+ requested_adjust = ntotal;
+ } else {
+ item_chunk *nch = (item_chunk *) new_it;
+ /* Chunks always have head chunk (the main it) */
+ ch->prev->next = nch;
+ if (ch->next)
+ ch->next->prev = nch;
+ memcpy(nch, ch, ch->used + sizeof(item_chunk));
+ ch->refcount = 0;
+ ch->it_flags = ITEM_SLABBED|ITEM_FETCHED;
+ slab_rebal.chunk_rescues++;
+#ifdef DEBUG_SLAB_MOVER
+ memcpy(ITEM_key((item *)ch), "deadbeef", 8);
+#endif
+ refcount_decr(it);
+ requested_adjust = s_cls->size;
+ }
+ } else {
+ /* restore ntotal in case we tried saving a head chunk. */
+ ntotal = ITEM_ntotal(it);
+ do_item_unlink(it, hv);
+ slabs_free(it, ntotal, slab_rebal.s_clsid);
+ /* Swing around again later to remove it from the freelist. */
+ slab_rebal.busy_items++;
+ was_busy++;
+ }
item_trylock_unlock(hold_lock);
pthread_mutex_lock(&slabs_lock);
+ /* Always remove the ntotal, as we added it in during
+ * do_slabs_alloc() when copying the item.
+ */
+ s_cls->requested -= requested_adjust;
+ break;
case MOVE_FROM_SLAB:
it->refcount = 0;
- it->it_flags = 0;
- it->slabs_clsid = 255;
+ it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
+#ifdef DEBUG_SLAB_MOVER
+ memcpy(ITEM_key(it), "deadbeef", 8);
+#endif
break;
case MOVE_BUSY:
case MOVE_LOCKED:
@@ -641,6 +937,9 @@
/* Some items were busy, start again from the top */
if (slab_rebal.busy_items) {
slab_rebal.slab_pos = slab_rebal.slab_start;
+ STATS_LOCK();
+ stats.slab_reassign_busy_items += slab_rebal.busy_items;
+ STATS_UNLOCK();
slab_rebal.busy_items = 0;
} else {
slab_rebal.done++;
@@ -655,23 +954,50 @@
static void slab_rebalance_finish(void) {
slabclass_t *s_cls;
slabclass_t *d_cls;
+ int x;
+ uint32_t rescues;
+ uint32_t evictions_nomem;
+ uint32_t inline_reclaim;
+ uint32_t chunk_rescues;
pthread_mutex_lock(&slabs_lock);
s_cls = &slabclass[slab_rebal.s_clsid];
- d_cls = &slabclass[slab_rebal.d_clsid];
+ d_cls = &slabclass[slab_rebal.d_clsid];
- /* At this point the stolen slab is completely clear */
- s_cls->slab_list[s_cls->killing - 1] =
- s_cls->slab_list[s_cls->slabs - 1];
- s_cls->slabs--;
- s_cls->killing = 0;
+#ifdef DEBUG_SLAB_MOVER
+ /* If the algorithm is broken, live items can sneak in. */
+ slab_rebal.slab_pos = slab_rebal.slab_start;
+ while (1) {
+ item *it = slab_rebal.slab_pos;
+ assert(it->it_flags == (ITEM_SLABBED|ITEM_FETCHED));
+ assert(memcmp(ITEM_key(it), "deadbeef", 8) == 0);
+ it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
+ slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size;
+ if (slab_rebal.slab_pos >= slab_rebal.slab_end)
+ break;
+ }
+#endif
- memset(slab_rebal.slab_start, 0, (size_t)settings.item_size_max);
+ /* At this point the stolen slab is completely clear.
+ * We always kill the "first"/"oldest" slab page in the slab_list, so
+ * shuffle the page list backwards and decrement.
+ */
+ s_cls->slabs--;
+ for (x = 0; x < s_cls->slabs; x++) {
+ s_cls->slab_list[x] = s_cls->slab_list[x+1];
+ }
d_cls->slab_list[d_cls->slabs++] = slab_rebal.slab_start;
- split_slab_page_into_freelist(slab_rebal.slab_start,
- slab_rebal.d_clsid);
+ /* Don't need to split the page into chunks if we're just storing it */
+ if (slab_rebal.d_clsid > SLAB_GLOBAL_PAGE_POOL) {
+ memset(slab_rebal.slab_start, 0, (size_t)settings.item_size_max);
+ split_slab_page_into_freelist(slab_rebal.slab_start,
+ slab_rebal.d_clsid);
+ } else if (slab_rebal.d_clsid == SLAB_GLOBAL_PAGE_POOL) {
+ /* mem_malloc'ed might be higher than mem_limit. */
+ memory_release();
+ }
slab_rebal.done = 0;
slab_rebal.s_clsid = 0;
@@ -679,14 +1005,25 @@
slab_rebal.slab_start = NULL;
slab_rebal.slab_end = NULL;
slab_rebal.slab_pos = NULL;
+ evictions_nomem = slab_rebal.evictions_nomem;
+ inline_reclaim = slab_rebal.inline_reclaim;
+ rescues = slab_rebal.rescues;
+ chunk_rescues = slab_rebal.chunk_rescues;
+ slab_rebal.evictions_nomem = 0;
+ slab_rebal.inline_reclaim = 0;
+ slab_rebal.rescues = 0;
slab_rebalance_signal = 0;
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
- stats.slab_reassign_running = false;
stats.slabs_moved++;
+ stats.slab_reassign_rescues += rescues;
+ stats.slab_reassign_evictions_nomem += evictions_nomem;
+ stats.slab_reassign_inline_reclaim += inline_reclaim;
+ stats.slab_reassign_chunk_rescues += chunk_rescues;
+ stats_state.slab_reassign_running = false;
STATS_UNLOCK();
if (settings.verbose > 1) {
@@ -694,97 +1031,6 @@
}
}
-/* Return 1 means a decision was reached.
- * Move to its own thread (created/destroyed as needed) once automover is more
- * complex.
- */
-static int slab_automove_decision(int *src, int *dst) {
- static uint64_t evicted_old[MAX_NUMBER_OF_SLAB_CLASSES];
- static unsigned int slab_zeroes[MAX_NUMBER_OF_SLAB_CLASSES];
- static unsigned int slab_winner = 0;
- static unsigned int slab_wins = 0;
- uint64_t evicted_new[MAX_NUMBER_OF_SLAB_CLASSES];
- uint64_t evicted_diff = 0;
- uint64_t evicted_max = 0;
- unsigned int highest_slab = 0;
- unsigned int total_pages[MAX_NUMBER_OF_SLAB_CLASSES];
- int i;
- int source = 0;
- int dest = 0;
- static rel_time_t next_run;
-
- /* Run less frequently than the slabmove tester. */
- if (current_time >= next_run) {
- next_run = current_time + 10;
- } else {
- return 0;
- }
-
- item_stats_evictions(evicted_new);
- pthread_mutex_lock(&slabs_lock);
- for (i = POWER_SMALLEST; i < power_largest; i++) {
- total_pages[i] = slabclass[i].slabs;
- }
- pthread_mutex_unlock(&slabs_lock);
-
- /* Find a candidate source; something with zero evicts 3+ times */
- for (i = POWER_SMALLEST; i < power_largest; i++) {
- evicted_diff = evicted_new[i] - evicted_old[i];
- if (evicted_diff == 0 && total_pages[i] > 2) {
- slab_zeroes[i]++;
- if (source == 0 && slab_zeroes[i] >= 3)
- source = i;
- } else {
- slab_zeroes[i] = 0;
- if (evicted_diff > evicted_max) {
- evicted_max = evicted_diff;
- highest_slab = i;
- }
- }
- evicted_old[i] = evicted_new[i];
- }
-
- /* Pick a valid destination */
- if (slab_winner != 0 && slab_winner == highest_slab) {
- slab_wins++;
- if (slab_wins >= 3)
- dest = slab_winner;
- } else {
- slab_wins = 1;
- slab_winner = highest_slab;
- }
-
- if (source && dest) {
- *src = source;
- *dst = dest;
- return 1;
- }
- return 0;
-}
-
-/* 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 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
*/
@@ -854,8 +1100,8 @@
/* 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)
+ if (src < POWER_SMALLEST || src > power_largest ||
+ dst < SLAB_GLOBAL_PAGE_POOL || dst > power_largest)
return REASSIGN_BADCLASS;
if (slabclass[src].slabs < 2)
@@ -889,7 +1135,6 @@
pthread_mutex_unlock(&slabs_rebalance_lock);
}
-static pthread_t maintenance_tid;
static pthread_t rebalance_tid;
int start_slab_maintenance_thread(void) {
@@ -910,11 +1155,6 @@
}
pthread_mutex_init(&slabs_rebalance_lock, NULL);
- if ((ret = pthread_create(&maintenance_tid, NULL,
- slab_maintenance_thread, NULL)) != 0) {
- 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));
@@ -933,6 +1173,5 @@
pthread_mutex_unlock(&slabs_rebalance_lock);
/* Wait for the maintenance thread to stop */
- pthread_join(maintenance_tid, NULL);
pthread_join(rebalance_tid, NULL);
}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/slabs.h
^
|
@@ -8,7 +8,7 @@
3rd argument specifies if the slab allocator should allocate all memory
up front (if true), or allocate memory in chunks as it is needed (if false)
*/
-void slabs_init(const size_t limit, const double factor, const bool prealloc);
+void slabs_init(const size_t limit, const double factor, const bool prealloc, const uint32_t *slab_sizes);
/**
@@ -19,7 +19,8 @@
unsigned int slabs_clsid(const size_t size);
/** Allocate object of given length. 0 on error */ /*@null@*/
-void *slabs_alloc(const size_t size, unsigned int id, unsigned int *total_chunks);
+#define SLABS_ALLOC_NO_NEWPAGE 1
+void *slabs_alloc(const size_t size, unsigned int id, uint64_t *total_bytes, unsigned int flags);
/** Free previously allocated object */
void slabs_free(void *ptr, size_t size, unsigned int id);
@@ -27,6 +28,9 @@
/** Adjust the stats for memory requested */
void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal);
+/** Adjust global memory limit up or down */
+bool slabs_adjust_mem_limit(size_t new_mem_limit);
+
/** Return a datum for stats in binary protocol */
bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c);
@@ -34,7 +38,10 @@
void slabs_stats(ADD_STAT add_stats, void *c);
/* Hints as to freespace in slab class */
-unsigned int slabs_available_chunks(unsigned int id, bool *mem_flag, unsigned int *total_chunks);
+unsigned int slabs_available_chunks(unsigned int id, bool *mem_flag, uint64_t *total_bytes, unsigned int *chunks_perslab);
+
+void slabs_mlock(void);
+void slabs_munlock(void);
int start_slab_maintenance_thread(void);
void stop_slab_maintenance_thread(void);
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/00-startup.t
^
|
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use strict;
-use Test::More tests => 18;
+use Test::More tests => 20;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -75,6 +75,16 @@
};
ok($@, "Died with illegal -B arg.");
+# Maximum connections must be greater than 0.
+eval {
+ my $server = new_memcached("-c 0");
+};
+ok($@, "Died with invalid maximum connections 0.");
+eval {
+ my $server = new_memcached("-c -1");
+};
+ok($@, "Died with invalid maximum connections -1.");
+
# Should not allow -t 0
eval {
my $server = new_memcached("-t 0");
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/binary.t
^
|
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use Test::More tests => 3639;
+use Test::More tests => 4963;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -61,7 +61,6 @@
# Let's turn on detail stats for all this stuff
$mc->stats('detail on');
-
my $check = sub {
my ($key, $orig_flags, $orig_val) = @_;
my ($flags, $val, $cas) = $mc->get($key);
@@ -122,6 +121,23 @@
$empty->('y');
{
+ # diag "Some chunked item tests";
+ my $s2 = new_memcached('-o slab_chunk_max=4096');
+ ok($s2, "started the server");
+ my $m2 = MC::Client->new($s2);
+ # Specifically trying to cross the chunk boundary when internally
+ # appending CLRF.
+ for my $k (7900..8100) {
+ my $val = 'd' x $k;
+ $val .= '123';
+ $m2->set('t', $val, 0, 0);
+ # Ensure we get back the same value. Bugs can chop chars.
+ my (undef, $gval, undef) = $m2->get('t');
+ ok($gval eq $val, $gval . " = " . $val);
+ }
+}
+
+{
# diag "Add";
$empty->('i');
$mc->add('i', 'ex', 5, 10);
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/cas.t
^
|
@@ -152,7 +152,7 @@
print $sock "gets bug15\r\n";
ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success");
my $next_bug15_cas = $1;
-is(scalar <$sock>, "1\r\n", "gets bug15 data is 0");
+is(scalar <$sock>, "1\r\n", "gets bug15 data is 1");
is(scalar <$sock>, "END\r\n","gets bug15 END");
ok($bug15_cas != $next_bug15_cas, "CAS changed");
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/chunked-items.t
^
|
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+# Networked logging tests.
+
+use strict;
+use warnings;
+
+use Test::More;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+my $server = new_memcached('-m 48 -o slab_chunk_max=16384');
+my $sock = $server->sock;
+
+# We're testing to ensure item chaining doesn't corrupt or poorly overlap
+# data, so create a non-repeating pattern.
+my @parts = ();
+for (1 .. 8000) {
+ push(@parts, $_);
+}
+my $pattern = join(':', @parts);
+
+my $plen = length($pattern);
+
+print $sock "set pattern 0 0 $plen\r\n$pattern\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored pattern successfully");
+
+mem_get_is($sock, "pattern", $pattern);
+
+for (1..5) {
+ my $size = 400 * 1024;
+ my $data = "x" x $size;
+ print $sock "set foo$_ 0 0 $size\r\n$data\r\n";
+ my $res = <$sock>;
+ is($res, "STORED\r\n", "stored some big items");
+}
+
+{
+ my $max = 1024 * 1024;
+ my $big = "a big value that's > .5M and < 1M. ";
+ while (length($big) * 2 < $max) {
+ $big = $big . $big;
+ }
+ my $biglen = length($big);
+
+ for (1..100) {
+ print $sock "set toast$_ 0 0 $biglen\r\n$big\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored big");
+ mem_get_is($sock, "toast$_", $big);
+ }
+}
+
+# Test a wide range of sets.
+{
+ my $len = 1024 * 200;
+ while ($len < 1024 * 1024) {
+ my $val = "B" x $len;
+ print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored size $len");
+ $len += 2048;
+ }
+}
+
+# Test long appends and prepends.
+# Note: memory bloats like crazy if we use one test per request.
+{
+ my $str = 'seedstring';
+ my $len = length($str);
+ print $sock "set appender 0 0 $len\r\n$str\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored seed string for append");
+ my $unexpected = 0;
+ for my $part (@parts) {
+ # reduce required loops but still have a pattern.
+ my $todo = $part . "x" x 10;
+ $str .= $todo;
+ my $len = length($todo);
+ print $sock "append appender 0 0 $len\r\n$todo\r\n";
+ is(scalar <$sock>, "STORED\r\n", "appened $todo size $len");
+ print $sock "get appender\r\n";
+ my $header = scalar <$sock>;
+ my $body = scalar <$sock>;
+ my $end = scalar <$sock>;
+ $unexpected++ unless $body eq "$str\r\n";
+ }
+ is($unexpected, 0, "No unexpected results during appends\n");
+ # Now test appending a chunked item to a chunked item.
+ $len = length($str);
+ print $sock "append appender 0 0 $len\r\n$str\r\n";
+ is(scalar <$sock>, "STORED\r\n", "append large string size $len");
+ mem_get_is($sock, "appender", $str . $str);
+ print $sock "delete appender\r\n";
+ is(scalar <$sock>, "DELETED\r\n", "removed appender key");
+}
+
+{
+ my $str = 'seedstring';
+ my $len = length($str);
+ print $sock "set prepender 0 0 $len\r\n$str\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored seed string for append");
+ my $unexpected = 0;
+ for my $part (@parts) {
+ # reduce required loops but still have a pattern.
+ $part .= "x" x 10;
+ $str = $part . $str;
+ my $len = length($part);
+ print $sock "prepend prepender 0 0 $len\r\n$part\r\n";
+ is(scalar <$sock>, "STORED\r\n", "prepend $part size $len");
+ print $sock "get prepender\r\n";
+ my $header = scalar <$sock>;
+ my $body = scalar <$sock>;
+ my $end = scalar <$sock>;
+ $unexpected++ unless $body eq "$str\r\n";
+ }
+ is($unexpected, 0, "No unexpected results during prepends\n");
+ # Now test prepending a chunked item to a chunked item.
+ $len = length($str);
+ print $sock "prepend prepender 0 0 $len\r\n$str\r\n";
+ is(scalar <$sock>, "STORED\r\n", "prepend large string size $len");
+ mem_get_is($sock, "prepender", $str . $str);
+ print $sock "delete prepender\r\n";
+ is(scalar <$sock>, "DELETED\r\n", "removed prepender key");
+}
+
+done_testing();
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/dyn-maxbytes.t
^
|
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+# Test the 'stats items' evictions counters.
+
+use strict;
+use Test::More tests => 309;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+my $server = new_memcached("-m 3 -o modern");
+my $sock = $server->sock;
+my $value = "B"x66560;
+my $key = 0;
+
+# These aren't set to expire.
+for ($key = 0; $key < 40; $key++) {
+ print $sock "set key$key 0 0 66560\r\n$value\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored key$key");
+}
+
+my $stats = mem_stats($sock);
+my $evicted = $stats->{evictions};
+isnt($evicted, "0", "check evicted");
+
+# We're past the memory limit. Try adjusting maxbytes upward.
+$stats = mem_stats($sock, "settings");
+my $pre_maxbytes = $stats->{"maxbytes"};
+print $sock "cache_memlimit 8\r\n";
+is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 3m to 8m");
+
+# Confirm maxbytes updated.
+$stats = mem_stats($sock, "settings");
+isnt($stats->{"maxbytes"}, $pre_maxbytes, "stats settings maxbytes updated");
+
+# Check for total_malloced increasing as new memory is added
+$stats = mem_stats($sock, "slabs");
+my $t_malloc = $stats->{"total_malloced"};
+
+print $sock "set toast 0 0 66560\r\n$value\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored toast");
+$stats = mem_stats($sock, "slabs");
+cmp_ok($stats->{"total_malloced"}, '>', $t_malloc, "stats slabs total_malloced increased");
+
+$stats = mem_stats($sock);
+my $new_evicted = $stats->{evictions};
+cmp_ok($new_evicted, '==', $evicted, "no new evictions");
+
+# Bump up to 16, fill a bit more, then delete everything.
+print $sock "cache_memlimit 16\r\n";
+is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 8m to 16m");
+for (;$key < 150; $key++) {
+ print $sock "set key$key 0 0 66560\r\n$value\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored key$key");
+}
+
+# Grab total_malloced after filling everything up.
+$stats = mem_stats($sock, "slabs");
+$t_malloc = $stats->{"total_malloced"};
+print $sock "cache_memlimit 8\r\n";
+is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 16m to 8m");
+
+# Remove all of the keys, allowing the slab rebalancer to push pages toward
+# the global page pool.
+for ($key = 0; $key < 150; $key++) {
+ print $sock "delete key$key\r\n";
+ like(scalar <$sock>, qr/(DELETED|NOT_FOUND)\r\n/, "deleted key$key");
+}
+
+# If memory limit is lower, it should free those pages back to the OS.
+my $reduced = 0;
+for (my $tries = 0; $tries < 6; $tries++) {
+ sleep 1;
+ $stats = mem_stats($sock, "slabs");
+ $reduced = $stats->{"total_malloced"} if ($t_malloc > $stats->{"total_malloced"});
+ last if $reduced;
+}
+
+isnt($reduced, 0, "total_malloced reduced to $reduced");
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/flags.t
^
|
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use strict;
-use Test::More tests => 6;
+use Test::More tests => 8;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -10,7 +10,7 @@
my $sock = $server->sock;
# set foo (and should get it)
-for my $flags (0, 123, 2**16-1) {
+for my $flags (0, 123, 2**16-1, 2**31) {
print $sock "set foo $flags 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is({ sock => $sock,
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/idle-timeout.t
^
|
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+# start up a server with 10 maximum connections
+my $server = new_memcached("-o idle_timeout=3");
+my $sock = $server->sock;
+
+# Make sure we can talk to start with
+my $stats = mem_stats($sock);
+is($stats->{idle_kicks}, "0", "check stats initial");
+isnt($sock->connected(), undef, "check connected");
+
+# Make sure we don't timeout when active
+for (my $i = 0; $i < 6; $i++) {
+ $stats = mem_stats($sock);
+ isnt($stats->{version}, undef, "check active $i");
+}
+$stats = mem_stats($sock);
+is($stats->{idle_kicks}, "0", "check stats 2");
+
+# Make sure we do timeout when not
+sleep(5);
+mem_stats($sock); # Network activity, so socket code will see dead socket
+sleep(1);
+is($sock->connected(), undef, "check disconnected");
+
+$sock = $server->sock;
+$stats = mem_stats($sock);
+isnt($stats->{idle_kicks}, 0, "check stats timeout");
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/inline_asciihdr.t
^
|
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+# Ensure get and gets can mirror flags + CAS properly when not inlining the
+# ascii response header.
+
+use strict;
+use Test::More tests => 17;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+my $server = new_memcached('-o no_inline_ascii_resp');
+my $sock = $server->sock;
+
+# 0 flags and size
+print $sock "set foo 0 0 0\r\n\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored");
+
+mem_get_is($sock, "foo", "");
+
+for my $flags (0, 123, 2**16-1, 2**31, 2**32-1) {
+ print $sock "set foo $flags 0 6\r\nfooval\r\n";
+ is(scalar <$sock>, "STORED\r\n", "stored foo");
+ mem_get_is({ sock => $sock,
+ flags => $flags }, "foo", "fooval", "got flags $flags back");
+ my @res = mem_gets($sock, "foo");
+ mem_gets_is({ sock => $sock,
+ flags => $flags }, $res[0], "foo", "fooval", "got flags $flags back");
+
+}
+
+
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/issue_42.t
^
|
@@ -18,4 +18,4 @@
my $first_stats = mem_stats($sock, "slabs");
my $req = $first_stats->{"1:mem_requested"};
-ok ($req == "640" || $req == "800", "Check allocated size");
+ok ($req == "640" || $req == "800" || $req == "770", "Check allocated size");
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/item_size_max.t
^
|
@@ -27,25 +27,25 @@
ok($@ && $@ =~ m/^Failed/, "Shouldn't start with > 128m item max");
# Minimum.
-$server = new_memcached('-I 1024');
+$server = new_memcached('-I 1024 -o slab_chunk_max=1024');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 1024);
$server->stop();
# Reasonable but unreasonable.
-$server = new_memcached('-I 1049600');
+$server = new_memcached('-I 2097152');
my $stats = mem_stats($server->sock, ' settings');
-is($stats->{item_size_max}, 1049600);
+is($stats->{item_size_max}, 2097152);
$server->stop();
# Suffix kilobytes.
-$server = new_memcached('-I 512k');
+$server = new_memcached('-I 512k -o slab_chunk_max=16384');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 524288);
$server->stop();
# Suffix megabytes.
-$server = new_memcached('-I 32m');
+$server = new_memcached('-m 256 -I 32m');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 33554432);
$server->stop();
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/lru-maintainer.t
^
|
@@ -1,13 +1,28 @@
#!/usr/bin/perl
use strict;
-use Test::More tests => 224;
+use warnings;
+use Test::More tests => 226;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
-my $server = new_memcached('-m 6 -o lru_maintainer,lru_crawler');
+# Regression test for underestimating the size of items after the large memory
+# change.
+my $server = new_memcached('-m 3 -o lru_maintainer,lru_crawler');
my $sock = $server->sock;
+my $keystub = "X"x200;
+for (1 .. 15000) {
+ print $sock "set $keystub$_ 0 0 2 noreply\r\nok\r\n";
+}
+# There's probably already an error on the wire, so we'll see that.
+$keystub .= "20001";
+print $sock "set $keystub 0 0 2\r\nok\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored key without OOM");
+
+# Basic tests
+$server = new_memcached('-m 6 -o lru_maintainer,lru_crawler');
+$sock = $server->sock;
for (1 .. 10) {
print $sock "set ifoo$_ 0 1 2\r\nok\r\n";
@@ -38,7 +53,8 @@
last;
}
isnt($stats->{"items:31:moves_to_cold"}, 0, "moved some items to cold");
- # Fetch the canary once, so it's now marked as active.
+ # Items need two fetches to become active
+ mem_get_is($sock, "canary", $value);
mem_get_is($sock, "canary", $value);
}
print $sock "set key$key 0 0 66560\r\n$value\r\n";
@@ -49,32 +65,32 @@
my $stats = mem_stats($sock);
isnt($stats->{evictions}, 0, "some evictions happened");
my $istats = mem_stats($sock, "items");
- isnt($stats->{"items:31:number_warm"}, 0, "our canary moved to warm");
+ isnt($istats->{"items:31:number_warm"}, 0, "our canary moved to warm");
+ use Data::Dumper qw/Dumper/;
}
# Key should've been saved to the WARM_LRU, and still exists.
mem_get_is($sock, "canary", $value);
-# Test NOEXP_LRU
-$server = new_memcached('-m 2 -o lru_maintainer,lru_crawler,expirezero_does_not_evict');
+# Test TEMP_LRU
+$server = new_memcached('-m 2 -o lru_maintainer,lru_crawler,temporary_ttl=61');
$sock = $server->sock;
{
my $stats = mem_stats($sock, "settings");
- is($stats->{expirezero_does_not_evict}, "yes");
+ is($stats->{temp_lru}, "yes");
}
-print $sock "set canary 0 0 66560\r\n$value\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored noexpire canary key");
+print $sock "set canary 0 30 66560\r\n$value\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored temporary canary key");
{
my $stats = mem_stats($sock, "items");
- is($stats->{"items:31:number_noexp"}, 1, "one item in noexpire LRU");
is($stats->{"items:31:number_hot"}, 0, "item did not go into hot LRU");
}
# *Not* fetching the key, and flushing the slab class with junk.
-# Using keys with actual TTL's here.
+# Using keys with higher TTL's here.
for (my $key = 0; $key < 100; $key++) {
print $sock "set key$key 0 3600 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
@@ -82,9 +98,10 @@
{
my $stats = mem_stats($sock, "items");
- isnt($stats->{evictions}, 0, "some evictions happened");
- isnt($stats->{number_hot}, 0, "nonzero exptime items went into hot LRU");
+ isnt($stats->{"items:31:evictions"}, 0, "some evictions happened");
+ isnt($stats->{"items:31:number_hot"}, 0, "high exptime items went into hot LRU");
+ is($stats->{"items:31:number_temp"}, 1, "still one item in temporary LRU");
}
# Canary should still exist, even unfetched, because it's protected by
-# noexpire.
+# temp LRU
mem_get_is($sock, "canary", $value);
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/slabs-reassign-chunked.t
^
|
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 8;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,slab_chunk_max=4096');
+my $sock = $server->sock;
+
+sub dump_stats {
+ my $s = shift;
+ my $filter = shift || '';
+ for my $k (sort keys %$s) {
+ if ($filter) {
+ next unless $k =~ m/$filter/;
+ }
+ print STDERR "STAT: $k = ", $s->{$k}, "\n";
+ }
+}
+
+my $value;
+{
+ my @chars = ("C".."Z");
+ for (1 .. 11000) {
+ $value .= $chars[rand @chars];
+ }
+}
+my $keycount = 5100;
+
+my $res;
+for (1 .. $keycount) {
+# print STDERR "HI $_\n";
+ print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n";
+# print $sock "set nfoo$_ 0 0 11000\r\n$value\r\n";
+# my $res = scalar <$sock>;
+# print STDERR "RES: $res\n";
+}
+
+my $todelete = 0;
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items");
+ $todelete = $stats->{curr_items} / 2;
+# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
+# print STDERR "$_: ", $stats->{$_}, "\n";
+# }
+}
+
+{
+ my $s = mem_stats($sock, 'slabs');
+ my $sid;
+ # Find the highest ID to source from.
+ for my $k (keys %$s) {
+ next unless $k =~ m/^(\d+):/;
+ $sid = $s->{$k} if $s->{$k} > $1;
+ }
+ for (my $x = 0; $x < 3; $x++) {
+ print $sock "slabs reassign 17 0\r\n";
+ my $res = scalar <$sock>;
+ chomp $res;
+ # print STDERR "SLABS REASSIGN RESULT: $res\n";
+ sleep 1;
+ }
+}
+
+# Make room in old class so rescues can happen when we switch slab classes.
+#for (1 .. $todelete) {
+# print $sock "delete nfoo$_ noreply\r\n";
+#}
+
+# Give LRU mover some time to reclaim slab chunks.
+#sleep 1;
+
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{slab_global_page_pool}, '>', 0, 'global page pool > 0');
+ cmp_ok($stats->{slab_reassign_chunk_rescues}, '>', 0, 'some chunk rescues happened');
+}
+
+{
+ my $hits = 0;
+ for (1 .. $keycount) {
+ print $sock "get nfoo$_\r\n";
+ my $body = scalar(<$sock>);
+ my $expected = "VALUE nfoo$_ 0 11000\r\n$value\r\nEND\r\n";
+ if ($body =~ /^END/) {
+ next;
+ } else {
+ $body .= scalar(<$sock>) . scalar(<$sock>);
+ if ($body ne $expected) {
+ die "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n";
+ }
+ $hits++;
+ }
+ }
+ cmp_ok($hits, '>', 0, "fetched back $hits values after reassignment");
+}
+
+$value = "A"x3000;
+for (1 .. $keycount) {
+ print $sock "set ifoo$_ 0 0 3000 noreply\r\n$value\r\n";
+}
+
+my $missing = 0;
+my $hits = 0;
+for (1 .. $keycount) {
+ print $sock "get ifoo$_\r\n";
+ my $body = scalar(<$sock>);
+ my $expected = "VALUE ifoo$_ 0 3000\r\n$value\r\nEND\r\n";
+ if ($body =~ /^END/) {
+ $missing++;
+ } else {
+ $body .= scalar(<$sock>) . scalar(<$sock>);
+ if ($body ne $expected) {
+ print STDERR "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n";
+ } else {
+ $hits++;
+ }
+ }
+}
+#print STDERR "HITS: $hits, MISSES: $missing\n";
+
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000');
+# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
+# print STDERR "$_: ", $stats->{$_}, "\n";
+# }
+}
+
+cmp_ok($hits, '>', 4000, 'were able to fetch back 2/3rds of 8k keys');
+my $stats_done = mem_stats($sock);
+cmp_ok($stats_done->{slab_reassign_chunk_rescues}, '>', 0, 'some reassign chunk rescues happened');
+# Reassign rescues won't happen here because the headers are of a different
+# size and we aren't moving pages out of that slab class
+#cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues happened');
+cmp_ok($stats_done->{slab_reassign_evictions_nomem}, '>', 0, 'some reassign evictions happened');
+
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/slabs-reassign2.t
^
|
@@ -0,0 +1,140 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 12;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+use Data::Dumper qw/Dumper/;
+
+my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer');
+my $sock = $server->sock;
+
+my $value = "B"x11000;
+my $keycount = 5000;
+
+my $res;
+for (1 .. $keycount) {
+ print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n";
+}
+
+my $todelete = 0;
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items");
+ $todelete = $stats->{curr_items};
+# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
+# print STDERR "$_: ", $stats->{$_}, "\n";
+# }
+}
+
+# Make room in old class so rescues can happen when we switch slab classes.
+for (1 .. $todelete) {
+ next unless $_ % 2 == 0;
+ print $sock "delete nfoo$_ noreply\r\n";
+}
+
+{
+ my $tries;
+ for ($tries = 20; $tries > 0; $tries--) {
+ sleep 1;
+ my $stats = mem_stats($sock);
+ if ($stats->{slab_global_page_pool} > 0) {
+ last;
+ }
+ }
+ cmp_ok($tries, '>', 0, 'some pages moved back to global pool');
+}
+
+$value = "B"x7000;
+for (1 .. $keycount) {
+ print $sock "set ifoo$_ 0 0 7000 noreply\r\n$value\r\n";
+}
+
+my $missing = 0;
+my $hits = 0;
+for (1 .. $keycount) {
+ print $sock "get ifoo$_\r\n";
+ my $body = scalar(<$sock>);
+ my $expected = "VALUE ifoo$_ 0 7000\r\n$value\r\nEND\r\n";
+ if ($body =~ /^END/) {
+ $missing++;
+ } else {
+ $body .= scalar(<$sock>) . scalar(<$sock>);
+ if ($body ne $expected) {
+ print STDERR "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n";
+ } else {
+ $hits++;
+ }
+ }
+}
+#print STDERR "HITS: $hits, MISSES: $missing\n";
+
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000');
+# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
+# print STDERR "$_: ", $stats->{$_}, "\n";
+# }
+}
+
+# Force reassign evictions by moving too much memory manually.
+{
+ my $s = mem_stats($sock, 'slabs');
+ my $max_pages = 0;
+ my $scls = 0;
+ for my $k (keys %$s) {
+ next unless $k =~ m/^(\d+)\:total_pages/;
+ if ($s->{$k} > $max_pages) {
+ $max_pages = $s->{$k};
+ $scls = $1;
+ }
+ }
+ my $tries;
+ for ($tries = 10; $tries > 0; $tries--) {
+ print $sock "slabs reassign $scls 1\r\n";
+ my $res = <$sock>;
+ sleep 1;
+ my $s = mem_stats($sock);
+ last if $s->{slab_reassign_evictions_nomem} > 0;
+ }
+ cmp_ok($tries, '>', 0, 'some reassign evictions happened');
+}
+cmp_ok($hits, '>', 2000, 'were able to fetch back some of the small keys');
+my $stats_done = mem_stats($sock);
+cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues happened');
+
+print $sock "flush_all\r\n";
+is(scalar <$sock>, "OK\r\n", "did flush_all");
+my $tries;
+for ($tries = 20; $tries > 0; $tries--) {
+ sleep 1;
+ my $stats = mem_stats($sock);
+ if ($stats->{slab_global_page_pool} > 50) {
+ last;
+ }
+}
+cmp_ok($tries, '>', 0, 'reclaimed at least 50 pages before timeout');
+
+{
+ my $stats = mem_stats($sock, "slabs");
+ is($stats->{total_malloced}, 62914560, "total_malloced is what we expect");
+}
+
+# Set into an entirely new class. Overload a bit to try to cause problems.
+$value = "B"x4096;
+for (1 .. $keycount * 4) {
+ print $sock "set jfoo$_ 0 0 4096 noreply\r\n$value\r\n";
+}
+
+{
+ my $stats = mem_stats($sock);
+ cmp_ok($stats->{curr_items}, '>', 10000, "stored at least 10000 4k items");
+ is($stats->{slab_global_page_pool}, 0, "drained the global page pool");
+}
+
+{
+ my $stats = mem_stats($sock, "slabs");
+ is($stats->{total_malloced}, 62914560, "total_malloced is same after re-assignment");
+}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/stats-conns.t
^
|
@@ -27,9 +27,14 @@
}
like($stats, qr/STAT \d+:addr /);
-$stats =~ m/STAT (\d+):addr unix:(\/[^\r\n]*)/g;
+$stats =~ m/STAT (\d+):addr unix:(.*[^\r\n])/g;
my $listen_fd = $1;
-is($2, $filename, "unix domain socket path reported correctly");
+my $socket_path = $2;
+# getsockname(2) doesn't return socket path on GNU/Hurd (and maybe others)
+SKIP: {
+ skip "socket path checking on GNU kernel", 1 if ($^O eq 'gnu');
+ is($socket_path, $filename, "unix domain socket path reported correctly");
+};
$stats =~ m/STAT (\d+):state conn_listening\r\n/g;
is($1, $listen_fd, "listen socket fd reported correctly");
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/stats.t
^
|
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use strict;
-use Test::More tests => 97;
+use Test::More tests => 108;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -17,52 +17,6 @@
## STAT time 1259170891
## STAT version 1.4.3
## STAT libevent 1.4.13-stable.
-## STAT pointer_size 32
-## STAT rusage_user 0.001198
-## STAT rusage_system 0.003523
-## STAT curr_connections 10
-## STAT total_connections 11
-## STAT connection_structures 11
-## STAT reserved_fds 20
-## STAT cmd_get 0
-## STAT cmd_set 0
-## STAT cmd_flush 0
-## STAT cmd_touch 0
-## STAT get_hits 0
-## STAT get_misses 0
-## STAT delete_misses 0
-## STAT delete_hits 0
-## STAT incr_misses 0
-## STAT incr_hits 0
-## STAT decr_misses 0
-## STAT decr_hits 0
-## STAT cas_misses 0
-## STAT cas_hits 0
-## STAT cas_badval 0
-## STAT touch_hits 0
-## STAT touch_misses 0
-## STAT auth_cmds 0
-## STAT auth_unknowns 0
-## STAT bytes_read 7
-## STAT bytes_written 0
-## STAT limit_maxbytes 67108864
-## STAT accepting_conns 1
-## STAT listen_disabled_num 0
-## STAT threads 4
-## STAT conn_yields 0
-## STAT hash_power_level 16
-## STAT hash_bytes 524288
-## STAT hash_is_expanding 0
-## STAT malloc_fails 0
-## STAT bytes 0
-## STAT curr_items 0
-## STAT total_items 0
-## STAT expired_unfetched 0
-## STAT evicted_unfetched 0
-## STAT evictions 0
-## STAT reclaimed 0
-## STAT crawler_reclaimed 0
-## STAT lrutail_reflocked 0
## see doc/protocol.txt for others
# note that auth stats are tested in auth specfic tests
@@ -70,12 +24,12 @@
my $stats = mem_stats($sock);
# Test number of keys
-is(scalar(keys(%$stats)), 52, "52 stats values");
+is(scalar(keys(%$stats)), 59, "59 stats values");
# Test initial state
-foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses
- bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits
- decr_misses listen_disabled_num lrutail_reflocked)) {
+foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses get_expired
+ bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits get_flushed
+ decr_misses listen_disabled_num lrutail_reflocked time_in_listen_disabled_us)) {
is($stats->{$key}, 0, "initial $key is zero");
}
is($stats->{accepting_conns}, 1, "initial accepting_conns is one");
@@ -189,6 +143,8 @@
is(0, $stats->{'cmd_set'});
is(0, $stats->{'get_hits'});
is(0, $stats->{'get_misses'});
+is(0, $stats->{'get_expired'});
+is(0, $stats->{'get_flushed'});
is(0, $stats->{'delete_misses'});
is(0, $stats->{'delete_hits'});
is(0, $stats->{'incr_misses'});
@@ -202,8 +158,21 @@
is(0, $stats->{'reclaimed'});
is(0, $stats->{'lrutail_reflocked'});
+# item expired
+print $sock "set should_expire 0 2678400 6\r\nfooval\r\n"; #2678400 = 31 days in seconds
+is(scalar <$sock>, "STORED\r\n", "set item to expire");
+print $sock "get should_expire\r\n";
+is(scalar <$sock>, "END\r\n", "item not returned");
+my $stats = mem_stats($sock);
+is(1, $stats->{'get_expired'}, "get_expired counter is 1");
+
+print $sock "set should_be_flushed 0 0 6\r\nbooval\r\n";
+is(scalar <$sock>, "STORED\r\n", "set item to flush");
print $sock "flush_all\r\n";
is(scalar <$sock>, "OK\r\n", "flushed");
+print $sock "get should_be_flushed\r\n";
+is(scalar <$sock>, "END\r\n", "flushed item not returned");
my $stats = mem_stats($sock);
is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1");
+is($stats->{get_flushed}, 1, "after flush and a get, get_flushed is 1");
|
[-]
[+]
|
Added |
memcached-1.4.36.tar.bz2/t/watcher.t
^
|
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+# Networked logging tests.
+
+use strict;
+use warnings;
+use Socket qw/SO_RCVBUF/;
+
+use Test::More tests => 8;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+my $server = new_memcached('-m 60 -o watcher_logbuf_size=8');
+my $client = $server->sock;
+my $watcher = $server->new_sock;
+
+# This doesn't return anything.
+print $watcher "watch\n";
+my $res = <$watcher>;
+is($res, "OK\r\n", "watcher enabled");
+
+print $client "get foo\n";
+$res = <$client>;
+is($res, "END\r\n", "basic get works");
+my $spacer = "X"x180;
+
+# This is a flaky test... depends on buffer sizes. Could either have memc
+# shrink the watcher buffer, or loop this and keep doubling until we get some
+# skipped values.
+for (1 .. 80000) {
+ print $client "get foo$_$spacer\n";
+ $res = <$client>;
+}
+
+# Let the logger thread catch up before we start reading.
+sleep 1;
+my $do_fetch = 0;
+#print STDERR "RESULT: $res\n";
+while (my $log = <$watcher>) {
+ # The "skipped" line won't actually print until some space frees up in the
+ # buffer, so we need to occasionally cause new lines to generate.
+ if (($do_fetch++ % 100) == 0) {
+ print $client "get foo\n";
+ $res = <$client>;
+ }
+ next unless $log =~ m/skipped/;
+ like($log, qr/skipped=/, "skipped some lines");
+ # This should unjam more of the text.
+ print $client "get foob\n";
+ $res = <$client>;
+ last;
+}
+$res = <$watcher>;
+like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_get/, "saw a real log line after a skip");
+
+# test combined logs
+# fill to evictions, then enable watcher, set again, and look for both lines
+
+{
+ my $value = "B"x11000;
+ my $keycount = 8000;
+
+ for (1 .. $keycount) {
+ print $client "set n,foo$_ 0 0 11000 noreply\r\n$value\r\n";
+ }
+
+ $watcher = $server->new_sock;
+ print $watcher "watch mutations evictions\n";
+ $res = <$watcher>;
+ is($res, "OK\r\n", "new watcher enabled");
+ my $watcher2 = $server->new_sock;
+ print $watcher2 "watch evictions\n";
+ $res = <$watcher2>;
+ is($res, "OK\r\n", "evictions watcher enabled");
+
+ print $client "set bfoo 0 0 11000 noreply\r\n$value\r\n";
+ my $found_log = 0;
+ my $found_ev = 0;
+ while (my $log = <$watcher>) {
+ $found_log = 1 if ($log =~ m/type=item_store/);
+ $found_ev = 1 if ($log =~ m/type=eviction/);
+ last if ($found_log && $found_ev);
+ }
+ is($found_log, 1, "found rawcmd log entry");
+ is($found_ev, 1, "found eviction log entry");
+}
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/t/whitespace.t
^
|
@@ -8,6 +8,7 @@
my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
push(@exempted, glob("doc/*.xml"));
+ push(@exempted, glob("doc/*.full"));
push(@exempted, glob("doc/xml2rfc/*.xsl"));
push(@exempted, glob("m4/*backport*m4"));
push(@exempted, glob("*.orig"));
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/thread.c
^
|
@@ -7,7 +7,6 @@
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
-#include <errno.h>
#include <string.h>
#include <pthread.h>
@@ -25,6 +24,7 @@
int event_flags;
int read_buffer_size;
enum network_transport transport;
+ conn *c;
CQ_ITEM *next;
};
@@ -63,8 +63,6 @@
#define hashsize(n) ((unsigned long int)1<<(n))
#define hashmask(n) (hashsize(n)-1)
-static LIBEVENT_DISPATCHER_THREAD dispatcher_thread;
-
/*
* Each libevent instance has a wakeup pipe, which other threads
* can use to signal that they've put a new connection on its queue.
@@ -81,41 +79,11 @@
static void thread_libevent_process(int fd, short which, void *arg);
-unsigned short refcount_incr(unsigned short *refcount) {
-#ifdef HAVE_GCC_ATOMICS
- return __sync_add_and_fetch(refcount, 1);
-#elif defined(__sun)
- return atomic_inc_ushort_nv(refcount);
-#else
- unsigned short res;
- mutex_lock(&atomics_mutex);
- (*refcount)++;
- res = *refcount;
- mutex_unlock(&atomics_mutex);
- return res;
-#endif
-}
-
-unsigned short refcount_decr(unsigned short *refcount) {
-#ifdef HAVE_GCC_ATOMICS
- return __sync_sub_and_fetch(refcount, 1);
-#elif defined(__sun)
- return atomic_dec_ushort_nv(refcount);
-#else
- unsigned short res;
- mutex_lock(&atomics_mutex);
- (*refcount)--;
- res = *refcount;
- mutex_unlock(&atomics_mutex);
- return res;
-#endif
-}
-
/* item_lock() must be held for an item before any modifications to either its
* associated hash bucket, or the structure itself.
* LRU modifications must hold the item lock, and the LRU lock.
* LRU's accessing items must item_trylock() before modifying an item.
- * Items accessable from an LRU must not be freed or modified
+ * Items accessible from an LRU must not be freed or modified
* without first locking and removing from the LRU.
*/
@@ -301,13 +269,12 @@
* Creates a worker thread.
*/
static void create_worker(void *(*func)(void *), void *arg) {
- pthread_t thread;
pthread_attr_t attr;
int ret;
pthread_attr_init(&attr);
- if ((ret = pthread_create(&thread, &attr, func, arg)) != 0) {
+ if ((ret = pthread_create(&((LIBEVENT_THREAD*)arg)->thread_id, &attr, func, arg)) != 0) {
fprintf(stderr, "Can't create thread: %s\n",
strerror(ret));
exit(1);
@@ -373,6 +340,11 @@
/* Any per-thread setup can happen here; memcached_thread_init() will block until
* all threads have finished initializing.
*/
+ me->l = logger_create();
+ me->lru_bump_buf = item_lru_bump_buf_create();
+ if (me->l == NULL || me->lru_bump_buf == NULL) {
+ abort();
+ }
register_thread_initialized();
@@ -389,38 +361,59 @@
LIBEVENT_THREAD *me = arg;
CQ_ITEM *item;
char buf[1];
+ unsigned int timeout_fd;
- if (read(fd, buf, 1) != 1)
+ if (read(fd, buf, 1) != 1) {
if (settings.verbose > 0)
fprintf(stderr, "Can't read from libevent pipe\n");
+ return;
+ }
switch (buf[0]) {
case 'c':
- item = cq_pop(me->new_conn_queue);
+ item = cq_pop(me->new_conn_queue);
- if (NULL != item) {
- conn *c = conn_new(item->sfd, item->init_state, item->event_flags,
- item->read_buffer_size, item->transport, me->base);
- if (c == NULL) {
- if (IS_UDP(item->transport)) {
- fprintf(stderr, "Can't listen for events on UDP socket\n");
- exit(1);
- } else {
- if (settings.verbose > 0) {
- fprintf(stderr, "Can't listen for events on fd %d\n",
- item->sfd);
+ if (NULL != item) {
+ conn *c = conn_new(item->sfd, item->init_state, item->event_flags,
+ item->read_buffer_size, item->transport,
+ me->base);
+ if (c == NULL) {
+ if (IS_UDP(item->transport)) {
+ fprintf(stderr, "Can't listen for events on UDP socket\n");
+ exit(1);
+ } else {
+ if (settings.verbose > 0) {
+ fprintf(stderr, "Can't listen for events on fd %d\n",
+ item->sfd);
+ }
+ close(item->sfd);
}
- close(item->sfd);
+ } else {
+ c->thread = me;
}
- } else {
- c->thread = me;
+ cqi_free(item);
+ }
+ break;
+ case 'r':
+ item = cq_pop(me->new_conn_queue);
+
+ if (NULL != item) {
+ conn_worker_readd(item->c);
+ cqi_free(item);
}
- cqi_free(item);
- }
break;
/* we were told to pause and report in */
case 'p':
- register_thread_initialized();
+ register_thread_initialized();
+ break;
+ /* a client socket timed out */
+ case 't':
+ if (read(fd, &timeout_fd, sizeof(timeout_fd)) != sizeof(timeout_fd)) {
+ if (settings.verbose > 0)
+ fprintf(stderr, "Can't read timeout fd from libevent pipe\n");
+ return;
+ }
+ conn_close_idle(conns[timeout_fd]);
break;
}
}
@@ -466,10 +459,43 @@
}
/*
- * Returns true if this is the thread that listens for new TCP connections.
+ * Re-dispatches a connection back to the original thread. Can be called from
+ * any side thread borrowing a connection.
*/
-int is_listen_thread() {
- return pthread_self() == dispatcher_thread.thread_id;
+void redispatch_conn(conn *c) {
+ CQ_ITEM *item = cqi_new();
+ char buf[1];
+ if (item == NULL) {
+ /* Can't cleanly redispatch connection. close it forcefully. */
+ c->state = conn_closed;
+ close(c->sfd);
+ return;
+ }
+ LIBEVENT_THREAD *thread = c->thread;
+ item->sfd = c->sfd;
+ item->init_state = conn_new_cmd;
+ item->c = c;
+
+ cq_push(thread->new_conn_queue, item);
+
+ buf[0] = 'r';
+ if (write(thread->notify_send_fd, buf, 1) != 1) {
+ perror("Writing to thread notify pipe");
+ }
+}
+
+/* This misses the allow_new_conns flag :( */
+void sidethread_conn_close(conn *c) {
+ c->state = conn_closed;
+ if (settings.verbose > 1)
+ fprintf(stderr, "<%d connection closed from side thread.\n", c->sfd);
+ close(c->sfd);
+
+ STATS_LOCK();
+ stats_state.curr_conns--;
+ STATS_UNLOCK();
+
+ return;
}
/********************************* ITEM ACCESS *******************************/
@@ -480,7 +506,7 @@
item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes) {
item *it;
/* do_item_alloc handles its own locks */
- it = do_item_alloc(key, nkey, flags, exptime, nbytes, 0);
+ it = do_item_alloc(key, nkey, flags, exptime, nbytes);
return it;
}
@@ -488,22 +514,22 @@
* Returns an item if it hasn't been marked as expired,
* lazy-expiring as needed.
*/
-item *item_get(const char *key, const size_t nkey) {
+item *item_get(const char *key, const size_t nkey, conn *c, const bool do_update) {
item *it;
uint32_t hv;
hv = hash(key, nkey);
item_lock(hv);
- it = do_item_get(key, nkey, hv);
+ it = do_item_get(key, nkey, hv, c, do_update);
item_unlock(hv);
return it;
}
-item *item_touch(const char *key, size_t nkey, uint32_t exptime) {
+item *item_touch(const char *key, size_t nkey, uint32_t exptime, conn *c) {
item *it;
uint32_t hv;
hv = hash(key, nkey);
item_lock(hv);
- it = do_item_touch(key, nkey, exptime, hv);
+ it = do_item_touch(key, nkey, exptime, hv, c);
item_unlock(hv);
return it;
}
@@ -556,18 +582,6 @@
}
/*
- * Moves an item to the back of the LRU queue.
- */
-void item_update(item *item) {
- uint32_t hv;
- hv = hash(ITEM_key(item), item->nkey);
-
- item_lock(hv);
- do_item_update(item);
- item_unlock(hv);
-}
-
-/*
* Does arithmetic on a numeric item value.
*/
enum delta_result_type add_delta(conn *c, const char *key,
@@ -609,35 +623,15 @@
}
void threadlocal_stats_reset(void) {
- int ii, sid;
+ int ii;
for (ii = 0; ii < settings.num_threads; ++ii) {
pthread_mutex_lock(&threads[ii].stats.mutex);
+#define X(name) threads[ii].stats.name = 0;
+ THREAD_STATS_FIELDS
+#undef X
- threads[ii].stats.get_cmds = 0;
- threads[ii].stats.get_misses = 0;
- threads[ii].stats.touch_cmds = 0;
- threads[ii].stats.touch_misses = 0;
- threads[ii].stats.delete_misses = 0;
- threads[ii].stats.incr_misses = 0;
- threads[ii].stats.decr_misses = 0;
- threads[ii].stats.cas_misses = 0;
- threads[ii].stats.bytes_read = 0;
- threads[ii].stats.bytes_written = 0;
- threads[ii].stats.flush_cmds = 0;
- threads[ii].stats.conn_yields = 0;
- threads[ii].stats.auth_cmds = 0;
- threads[ii].stats.auth_errors = 0;
-
- for(sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- threads[ii].stats.slab_stats[sid].set_cmds = 0;
- threads[ii].stats.slab_stats[sid].get_hits = 0;
- threads[ii].stats.slab_stats[sid].touch_hits = 0;
- threads[ii].stats.slab_stats[sid].delete_hits = 0;
- threads[ii].stats.slab_stats[sid].incr_hits = 0;
- threads[ii].stats.slab_stats[sid].decr_hits = 0;
- threads[ii].stats.slab_stats[sid].cas_hits = 0;
- threads[ii].stats.slab_stats[sid].cas_badval = 0;
- }
+ memset(&threads[ii].stats.slab_stats, 0,
+ sizeof(threads[ii].stats.slab_stats));
pthread_mutex_unlock(&threads[ii].stats.mutex);
}
@@ -652,39 +646,15 @@
for (ii = 0; ii < settings.num_threads; ++ii) {
pthread_mutex_lock(&threads[ii].stats.mutex);
-
- stats->get_cmds += threads[ii].stats.get_cmds;
- stats->get_misses += threads[ii].stats.get_misses;
- stats->touch_cmds += threads[ii].stats.touch_cmds;
- stats->touch_misses += threads[ii].stats.touch_misses;
- stats->delete_misses += threads[ii].stats.delete_misses;
- stats->decr_misses += threads[ii].stats.decr_misses;
- stats->incr_misses += threads[ii].stats.incr_misses;
- stats->cas_misses += threads[ii].stats.cas_misses;
- stats->bytes_read += threads[ii].stats.bytes_read;
- stats->bytes_written += threads[ii].stats.bytes_written;
- stats->flush_cmds += threads[ii].stats.flush_cmds;
- stats->conn_yields += threads[ii].stats.conn_yields;
- stats->auth_cmds += threads[ii].stats.auth_cmds;
- stats->auth_errors += threads[ii].stats.auth_errors;
+#define X(name) stats->name += threads[ii].stats.name;
+ THREAD_STATS_FIELDS
+#undef X
for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- stats->slab_stats[sid].set_cmds +=
- threads[ii].stats.slab_stats[sid].set_cmds;
- stats->slab_stats[sid].get_hits +=
- threads[ii].stats.slab_stats[sid].get_hits;
- stats->slab_stats[sid].touch_hits +=
- threads[ii].stats.slab_stats[sid].touch_hits;
- stats->slab_stats[sid].delete_hits +=
- threads[ii].stats.slab_stats[sid].delete_hits;
- stats->slab_stats[sid].decr_hits +=
- threads[ii].stats.slab_stats[sid].decr_hits;
- stats->slab_stats[sid].incr_hits +=
- threads[ii].stats.slab_stats[sid].incr_hits;
- stats->slab_stats[sid].cas_hits +=
- threads[ii].stats.slab_stats[sid].cas_hits;
- stats->slab_stats[sid].cas_badval +=
- threads[ii].stats.slab_stats[sid].cas_badval;
+#define X(name) stats->slab_stats[sid].name += \
+ threads[ii].stats.slab_stats[sid].name;
+ SLAB_STATS_FIELDS
+#undef X
}
pthread_mutex_unlock(&threads[ii].stats.mutex);
@@ -694,24 +664,12 @@
void slab_stats_aggregate(struct thread_stats *stats, struct slab_stats *out) {
int sid;
- out->set_cmds = 0;
- out->get_hits = 0;
- out->touch_hits = 0;
- out->delete_hits = 0;
- out->incr_hits = 0;
- out->decr_hits = 0;
- out->cas_hits = 0;
- out->cas_badval = 0;
+ memset(out, 0, sizeof(*out));
for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- out->set_cmds += stats->slab_stats[sid].set_cmds;
- out->get_hits += stats->slab_stats[sid].get_hits;
- out->touch_hits += stats->slab_stats[sid].touch_hits;
- out->delete_hits += stats->slab_stats[sid].delete_hits;
- out->decr_hits += stats->slab_stats[sid].decr_hits;
- out->incr_hits += stats->slab_stats[sid].incr_hits;
- out->cas_hits += stats->slab_stats[sid].cas_hits;
- out->cas_badval += stats->slab_stats[sid].cas_badval;
+#define X(name) out->name += stats->slab_stats[sid].name;
+ SLAB_STATS_FIELDS
+#undef X
}
}
@@ -719,9 +677,8 @@
* Initializes the thread subsystem, creating various worker threads.
*
* nthreads Number of worker event handler threads to spawn
- * main_base Event base for main thread
*/
-void memcached_thread_init(int nthreads, struct event_base *main_base) {
+void memcached_thread_init(int nthreads) {
int i;
int power;
@@ -743,9 +700,13 @@
power = 11;
} else if (nthreads < 5) {
power = 12;
- } else {
- /* 8192 buckets, and central locks don't scale much past 5 threads */
+ } else if (nthreads <= 10) {
power = 13;
+ } else if (nthreads <= 20) {
+ power = 14;
+ } else {
+ /* 32k buckets. just under the hashpower default. */
+ power = 15;
}
if (power >= hashpower) {
@@ -773,9 +734,6 @@
exit(1);
}
- dispatcher_thread.base = main_base;
- dispatcher_thread.thread_id = pthread_self();
-
for (i = 0; i < nthreads; i++) {
int fds[2];
if (pipe(fds)) {
@@ -788,7 +746,7 @@
setup_thread(&threads[i]);
/* Reserve three fds for the libevent base, and two for the pipe */
- stats.reserved_fds += 5;
+ stats_state.reserved_fds += 5;
}
/* Create threads after we've done all the libevent setup. */
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/util.c
^
|
@@ -8,6 +8,41 @@
#include "memcached.h"
+static char *uriencode_map[256];
+static char uriencode_str[768];
+
+void uriencode_init(void) {
+ int x;
+ char *str = uriencode_str;
+ for (x = 0; x < 256; x++) {
+ if (isalnum(x) || x == '-' || x == '.' || x == '_' || x == '~') {
+ uriencode_map[x] = NULL;
+ } else {
+ snprintf(str, 4, "%%%02hhX", (unsigned char)x);
+ uriencode_map[x] = str;
+ str += 3; /* lobbing off the \0 is fine */
+ }
+ }
+}
+
+bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen) {
+ int x;
+ size_t d = 0;
+ for (x = 0; x < srclen; x++) {
+ if (d + 4 >= dstlen)
+ return false;
+ if (uriencode_map[(unsigned char) src[x]] != NULL) {
+ memcpy(&dst[d], uriencode_map[(unsigned char) src[x]], 3);
+ d += 3;
+ } else {
+ dst[d] = src[x];
+ d++;
+ }
+ }
+ dst[d] = '\0';
+ return true;
+}
+
/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
#define xisspace(c) isspace((unsigned char)c)
@@ -97,6 +132,23 @@
return true;
}
return false;
+}
+
+bool safe_strtod(const char *str, double *out) {
+ assert(out != NULL);
+ errno = 0;
+ *out = 0;
+ char *endptr;
+ double d = strtod(str, &endptr);
+ if ((errno == ERANGE) || (str == endptr)) {
+ return false;
+ }
+
+ if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
+ *out = d;
+ return true;
+ }
+ return false;
}
void vperror(const char *fmt, ...) {
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/util.h
^
|
@@ -1,3 +1,7 @@
+/* fast-enough functions for uriencoding strings. */
+void uriencode_init(void);
+bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen);
+
/*
* Wrappers around strtoull/strtoll that are safer and easier to
* use. For tests and assumptions, see internal_tests.c.
@@ -11,6 +15,7 @@
bool safe_strtoll(const char *str, int64_t *out);
bool safe_strtoul(const char *str, uint32_t *out);
bool safe_strtol(const char *str, int32_t *out);
+bool safe_strtod(const char *str, double *out);
#ifndef HAVE_HTONLL
extern uint64_t htonll(uint64_t);
|
[-]
[+]
|
Changed |
memcached-1.4.36.tar.bz2/version.m4
^
|
@@ -1 +1 @@
-m4_define([VERSION_NUMBER], [1.4.24])
+m4_define([VERSION_NUMBER], [1.4.36])
|