├── .gitignore ├── INSTALL ├── README.md ├── conf └── libshmcache.conf ├── doc └── intro_in_chinese.pdf ├── java ├── README ├── jni │ ├── Makefile.in │ ├── make.sh │ ├── org_csource_shmcache_ShmCache.c │ └── org_csource_shmcache_ShmCache.h ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── csource │ └── shmcache │ ├── ShmCache.java │ ├── ShmCachePropertyConfigAnnotation.java │ └── ShmCachePropertyConfigurer.java ├── libshmcache.spec ├── php-shmcache ├── bench │ ├── redis.php │ └── shmcache.php ├── config.m4 ├── php-shmcache.spec.in ├── php_shmcache.c ├── php_shmcache.h ├── serializer │ ├── igbinary.c │ ├── msgpack.c │ ├── php.c │ ├── shmcache_serializer.c │ └── shmcache_serializer.h ├── shmcache.ini └── test.php ├── src ├── Makefile ├── shm_hashtable.c ├── shm_hashtable.h ├── shm_list.h ├── shm_lock.c ├── shm_lock.h ├── shm_object_pool.c ├── shm_object_pool.h ├── shm_op_wrapper.c ├── shm_op_wrapper.h ├── shm_striping_allocator.c ├── shm_striping_allocator.h ├── shm_value_allocator.c ├── shm_value_allocator.h ├── shmcache.c ├── shmcache.h ├── shmcache_ini_annotation.c ├── shmcache_ini_annotation.h ├── shmcache_types.h ├── shmopt.c ├── shmopt.h ├── tests │ ├── Makefile │ └── tests.c └── tools │ ├── Makefile │ ├── shmcache_delete.c │ ├── shmcache_dump.c │ ├── shmcache_get.c │ ├── shmcache_remove_all.c │ ├── shmcache_set.c │ └── shmcache_stats.c └── tsar-shmcache-module ├── Makefile ├── mod_shmcache.c ├── mod_shmcache.conf └── tsar-shmcache-module.spec.in /.gitignore: -------------------------------------------------------------------------------- 1 | # Makefile.in 2 | java/jni/Makefile 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dSYM 21 | *.dll 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.out 31 | java/target 32 | src/tools/shmcache_delete 33 | src/tools/shmcache_dump 34 | src/tools/shmcache_get 35 | src/tools/shmcache_remove_all 36 | src/tools/shmcache_set 37 | src/tools/shmcache_stats 38 | 39 | # other 40 | *.swp 41 | *.swo 42 | php-shmcache/.deps 43 | php-shmcache/.libs/ 44 | php-shmcache/Makefile 45 | php-shmcache/Makefile.fragments 46 | php-shmcache/Makefile.global 47 | php-shmcache/Makefile.objects 48 | php-shmcache/acinclude.m4 49 | php-shmcache/aclocal.m4 50 | php-shmcache/autom4te.cache/ 51 | php-shmcache/build/ 52 | php-shmcache/config.guess 53 | php-shmcache/config.h 54 | php-shmcache/config.h.in 55 | php-shmcache/config.log 56 | php-shmcache/config.nice 57 | php-shmcache/config.status 58 | php-shmcache/config.sub 59 | php-shmcache/configure 60 | php-shmcache/configure.ac 61 | php-shmcache/install-sh 62 | php-shmcache/libtool 63 | php-shmcache/ltmain.sh 64 | php-shmcache/missing 65 | php-shmcache/mkinstalldirs 66 | php-shmcache/run-tests.php 67 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Copy right 2016 Happy Fish / YuQing 2 | 3 | libshmcache may be copied or modified under the terms of BSD License. 4 | Please visit the libshmcache Home Page for more detail. 5 | website: https://github.com/happyfish100/libshmcache 6 | 7 | # step 1. compile && install libfastcommon 8 | git clone https://github.com/happyfish100/libfastcommon 9 | cd libfastcommon && ./make.sh && sudo ./make.sh install 10 | 11 | # step 2. compile && install libshmcache 12 | git clone https://github.com/happyfish100/libshmcache 13 | cd libshmcache/src && make && sudo make install 14 | cd tools && make && sudo make install 15 | cd ../.. 16 | 17 | # step 3. [optional] compile && install libshmcache php extension 18 | cd php-shmcache 19 | phpize 20 | ./configure 21 | make && sudo make install 22 | cd .. 23 | 24 | # step 4. [optional] compile && install java JNI library 25 | cd java/jni 26 | ./make.sh && sudo make install 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016 Happy Fish / YuQing 2 | 3 | libshmcache may be copied or modified under the terms of BSD License. 4 | 5 | libshmcache is a local share memory cache for multi processes. 6 | it is a high performance library because read mechanism is lockless. 7 | libshmcache is 100+ times faster than a remote interface such as redis. 8 | 9 | this project contains C library, PHP extension and Java JNI wrapper. 10 | 11 | Its high performance features include: 12 | * pthread mutex lock on write, read is lockless 13 | * use hash table for quickly setting, getting and deletion 14 | * use object pool (FIFO queue) for hash/KV entry allocation 15 | * use striping/block buffer allocator. in a memory striping, 16 | allocate value buffer in order. just only decrease allocator's 17 | used size when free the value buffer. recycle the whole memory 18 | striping/allocator when it's used size reach to zero after memory free up. 19 | * use FIFO elimination algorithm instead of LRU 20 | 21 | stable features are: 22 | * deadlock detection and auto unlock that caused by other crushed process 23 | * check some key fields for consistency when init, old share memories 24 | should be cleaned and reinit when the memory related parameters changed 25 | * add sleep time to avoid other processes reading dirty data when 26 | recycle more than one valid (not expired) hash/KV entries 27 | 28 | other features are: 29 | * support both related processes (such as parent process and subprocesses) 30 | and unrelated processes (such as php-fpm and php CLI, two php CLI processes, etc.) 31 | * incrementally allocate value buffer segment as need, this is controlled 32 | by config parameter: segment_size 33 | * provide abundant stats info: counters for set, get and delete, 34 | memory recycle stats, lock stats etc. 35 | * support atomic increment 36 | * PHP extension support multiple serializers: igbinary, msgpack, php. 37 | these serializers can coexist in a share memory. 38 | 39 | utility commands in directory: src/tools, in /usr/bin/ after make && make install 40 | * shmcache_set: set a key 41 | * shmcache_get: get a key 42 | * shmcache_delete: delete a key 43 | * shmcache_remove_all: remove the share memory 44 | * shmcache_stats: show share memory statistics 45 | 46 | * Note: the key size can not be longer than 64 bytes 47 | 48 | libshmcache PHP extension is supplied in the directory: php-shmcache, support PHP 5 and PHP 7 49 | 50 | ShmCache::__construct(string $config_filename[, long $serializer = 51 | ShmCache::SERIALIZER_IGBINARY]) 52 | * @param config_filename: the config filename as conf/libshmcache.conf 53 | * @param serializer: the serializer type 54 |
 55 |       ShmCache::SERIALIZER_IGBINARY for igbinary, the default serializer
 56 |       ShmCache::SERIALIZER_MSGPACK for msgpack
 57 |       ShmCache::SERIALIZER_PHP for php serializer
 58 |     
59 | * @throws ShmCacheException if the serializer not enabled 60 | * @example: $cache = new ShmCache("/etc/libshmcache.conf"); 61 | * @note: igbinary and msgpack php extensions must be enabled before use them 62 |
 63 |     check method:
 64 |     php -m | grep igbinary
 65 |     php -m | grep msgpack
 66 |     
67 | 68 | boolean ShmCache::set(string $key, mixed $value, long $ttl) 69 | * @param key: the key, must be a string variable 70 | * @param value: the value, any php variable 71 | * @param ttl: timeout / expire in seconds, such as 600 for ten minutes, 72 | ShmCache::NEVER_EXPIRED for never expired 73 | * @return true for success, false for fail 74 | * @throws ShmCacheException if $value is false 75 | * @example: $cache->set($key, $value, 300); 76 | 77 | long ShmCache::incr(string $key, long $increment, long $ttl) 78 | * @desc: atomic increment 79 | * @param key: the key, must be a string variable 80 | * @param increment: the increment integer, can be negative, such as -1 81 | * @param ttl: timeout / expire in seconds, such as 600 for ten minutes, 82 | * @return the value after increase, false for fail 83 | 84 | mixed ShmCache::get(string $key[, boolean $returnExpired = false]) 85 | * @param key: the key, must be a string variable 86 | * @param returnExpired: if return expired key / value 87 | * @return mixed value for success, false for key not exist or expired 88 | * @example: $value = $cache->get($key); 89 | 90 | long ShmCache::getExpires(string $key[, boolean $returnExpired = false]) 91 | * @desc: get expires time as unix timestamp 92 | * @param key: the key, must be a string variable 93 | * @param returnExpired: if return expired key / value 94 | * @return expires timestamps such as 1483952635, 0 for never expired, false for not exist 95 | * @example: $value = $cache->getExpires($key); 96 | 97 | boolean ShmCache::setExpires(string $key, long $expires) 98 | * @param key: the key, must be a string variable 99 | * @param expires: expires timestamp (unix timestamp eg. 1591347245) 100 | ShmCache::NEVER_EXPIRED for never expired 101 | * @return true for success, false for key not exist or expired or other error 102 | * @throws ShmCacheException if $expires is invalid 103 | * @example: $cache->setExpires($key, 1591347245); 104 | 105 | boolean ShmCache::setTTL(string $key, long $ttl) 106 | * @param key: the key, must be a string variable 107 | * @param ttl: timeout / expire in seconds, such as 600 for ten minutes, 108 | ShmCache::NEVER_EXPIRED for never expired 109 | * @return true for success, false for key not exist or expired or other error 110 | * @throws ShmCacheException if $ttl is invalid 111 | * @example: $cache->setTTL($key, 300); 112 | 113 | boolean ShmCache::delete(string $key) 114 | * @param key: the key, must be a string variable 115 | * @return true for success, false for fail 116 | * @example: $cache->delete($key); 117 | 118 | array ShmCache::stats() 119 | * @return stats array 120 | * @example: echo json_encode($cache->stats(), JSON_PRETTY_PRINT); 121 | 122 | boolean ShmCache::clear() 123 | * @desc: clear hashtable to empty. use this function carefully because it will remove all keys in cache 124 | * @return true for success, false for fail 125 | -------------------------------------------------------------------------------- /conf/libshmcache.conf: -------------------------------------------------------------------------------- 1 | 2 | # shared memory type, shm or mmap 3 | # shm for SystemV pure shared memory 4 | # mmap for POSIX shared memory based file 5 | # default value is mmap 6 | # Note: when type is shm, the shm limit is too small in FreeBSD and MacOS, 7 | # you should increase following kernel parameters: 8 | # kern.sysv.shmmax 9 | # kern.sysv.shmall 10 | # kern.sysv.shmseg 11 | # you can refer to http://www.unidata.ucar.edu/software/mcidas/2008/users_guide/workstation.html 12 | type = mmap 13 | 14 | # the lock filename 15 | filename = /tmp/shmcache 16 | 17 | # the memory limit 18 | # the oldest memory will be recycled when this max memory reached 19 | max_memory = 256M 20 | 21 | # the min memory 22 | # default: 0, means do NOT set min memory 23 | min_memory = 0 24 | 25 | # the memory segment size for incremental memory allocation 26 | segment_size = 8M 27 | 28 | # the key number limit 29 | max_key_count = 200000 30 | 31 | # the size limit for one value 32 | max_value_size = 256K 33 | 34 | # the hash function in libfastcommon/src/hash.h 35 | # default: simple_hash 36 | hash_function = simple_hash 37 | 38 | # recycle key number once when reach max keys 39 | # <= 0 means recycle one memory striping 40 | # default: 0 41 | recycle_key_once = 0 42 | 43 | # if recycle valid entries by FIFO 44 | # default: false 45 | recycle_valid_entries = true 46 | 47 | # value allocator policy 48 | # avg. key TTL threshold for recycling memory 49 | # <= 0 for never recycle memory until reach memory limit (max_memory) 50 | # unit: second 51 | # default: 0 52 | value_policy.avg_key_ttl = 5400 53 | 54 | # when the remain memory <= this parameter, discard it 55 | value_policy.discard_memory_size = 128 56 | 57 | # when a value allocator allocate fail times > this parameter, 58 | # means it is almost full, discard it 59 | # default: 5 60 | value_policy.max_fail_times = 5 61 | 62 | # sleep time to avoid other processes read dirty data when 63 | # recycle more than one valid (in TTL / not expired) KV entries 64 | # 0 for never sleep 65 | # unit: microsecond (us) 66 | # default: 0 67 | value_policy.sleep_us_when_recycle_valid_entries = 1000 68 | 69 | # if read (get) within the lock 70 | # default value is false 71 | lock_policy.read_within_lock = false 72 | 73 | # try lock interval in us, must great than zero 74 | # unit: microsecond (us) 75 | # default value is 200 us 76 | lock_policy.trylock_interval_us = 200 77 | 78 | # the interval to detect deadlock caused by the crushed process 79 | # must great than zero 80 | # unit: millisecond (ms) 81 | # default value is 1000 ms 82 | lock_policy.detect_deadlock_interval_ms = 1000 83 | 84 | # standard log level as syslog, case insensitive, value list: 85 | ## emerg for emergency 86 | ## alert 87 | ## crit for critical 88 | ## error 89 | ## warn for warning 90 | ## notice 91 | ## info 92 | ## debug 93 | log_level = info 94 | -------------------------------------------------------------------------------- /doc/intro_in_chinese.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happyfish100/libshmcache/aba5bbfd0ae0ca63cd18204133e477bc62671c88/doc/intro_in_chinese.pdf -------------------------------------------------------------------------------- /java/README: -------------------------------------------------------------------------------- 1 | 2 | the native Java SDK for libshmcache to get config from the share memory. 3 | it can integrate into the spring framework to make development easy. 4 | 5 | * directories and descriptions: 6 | |__ jni C source codes for JNI, use make.sh to compile and install 7 | |__ src java source codes, use mvn to compile and package 8 | |__ main/java/org/csource/shmcache/ 9 | |__ ShmCache.java libshmcache JNI wrapper 10 | |__ ShmCachePropertyConfigAnnotation.java helper class for @config() in property file 11 | |__ ShmCachePropertyConfigurer.java sub-class of spring PropertyPlaceholderConfigurer 12 | 13 | 14 | * use ShmCache config in the property file: 15 | KEY = @config([keyname][,defaultValue]) 16 | 17 | keyname: the key name in the share memory, use KEY when keyname is empty 18 | defaultValue: default value for the missed key, empty string when not set 19 | 20 | for example: 21 | # get the config value of "app.name" from local share memory 22 | app.name = @config() 23 | 24 | 25 | * config example in spring xml file: 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | classpath:conf/sysconfig.properties 34 | 35 | 36 | 37 | 38 | the properties for class ShmCachePropertyConfigurer: 39 | libraryFilename: [required] the full path library filename for libshmcachejni.so 40 | configFilename: [required] the full path config filename for libshmcache.conf 41 | charset: [optional] the charset for config value, default charset is UTF-8 42 | debug: [optional] debug flag, true or 1 for verbose output, default is false 43 | 44 | 45 | 46 | * example for get config use Spring Bean in Serverlet: 47 | ... 48 | import org.csource.shmcache.ShmCachePropertyConfigurer; 49 | import org.springframework.context.ApplicationContext; 50 | import org.springframework.web.context.support.WebApplicationContextUtils; 51 | ... 52 | 53 | String key = "app.name"; 54 | ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); 55 | ShmCachePropertyConfigurer configurer = (ShmCachePropertyConfigurer)ac.getBean("propertySource"); 56 | String value = configurer.getConfig(key); 57 | ... 58 | 59 | 60 | * example for get config in natual Java codes: 61 | package xxx; 62 | import org.csource.shmcache.ShmCache; 63 | ... 64 | 65 | public class XXX { 66 | 67 | static { 68 | ShmCache.setCharset("UTF-8"); 69 | ShmCache.setLibraryFilename("/usr/lib/libshmcachejni.so"); 70 | } 71 | 72 | ... 73 | 74 | public String get(String key) { 75 | ShmCache shmCache = ShmCache.getInstance("/etc/libshmcache.conf"); 76 | return shmCache.getString(key); 77 | } 78 | 79 | ... 80 | } 81 | 82 | -------------------------------------------------------------------------------- /java/jni/Makefile.in: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .c .o .lo 2 | 3 | COMPILE = $(CC) -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -g -O3 4 | PREFIX = $(TARGET_PREFIX) 5 | INC_PATH = -I/usr/local/include $(INCLUDES) 6 | LIB_PATH = -lfastcommon -lshmcache 7 | 8 | SHMCACHE_SHARED_OBJS = org_csource_shmcache_ShmCache.lo 9 | 10 | ALL_OBJS = $(SHMCACHE_SHARED_OBJS) 11 | 12 | ALL_PRGS = 13 | SHARED_LIBS = libshmcachejni.so 14 | ALL_LIBS = $(SHARED_LIBS) 15 | 16 | all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) 17 | libshmcachejni.so: 18 | $(COMPILE) -o $@ $< -shared $(SHMCACHE_SHARED_OBJS) $(LIB_PATH) 19 | .c.o: 20 | $(COMPILE) -c -o $@ $< $(INC_PATH) 21 | .c.lo: 22 | $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) 23 | install: 24 | mkdir -p $(PREFIX)/lib64 25 | mkdir -p $(PREFIX)/lib 26 | install -m 755 $(SHARED_LIBS) $(PREFIX)/lib64 27 | install -m 755 $(SHARED_LIBS) $(PREFIX)/lib 28 | clean: 29 | rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) 30 | 31 | -------------------------------------------------------------------------------- /java/jni/make.sh: -------------------------------------------------------------------------------- 1 | 2 | TARGET_PREFIX=$DESTDIR/usr 3 | LIB_VERSION=lib64 4 | 5 | if [ -f /usr/include/fastcommon/_os_define.h ]; then 6 | OS_BITS=$(fgrep OS_BITS /usr/include/fastcommon/_os_define.h | awk '{print $NF;}') 7 | elif [ -f /usr/local/include/fastcommon/_os_define.h ]; then 8 | OS_BITS=$(fgrep OS_BITS /usr/local/include/fastcommon/_os_define.h | awk '{print $NF;}') 9 | else 10 | OS_BITS=64 11 | fi 12 | 13 | if [ "$OS_BITS" -eq 64 ]; then 14 | LIB_VERSION=lib64 15 | else 16 | LIB_VERSION=lib 17 | fi 18 | 19 | uname=$(uname) 20 | if [ "$uname" = "Linux" ]; then 21 | LIBS= 22 | elif [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then 23 | if [ "$uname" = "Darwin" ]; then 24 | out=$(echo $TARGET_PREFIX | fgrep local) 25 | if [ $? -ne 0 ]; then 26 | TARGET_PREFIX=$TARGET_PREFIX/local 27 | fi 28 | LIB_VERSION=lib 29 | fi 30 | elif [ "$uname" = "SunOS" ]; then 31 | export CC=gcc 32 | elif [ "$uname" = "AIX" ]; then 33 | export CC=gcc 34 | fi 35 | 36 | files=$(locate jni.h) 37 | if [ -z "$files" ]; then 38 | echo "can't locate jni.h, please install java SDK first." 39 | exit 2 40 | fi 41 | 42 | count=$(echo "$files" | wc -l) 43 | if [ $count -eq 1 ]; then 44 | filename=$files 45 | else 46 | i=0 47 | for file in $files; do 48 | i=$(expr $i + 1) 49 | echo "$i. $file" 50 | done 51 | 52 | printf "please input the correct file no.: " 53 | read n 54 | if [ -z "$n" ]; then 55 | echo "invalid file no." 56 | exit 2 57 | fi 58 | 59 | filename=$(echo "$files" | head -n $n | tail -n 1) 60 | fi 61 | 62 | INCLUDES= 63 | path=$(dirname $filename) 64 | for d in $(find $path -type d); do 65 | INCLUDES="$INCLUDES -I$d" 66 | done 67 | 68 | sed_replace() 69 | { 70 | sed_cmd=$1 71 | filename=$2 72 | if [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then 73 | sed -i "" "$sed_cmd" $filename 74 | else 75 | sed -i "$sed_cmd" $filename 76 | fi 77 | } 78 | 79 | cp Makefile.in Makefile 80 | sed_replace "s#\\\$(INCLUDES)#$INCLUDES#g" Makefile 81 | sed_replace "s#\\\$(LIBS)#$LIBS#g" Makefile 82 | sed_replace "s#\\\$(TARGET_PREFIX)#$TARGET_PREFIX#g" Makefile 83 | sed_replace "s#\\\$(LIB_VERSION)#$LIB_VERSION#g" Makefile 84 | make $1 $2 $3 85 | 86 | if [ "$1" = "clean" ]; then 87 | /bin/rm -f Makefile 88 | fi 89 | 90 | -------------------------------------------------------------------------------- /java/jni/org_csource_shmcache_ShmCache.c: -------------------------------------------------------------------------------- 1 | #include "fastcommon/logger.h" 2 | #include "shmcache/shmcache.h" 3 | #include "org_csource_shmcache_ShmCache.h" 4 | 5 | static void throws_exception(JNIEnv *env, const char *message) 6 | { 7 | jclass exClass; 8 | exClass = (*env)->FindClass(env, "java/lang/Exception"); 9 | if (exClass == NULL) { 10 | return; 11 | } 12 | (*env)->ThrowNew(env, exClass, message); 13 | } 14 | 15 | jlong JNICALL Java_org_csource_shmcache_ShmCache_doInit 16 | (JNIEnv *env, jobject obj, jstring config_filename) 17 | { 18 | 19 | struct shmcache_context *context; 20 | jboolean *isCopy = NULL; 21 | const char *szFilename; 22 | int result; 23 | 24 | log_init(); 25 | context = (struct shmcache_context *)malloc(sizeof(struct shmcache_context)); 26 | if (context == NULL) { 27 | logError("file: "__FILE__", line: %d, " 28 | "malloc %d bytes fail", __LINE__, 29 | (int)sizeof(struct shmcache_context)); 30 | throws_exception(env, "out of memory"); 31 | return 0; 32 | } 33 | 34 | szFilename = (*env)->GetStringUTFChars(env, config_filename, isCopy); 35 | result = shmcache_init_from_file(context, szFilename); 36 | 37 | (*env)->ReleaseStringUTFChars(env, config_filename, szFilename); 38 | return result == 0 ? (long)context : 0; 39 | } 40 | 41 | void JNICALL Java_org_csource_shmcache_ShmCache_doDestroy 42 | (JNIEnv *env, jobject obj, jlong handler) 43 | { 44 | struct shmcache_context *context; 45 | 46 | context = (struct shmcache_context *)handler; 47 | if (context == NULL) { 48 | logError("file: "__FILE__", line: %d, " 49 | "empty handler", __LINE__); 50 | throws_exception(env, "empty handler"); 51 | return; 52 | } 53 | 54 | shmcache_destroy(context); 55 | free(context); 56 | } 57 | 58 | void JNICALL Java_org_csource_shmcache_ShmCache_doSetVO 59 | (JNIEnv *env, jobject obj, jlong handler, jstring key, jobject vo) 60 | { 61 | struct shmcache_context *context; 62 | struct shmcache_key_info shmcache_key; 63 | struct shmcache_value_info shmcache_value; 64 | jboolean *isCopy = NULL; 65 | jclass value_class; 66 | jmethodID value_mid; 67 | jmethodID options_mid; 68 | jmethodID expires_mid; 69 | jbyteArray value; 70 | int result; 71 | 72 | context = (struct shmcache_context *)handler; 73 | if (context == NULL) { 74 | logError("file: "__FILE__", line: %d, " 75 | "empty handler", __LINE__); 76 | throws_exception(env, "empty handler"); 77 | return; 78 | } 79 | 80 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 81 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 82 | 83 | value_class = (*env)->GetObjectClass(env, vo); 84 | value_mid = (*env)->GetMethodID(env, value_class, "getValue", "()[B"); 85 | options_mid = (*env)->GetMethodID(env, value_class, "getOptions", "()I"); 86 | expires_mid = (*env)->GetMethodID(env, value_class, "getExpires", "()J"); 87 | 88 | value = (*env)->CallObjectMethod(env, vo, value_mid); 89 | shmcache_value.options = (*env)->CallIntMethod(env, vo, options_mid); 90 | shmcache_value.expires = (*env)->CallLongMethod(env, vo, expires_mid) / 1000; 91 | 92 | shmcache_value.data = (char *)((*env)->GetByteArrayElements(env, value, isCopy)); 93 | shmcache_value.length = (*env)->GetArrayLength(env, value); 94 | 95 | result = shmcache_set_ex(context, &shmcache_key, &shmcache_value); 96 | 97 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 98 | (*env)->ReleaseByteArrayElements(env, value, (jbyte *)shmcache_value.data, 0); 99 | if (result != 0) { 100 | throws_exception(env, strerror(result)); 101 | } 102 | } 103 | 104 | void JNICALL Java_org_csource_shmcache_ShmCache_doSet 105 | (JNIEnv *env, jobject obj, jlong handler, jstring key, 106 | jbyteArray value, jint ttl) 107 | { 108 | struct shmcache_context *context; 109 | struct shmcache_key_info shmcache_key; 110 | jboolean *isCopy = NULL; 111 | jbyte *szValue; 112 | jsize value_len; 113 | int result; 114 | 115 | context = (struct shmcache_context *)handler; 116 | if (context == NULL) { 117 | logError("file: "__FILE__", line: %d, " 118 | "empty handler", __LINE__); 119 | throws_exception(env, "empty handler"); 120 | return; 121 | } 122 | 123 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 124 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 125 | 126 | szValue = (*env)->GetByteArrayElements(env, value, isCopy); 127 | value_len = (*env)->GetArrayLength(env, value); 128 | 129 | result = shmcache_set(context, &shmcache_key, 130 | (const char *)szValue, value_len, ttl); 131 | 132 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 133 | (*env)->ReleaseByteArrayElements(env, value, szValue, 0); 134 | if (result != 0) { 135 | throws_exception(env, strerror(result)); 136 | } 137 | } 138 | 139 | jlong JNICALL Java_org_csource_shmcache_ShmCache_doIncr 140 | (JNIEnv *env, jobject obj, jlong handler, jstring key, 141 | jlong increment, jint ttl) 142 | { 143 | struct shmcache_context *context; 144 | struct shmcache_key_info shmcache_key; 145 | jboolean *isCopy = NULL; 146 | int result; 147 | int64_t new_value = 0; 148 | 149 | context = (struct shmcache_context *)handler; 150 | if (context == NULL) { 151 | logError("file: "__FILE__", line: %d, " 152 | "empty handler", __LINE__); 153 | throws_exception(env, "empty handler"); 154 | return new_value; 155 | } 156 | 157 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 158 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 159 | 160 | result = shmcache_incr(context, &shmcache_key, increment, ttl, &new_value); 161 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 162 | 163 | if (result != 0) { 164 | throws_exception(env, strerror(result)); 165 | } 166 | return new_value; 167 | } 168 | 169 | jobject JNICALL Java_org_csource_shmcache_ShmCache_doGet 170 | (JNIEnv *env, jobject obj, jlong handler, jstring key) 171 | { 172 | #define VALUE_CLASS_NAME "org/csource/shmcache/ShmCache$Value" 173 | 174 | struct shmcache_context *context; 175 | jboolean *isCopy = NULL; 176 | int result; 177 | struct shmcache_key_info shmcache_key; 178 | struct shmcache_value_info shmcache_value; 179 | jbyteArray value; 180 | jobject vo; 181 | 182 | context = (struct shmcache_context *)handler; 183 | if (context == NULL) { 184 | logError("file: "__FILE__", line: %d, " 185 | "empty handler", __LINE__); 186 | throws_exception(env, "empty handler"); 187 | return NULL; 188 | } 189 | 190 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 191 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 192 | result = shmcache_get(context, &shmcache_key, &shmcache_value); 193 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 194 | 195 | if (result == 0) { 196 | char error_info[256]; 197 | jclass value_class; 198 | jmethodID constructor_mid; 199 | 200 | value_class = (*env)->FindClass(env, VALUE_CLASS_NAME); 201 | if (value_class == NULL) { 202 | sprintf(error_info, "can find class: %s", VALUE_CLASS_NAME); 203 | logError("file: "__FILE__", line: %d, %s", 204 | __LINE__, error_info); 205 | throws_exception(env, error_info); 206 | return 0; 207 | } 208 | 209 | constructor_mid = (*env)->GetMethodID(env, value_class, 210 | "", "([BIJ)V"); 211 | if (constructor_mid == NULL) { 212 | sprintf(error_info, "can find constructor for class %s", VALUE_CLASS_NAME); 213 | logError("file: "__FILE__", line: %d, %s", 214 | __LINE__, error_info); 215 | throws_exception(env, error_info); 216 | return 0; 217 | } 218 | 219 | value = (*env)->NewByteArray(env, shmcache_value.length); 220 | (*env)->SetByteArrayRegion(env, value, 0, shmcache_value.length, 221 | (const jbyte *)shmcache_value.data); 222 | 223 | vo = (*env)->NewObject(env, value_class, constructor_mid, 224 | value, shmcache_value.options, 225 | (int64_t)shmcache_value.expires * 1000); 226 | } else { 227 | vo = NULL; 228 | } 229 | 230 | return vo; 231 | } 232 | 233 | jbyteArray JNICALL Java_org_csource_shmcache_ShmCache_doGetBytes 234 | (JNIEnv *env, jobject obj, jlong handler, jstring key) 235 | { 236 | struct shmcache_context *context; 237 | jboolean *isCopy = NULL; 238 | int result; 239 | struct shmcache_key_info shmcache_key; 240 | struct shmcache_value_info shmcache_value; 241 | jbyteArray value; 242 | 243 | context = (struct shmcache_context *)handler; 244 | if (context == NULL) { 245 | logError("file: "__FILE__", line: %d, " 246 | "empty handler", __LINE__); 247 | throws_exception(env, "empty handler"); 248 | return NULL; 249 | } 250 | 251 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 252 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 253 | result = shmcache_get(context, &shmcache_key, &shmcache_value); 254 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 255 | 256 | if (result == 0) { 257 | value = (*env)->NewByteArray(env, shmcache_value.length); 258 | (*env)->SetByteArrayRegion(env, value, 0, shmcache_value.length, 259 | (const jbyte *)shmcache_value.data); 260 | } else { 261 | value = NULL; 262 | } 263 | return value; 264 | } 265 | 266 | jboolean JNICALL Java_org_csource_shmcache_ShmCache_doDelete 267 | (JNIEnv *env, jobject obj, jlong handler, jstring key) 268 | { 269 | struct shmcache_context *context; 270 | jboolean *isCopy = NULL; 271 | struct shmcache_key_info shmcache_key; 272 | int result; 273 | 274 | context = (struct shmcache_context *)handler; 275 | if (context == NULL) { 276 | logError("file: "__FILE__", line: %d, " 277 | "empty handler", __LINE__); 278 | throws_exception(env, "empty handler"); 279 | return false; 280 | } 281 | 282 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 283 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 284 | result = shmcache_delete(context, &shmcache_key); 285 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 286 | return (result == 0); 287 | } 288 | 289 | jboolean JNICALL Java_org_csource_shmcache_ShmCache_doSetTTL 290 | (JNIEnv *env, jobject obj, jlong handler, jstring key, jint ttl) 291 | { 292 | struct shmcache_context *context; 293 | jboolean *isCopy = NULL; 294 | struct shmcache_key_info shmcache_key; 295 | int result; 296 | 297 | context = (struct shmcache_context *)handler; 298 | if (context == NULL) { 299 | logError("file: "__FILE__", line: %d, " 300 | "empty handler", __LINE__); 301 | throws_exception(env, "empty handler"); 302 | return false; 303 | } 304 | 305 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 306 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 307 | result = shmcache_set_ttl(context, &shmcache_key, ttl); 308 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 309 | return (result == 0); 310 | } 311 | 312 | jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetExpires 313 | (JNIEnv *env, jobject obj, jlong handler, jstring key) 314 | { 315 | struct shmcache_context *context; 316 | jboolean *isCopy = NULL; 317 | struct shmcache_key_info shmcache_key; 318 | struct shmcache_value_info shmcache_value; 319 | int result; 320 | 321 | context = (struct shmcache_context *)handler; 322 | if (context == NULL) { 323 | logError("file: "__FILE__", line: %d, " 324 | "empty handler", __LINE__); 325 | throws_exception(env, "empty handler"); 326 | return false; 327 | } 328 | 329 | shmcache_key.length = (*env)->GetStringUTFLength(env, key); 330 | shmcache_key.data = (char *)((*env)->GetStringUTFChars(env, key, isCopy)); 331 | result = shmcache_get(context, &shmcache_key, &shmcache_value); 332 | (*env)->ReleaseStringUTFChars(env, key, shmcache_key.data); 333 | if (result == 0) { 334 | return shmcache_value.expires; 335 | } else { 336 | return -1; 337 | } 338 | } 339 | 340 | jboolean JNICALL Java_org_csource_shmcache_ShmCache_doClear 341 | (JNIEnv *env, jobject obj, jlong handler) 342 | { 343 | struct shmcache_context *context; 344 | 345 | context = (struct shmcache_context *)handler; 346 | if (context == NULL) { 347 | logError("file: "__FILE__", line: %d, " 348 | "empty handler", __LINE__); 349 | throws_exception(env, "empty handler"); 350 | return false; 351 | } 352 | 353 | return shmcache_clear(context) == 0; 354 | } 355 | 356 | jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetLastClearTime 357 | (JNIEnv *env, jobject obj, jlong handler) 358 | { 359 | struct shmcache_context *context; 360 | 361 | context = (struct shmcache_context *)handler; 362 | if (context == NULL) { 363 | logError("file: "__FILE__", line: %d, " 364 | "empty handler", __LINE__); 365 | throws_exception(env, "empty handler"); 366 | return 0; 367 | } 368 | 369 | return shmcache_get_last_clear_time(context); 370 | } 371 | 372 | jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetInitTime 373 | (JNIEnv *env, jobject obj, jlong handler) 374 | { 375 | struct shmcache_context *context; 376 | 377 | context = (struct shmcache_context *)handler; 378 | if (context == NULL) { 379 | logError("file: "__FILE__", line: %d, " 380 | "empty handler", __LINE__); 381 | throws_exception(env, "empty handler"); 382 | return 0; 383 | } 384 | 385 | return shmcache_get_init_time(context); 386 | } 387 | -------------------------------------------------------------------------------- /java/jni/org_csource_shmcache_ShmCache.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_csource_shmcache_ShmCache */ 4 | 5 | #ifndef _Included_org_csource_shmcache_ShmCache 6 | #define _Included_org_csource_shmcache_ShmCache 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #undef org_csource_shmcache_ShmCache_TTL_NEVER_EXPIRED 11 | #define org_csource_shmcache_ShmCache_TTL_NEVER_EXPIRED 0L 12 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_STRING 13 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_STRING 0L 14 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_INTEGER 15 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_INTEGER 1L 16 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_NONE 17 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_NONE 256L 18 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_IGBINARY 19 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_IGBINARY 512L 20 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_MSGPACK 21 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_MSGPACK 1024L 22 | #undef org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_PHP 23 | #define org_csource_shmcache_ShmCache_SHMCACHE_SERIALIZER_PHP 2048L 24 | /* 25 | * Class: org_csource_shmcache_ShmCache 26 | * Method: doInit 27 | * Signature: (Ljava/lang/String;)J 28 | */ 29 | JNIEXPORT jlong JNICALL Java_org_csource_shmcache_ShmCache_doInit 30 | (JNIEnv *, jobject, jstring); 31 | 32 | /* 33 | * Class: org_csource_shmcache_ShmCache 34 | * Method: doDestroy 35 | * Signature: (J)V 36 | */ 37 | JNIEXPORT void JNICALL Java_org_csource_shmcache_ShmCache_doDestroy 38 | (JNIEnv *, jobject, jlong); 39 | 40 | /* 41 | * Class: org_csource_shmcache_ShmCache 42 | * Method: doSetVO 43 | * Signature: (JLjava/lang/String;Lorg/csource/shmcache/ShmCache/Value;)V 44 | */ 45 | JNIEXPORT void JNICALL Java_org_csource_shmcache_ShmCache_doSetVO 46 | (JNIEnv *, jobject, jlong, jstring, jobject); 47 | 48 | /* 49 | * Class: org_csource_shmcache_ShmCache 50 | * Method: doSet 51 | * Signature: (JLjava/lang/String;[BI)V 52 | */ 53 | JNIEXPORT void JNICALL Java_org_csource_shmcache_ShmCache_doSet 54 | (JNIEnv *, jobject, jlong, jstring, jbyteArray, jint); 55 | 56 | /* 57 | * Class: org_csource_shmcache_ShmCache 58 | * Method: doIncr 59 | * Signature: (JLjava/lang/String;JI)J 60 | */ 61 | JNIEXPORT jlong JNICALL Java_org_csource_shmcache_ShmCache_doIncr 62 | (JNIEnv *, jobject, jlong, jstring, jlong, jint); 63 | 64 | /* 65 | * Class: org_csource_shmcache_ShmCache 66 | * Method: doGetBytes 67 | * Signature: (JLjava/lang/String;)[B 68 | */ 69 | JNIEXPORT jbyteArray JNICALL Java_org_csource_shmcache_ShmCache_doGetBytes 70 | (JNIEnv *, jobject, jlong, jstring); 71 | 72 | /* 73 | * Class: org_csource_shmcache_ShmCache 74 | * Method: doGet 75 | * Signature: (JLjava/lang/String;)Lorg/csource/shmcache/ShmCache/Value; 76 | */ 77 | JNIEXPORT jobject JNICALL Java_org_csource_shmcache_ShmCache_doGet 78 | (JNIEnv *, jobject, jlong, jstring); 79 | 80 | /* 81 | * Class: org_csource_shmcache_ShmCache 82 | * Method: doDelete 83 | * Signature: (JLjava/lang/String;)Z 84 | */ 85 | JNIEXPORT jboolean JNICALL Java_org_csource_shmcache_ShmCache_doDelete 86 | (JNIEnv *, jobject, jlong, jstring); 87 | 88 | /* 89 | * Class: org_csource_shmcache_ShmCache 90 | * Method: doSetTTL 91 | * Signature: (JLjava/lang/String;I)Z 92 | */ 93 | JNIEXPORT jboolean JNICALL Java_org_csource_shmcache_ShmCache_doSetTTL 94 | (JNIEnv *, jobject, jlong, jstring, jint); 95 | 96 | /* 97 | * Class: org_csource_shmcache_ShmCache 98 | * Method: doGetExpires 99 | * Signature: (JLjava/lang/String;)J 100 | */ 101 | JNIEXPORT jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetExpires 102 | (JNIEnv *, jobject, jlong, jstring); 103 | 104 | /* 105 | * Class: org_csource_shmcache_ShmCache 106 | * Method: doClear 107 | * Signature: (J)Z 108 | */ 109 | JNIEXPORT jboolean JNICALL Java_org_csource_shmcache_ShmCache_doClear 110 | (JNIEnv *, jobject, jlong); 111 | 112 | /* 113 | * Class: org_csource_shmcache_ShmCache 114 | * Method: doGetLastClearTime 115 | * Signature: (J)J 116 | */ 117 | JNIEXPORT jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetLastClearTime 118 | (JNIEnv *, jobject, jlong); 119 | 120 | /* 121 | * Class: org_csource_shmcache_ShmCache 122 | * Method: doGetInitTime 123 | * Signature: (J)J 124 | */ 125 | JNIEXPORT jlong JNICALL Java_org_csource_shmcache_ShmCache_doGetInitTime 126 | (JNIEnv *, jobject, jlong); 127 | 128 | #ifdef __cplusplus 129 | } 130 | #endif 131 | #endif 132 | -------------------------------------------------------------------------------- /java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.csource 8 | libshmcache4j 9 | 1.0-SNAPSHOT 10 | 11 | 5.3.27 12 | 13 | 14 | 15 | 16 | org.springframework 17 | spring-context 18 | ${spring-version} 19 | 20 | 21 | 22 | org.springframework 23 | spring-core 24 | ${spring-version} 25 | 26 | 27 | org.springframework 28 | spring-beans 29 | ${spring-version} 30 | 31 | 32 | org.glassfish 33 | javax.json 34 | 1.0.4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 45 | 1.8 46 | 1.8 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /java/src/main/java/org/csource/shmcache/ShmCache.java: -------------------------------------------------------------------------------- 1 | package org.csource.shmcache; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.io.UnsupportedEncodingException; 8 | import java.io.StringReader; 9 | import javax.json.Json; 10 | import javax.json.JsonReader; 11 | import javax.json.JsonArray; 12 | import javax.json.JsonObject; 13 | import javax.json.JsonValue; 14 | 15 | public class ShmCache { 16 | public static class Value { 17 | private byte[] value; 18 | private int options; //options for application 19 | private long expires; //expire time in millisenconds 20 | 21 | public Value(byte[] value, int options, long expires) { 22 | this.value = value; 23 | this.options = options; 24 | this.expires = expires; 25 | } 26 | 27 | public byte[] getValue() { 28 | return this.value; 29 | } 30 | 31 | public int getOptions() { 32 | return this.options; 33 | } 34 | 35 | public long getExpires() { 36 | return this.expires; 37 | } 38 | 39 | public String toString() { 40 | try { 41 | return "value: " + new String(this.value, ShmCache.charset) 42 | + ", options: " + this.options + ", expires: " + this.expires; 43 | } catch (UnsupportedEncodingException ex) { 44 | throw new RuntimeException(ex); 45 | } 46 | } 47 | } 48 | 49 | public static final int TTL_NEVER_EXPIRED = 0; 50 | 51 | public static final int SHMCACHE_SERIALIZER_STRING = 0x1; //string type 52 | public static final int SHMCACHE_SERIALIZER_LIST = (SHMCACHE_SERIALIZER_STRING | 0x2); 53 | public static final int SHMCACHE_SERIALIZER_MAP = (SHMCACHE_SERIALIZER_STRING | 0x4); 54 | public static final int SHMCACHE_SERIALIZER_INTEGER = (SHMCACHE_SERIALIZER_STRING | 0x8); 55 | public static final int SHMCACHE_SERIALIZER_IGBINARY = 0x200; 56 | public static final int SHMCACHE_SERIALIZER_MSGPACK = 0x400; 57 | public static final int SHMCACHE_SERIALIZER_PHP = 0x800; 58 | 59 | private static HashMap instances = new HashMap(); 60 | private static String charset = "UTF-8"; 61 | private static String libraryFilename = null; 62 | 63 | private long handler; 64 | 65 | private native long doInit(String configFilename); 66 | private native void doDestroy(long handler); 67 | private native void doSetVO(long handler, String key, Value value); 68 | private native void doSet(long handler, String key, byte[] value, int ttl); 69 | private native long doIncr(long handler, String key, long increment, int ttl); 70 | private native byte[] doGetBytes(long handler, String key); 71 | private native Value doGet(long handler, String key); 72 | private native boolean doDelete(long handler, String key); 73 | private native boolean doSetTTL(long handler, String key, int ttl); 74 | private native long doGetExpires(long handler, String key); 75 | private native boolean doClear(long handler); 76 | private native long doGetLastClearTime(long handler); 77 | private native long doGetInitTime(long handler); 78 | 79 | public static String getLibraryFilename() { 80 | return libraryFilename; 81 | } 82 | 83 | /** 84 | * set libshmcachejni.so filename to load 85 | * @param filename the full filename 86 | * @return none 87 | */ 88 | public static void setLibraryFilename(String filename) { 89 | if (libraryFilename == null) { 90 | System.load(filename); //load the library 91 | libraryFilename = filename; 92 | } else if (libraryFilename.equals(filename)) { 93 | System.err.println("[WARNING] library " + libraryFilename + " already loaded"); 94 | } else { 95 | throw new RuntimeException("library " + libraryFilename 96 | + " already loaded, can't change to " + filename); 97 | } 98 | } 99 | 100 | public static String getCharset() { 101 | return charset; 102 | } 103 | 104 | public static void setCharset(String value) { 105 | charset = value; 106 | } 107 | 108 | // private for singleton 109 | private ShmCache(String configFilename) { 110 | this.handler = doInit(configFilename); 111 | } 112 | 113 | protected void finalize() throws Throwable { 114 | super.finalize(); 115 | this.close(); 116 | } 117 | 118 | private void close() { 119 | System.out.println("close"); 120 | doDestroy(this.handler); 121 | } 122 | 123 | /** 124 | * get ShmCache instance 125 | * @param configFilename the config filename such as /usr/local/etc/libshmcache.conf 126 | * @return ShmCache object 127 | */ 128 | public synchronized static ShmCache getInstance(String configFilename) { 129 | ShmCache obj = instances.get(configFilename); 130 | if (obj == null) { 131 | obj = new ShmCache(configFilename); 132 | instances.put(configFilename, obj); 133 | } 134 | 135 | return obj; 136 | } 137 | 138 | /** 139 | * clear ShmCache instances 140 | * @return none 141 | */ 142 | public synchronized static void clearInstances() { 143 | instances.clear(); 144 | } 145 | 146 | /** 147 | * get the value of the key 148 | * @param key the key 149 | * @return the value object, null for not exists 150 | */ 151 | public Value getValue(String key) { 152 | return doGet(this.handler, key); 153 | } 154 | 155 | /** 156 | * get byte array value of the key 157 | * @param key the key 158 | * @return byte array value, null for not exists 159 | */ 160 | public byte[] getBytes(String key) { 161 | return doGetBytes(this.handler, key); 162 | } 163 | 164 | /** 165 | * get string value of the key 166 | * @param key the key 167 | * @return string value, null for not exists 168 | */ 169 | public String getString(String key) { 170 | byte[] value = doGetBytes(this.handler, key); 171 | try { 172 | return value != null ? new String(value, charset) : null; 173 | } catch (UnsupportedEncodingException ex) { 174 | throw new RuntimeException(ex); 175 | } 176 | } 177 | 178 | private String unquote(String sv) 179 | { 180 | if (sv.length() >= 2 && (sv.charAt(0) == '"' && 181 | sv.charAt(sv.length() - 1) == '"')) 182 | { 183 | return sv.substring(1, sv.length() - 1); 184 | } else { 185 | return sv; 186 | } 187 | } 188 | 189 | /** 190 | * json string to list 191 | * @param value the value 192 | * @return string list, null for not exists 193 | */ 194 | public List jsonToList(String value) { 195 | ArrayList list = new ArrayList(); 196 | JsonReader jsonReader = Json.createReader(new StringReader(value)); 197 | try { 198 | JsonArray array = jsonReader.readArray(); 199 | for (JsonValue jvalue: array) { 200 | list.add(this.unquote(jvalue.toString())); 201 | } 202 | } finally { 203 | jsonReader.close(); 204 | } 205 | 206 | return list; 207 | } 208 | 209 | /** 210 | * get string list of the key 211 | * @param key the key 212 | * @return string list, null for not exists 213 | */ 214 | public List getList(String key) { 215 | return jsonToList(getString(key)); 216 | } 217 | 218 | /** 219 | * json string to map 220 | * @param value the value 221 | * @return string map, null for not exists 222 | */ 223 | public Map jsonToMap(String value) { 224 | if (value == null) { 225 | return null; 226 | } 227 | HashMap map= new HashMap(); 228 | JsonReader jsonReader = Json.createReader(new StringReader(value)); 229 | try { 230 | JsonObject jobject = jsonReader.readObject(); 231 | for (Map.Entry entry : jobject.entrySet()) { 232 | map.put(entry.getKey(), this.unquote(entry.getValue().toString())); 233 | } 234 | } finally { 235 | jsonReader.close(); 236 | } 237 | 238 | return map; 239 | } 240 | 241 | /** 242 | * get string map of the key 243 | * @param key the key 244 | * @return string map, null for not exists 245 | */ 246 | public Map getMap(String key) { 247 | return jsonToMap(getString(key)); 248 | } 249 | 250 | /** 251 | * get the value of the key 252 | * @param key the key 253 | * @return the value object (String, Long, List, Map or byte[]), 254 | null for not exists 255 | */ 256 | public Object get(String key) { 257 | Value vo = doGet(this.handler, key); 258 | if (vo == null) { 259 | return null; 260 | } 261 | 262 | byte[] bs = vo.getValue(); 263 | try { 264 | switch (vo.getOptions()) { 265 | case SHMCACHE_SERIALIZER_STRING: 266 | return new String(bs, charset); 267 | case SHMCACHE_SERIALIZER_INTEGER: 268 | return Long.parseLong(new String(bs, charset)); 269 | case SHMCACHE_SERIALIZER_LIST: 270 | return jsonToList(new String(bs, charset)); 271 | case SHMCACHE_SERIALIZER_MAP: 272 | return jsonToMap(new String(bs, charset)); 273 | default: 274 | return bs; 275 | } 276 | } catch (UnsupportedEncodingException ex) { 277 | throw new RuntimeException(ex); 278 | } 279 | } 280 | 281 | /** 282 | * set value object of the key 283 | * @param key the key 284 | * @param value the value object 285 | * @return none 286 | */ 287 | public void set(String key, Value value) { 288 | doSetVO(this.handler, key, value); 289 | } 290 | 291 | /** 292 | * set value and TTL of the key 293 | * @param key the key 294 | * @param value the byte array value 295 | * @param ttl the TTL in seconds 296 | * @return none 297 | */ 298 | public void set(String key, byte[] value, int ttl) { 299 | doSet(this.handler, key, value, ttl); 300 | } 301 | 302 | /** 303 | * set value and TTL of the key 304 | * @param key the key 305 | * @param value the string value 306 | * @param ttl the TTL in seconds 307 | * @return none 308 | */ 309 | public void set(String key, String value, int ttl) { 310 | try { 311 | doSet(this.handler, key, value.getBytes(charset), ttl); 312 | } catch (UnsupportedEncodingException ex) { 313 | throw new RuntimeException(ex); 314 | } 315 | } 316 | 317 | /** 318 | * set TTL of the key 319 | * @param key the key 320 | * @param ttl the TTL in seconds 321 | * @return true for success, otherwise fail 322 | */ 323 | public boolean setTTL(String key, int ttl) { 324 | return doSetTTL(this.handler, key, ttl); 325 | } 326 | 327 | /** 328 | * increase value of the key 329 | * @param key the key 330 | * @param increment the increment such as 1 331 | * @param ttl the TTL in seconds 332 | * @return the value after increase 333 | */ 334 | public long incr(String key, long increment, int ttl) { 335 | return doIncr(this.handler, key, increment, ttl); 336 | } 337 | 338 | /** 339 | * remove the key 340 | * @param key the key to remove 341 | * @return true for success, false for not exists 342 | */ 343 | public boolean remove(String key) { 344 | return doDelete(this.handler, key); 345 | } 346 | 347 | /** 348 | * clear the shm, remove all keys 349 | * @return true for success, false for fail 350 | */ 351 | public boolean clear() { 352 | return doClear(this.handler); 353 | } 354 | 355 | /** 356 | * get the expires timestamp of the key 357 | * @param key the key 358 | * @return expires timestamp in milliseconds 359 | * return -1 when not exist 360 | * return 0 for never expired 361 | */ 362 | public long getExpires(String key) { 363 | long expires = doGetExpires(this.handler, key); 364 | return expires > 0 ? (expires * 1000) : expires; 365 | } 366 | 367 | /** 368 | * get the TTL of the key 369 | * @param key the key 370 | * @return the ttl in seconds 371 | * return -1 when not exist 372 | * return 0 for never expired 373 | */ 374 | public int getTTL(String key) { 375 | long expires = this.getExpires(key); 376 | return (int)(expires > 0 ? (expires - System.currentTimeMillis()) / 1000 : expires); 377 | } 378 | 379 | /** 380 | * get last clear timestamp 381 | * @return last clear timestamp in milliseconds 382 | * return 0 for never cleared 383 | */ 384 | public long getLastClearTime() { 385 | return doGetLastClearTime(this.handler) * 1000; 386 | } 387 | 388 | /** 389 | * get the shm init timestamp 390 | * @return the shm init timestamp in milliseconds 391 | */ 392 | public long getInitTime() { 393 | return doGetInitTime(this.handler) * 1000; 394 | } 395 | 396 | public static void main(String[] args) throws UnsupportedEncodingException { 397 | String configFilename = "/usr/local/etc/libshmcache.conf"; 398 | String key; 399 | String value; 400 | if (args.length > 0) { 401 | key = args[0]; 402 | } else { 403 | key = "测试key"; 404 | } 405 | if (args.length > 1) { 406 | value = args[1]; 407 | } else { 408 | value = "测试value"; 409 | } 410 | 411 | ShmCache.setLibraryFilename("/Users/yuqing/Devel/libshmcache/java/jni/libshmcachejni.so"); 412 | ShmCache shmCache = ShmCache.getInstance(configFilename); 413 | 414 | System.out.println("value: " + shmCache.get(key)); 415 | System.out.println("TTL: " + shmCache.getTTL(key) + ", expires: " + shmCache.getExpires(key)); 416 | 417 | Value vo; 418 | vo = new Value(value.getBytes(charset), SHMCACHE_SERIALIZER_STRING, 419 | System.currentTimeMillis() + 300 * 1000); 420 | shmCache.set(key, vo); 421 | System.out.println(shmCache.get(key)); 422 | 423 | shmCache.set(key, value, 300); 424 | System.out.println("value: " + shmCache.getString(key)); 425 | System.out.println("TTL: " + shmCache.getTTL(key) + ", expires: " + shmCache.getExpires(key)); 426 | System.out.println(shmCache.getValue(key)); 427 | 428 | System.out.println("set TTL: " + shmCache.setTTL(key, 600)); 429 | System.out.println("TTL: " + shmCache.getTTL(key) + ", expires: " + shmCache.getExpires(key)); 430 | System.out.println(shmCache.getValue(key)); 431 | 432 | System.out.println("after incr: " + shmCache.incr(key, 1, TTL_NEVER_EXPIRED)); 433 | System.out.println("TTL: " + shmCache.getTTL(key) + ", expires: " + shmCache.getExpires(key)); 434 | System.out.println(shmCache.getValue(key)); 435 | 436 | System.out.println("remove key: " + shmCache.remove(key)); 437 | 438 | 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /java/src/main/java/org/csource/shmcache/ShmCachePropertyConfigAnnotation.java: -------------------------------------------------------------------------------- 1 | package org.csource.shmcache; 2 | 3 | import java.util.Properties; 4 | import java.util.ArrayList; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | public class ShmCachePropertyConfigAnnotation { 9 | 10 | public static final String CONFIG_TAG = "@config"; 11 | private static boolean debug = false; 12 | 13 | private static class KeyValue { 14 | private String key; 15 | private String value; 16 | 17 | public KeyValue(String key, String value) { 18 | this.key = key; 19 | this.value = value; 20 | } 21 | 22 | public String getKey() { 23 | return this.key; 24 | } 25 | 26 | public String getValue() { 27 | return this.value; 28 | } 29 | } 30 | 31 | private ShmCachePropertyConfigAnnotation() { 32 | } 33 | 34 | public static void setDebug(boolean value) { 35 | debug = value; 36 | } 37 | 38 | protected static String processProperties(ShmCache shmCache, String key, String value) { 39 | value = value.substring(CONFIG_TAG.length()).trim(); 40 | if (!(value.startsWith("(") && value.endsWith(")"))) { 41 | return null; 42 | } 43 | 44 | String params = value.substring(1, value.length() - 1); 45 | String[] parts = params.split(","); 46 | if (parts.length > 2) { 47 | System.err.println("too many parameters!"); 48 | return null; 49 | } 50 | 51 | String keyParam = parts[0].trim(); 52 | String defaultValue = parts.length == 2 ? parts[1].trim() : ""; 53 | if (keyParam.length() == 0) { 54 | keyParam = key; 55 | } 56 | if (debug) { 57 | System.err.println("key: " + keyParam + ", default value: " + defaultValue); 58 | } 59 | 60 | String v = shmCache.getString(keyParam); 61 | if (v == null) { 62 | System.err.println("key: " + keyParam + " not found, set to default: " + defaultValue); 63 | v = defaultValue; 64 | } 65 | 66 | return v; 67 | } 68 | 69 | public static void processProperties(ShmCache shmCache, Properties props) { 70 | 71 | ArrayList modifiedKV = new ArrayList(); 72 | Set> entries = props.entrySet(); 73 | for (Map.Entry entry : entries) { 74 | String key = (String)entry.getKey(); 75 | String value = (String)entry.getValue(); 76 | 77 | if (value.startsWith(CONFIG_TAG)) { 78 | if (debug) { 79 | System.err.println("processing: " + key + "=" + value); 80 | } 81 | String newValue = processProperties(shmCache, key, value); 82 | if (newValue != null) { 83 | modifiedKV.add(new KeyValue(key, newValue)); 84 | } 85 | } 86 | } 87 | 88 | for (KeyValue kv : modifiedKV) { 89 | props.put(kv.getKey(), kv.getValue()); 90 | if (debug) { 91 | System.err.println("set key: " + kv.getKey() + " to value: " + kv.getValue()); 92 | } 93 | } 94 | if (debug) { 95 | System.err.println("@config key count: " + modifiedKV.size()); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /java/src/main/java/org/csource/shmcache/ShmCachePropertyConfigurer.java: -------------------------------------------------------------------------------- 1 | package org.csource.shmcache; 2 | 3 | import java.util.Properties; 4 | import java.io.IOException; 5 | import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 6 | 7 | public class ShmCachePropertyConfigurer extends PropertyPlaceholderConfigurer { 8 | 9 | protected String libraryFilename; 10 | protected String configFilename; 11 | protected String charset = "UTF-8"; 12 | protected boolean debug = false; 13 | protected ShmCache shmCache; 14 | 15 | public void setLibraryFilename(String libraryFilename) { 16 | this.libraryFilename = libraryFilename; 17 | } 18 | 19 | public void setConfigFilename(String configFilename) { 20 | this.configFilename = configFilename; 21 | } 22 | 23 | public void setCharset(String charset) { 24 | this.charset = charset; 25 | } 26 | 27 | public void setDebug(String value) { 28 | this.debug = value.equals("1") || value.equals("true"); 29 | } 30 | 31 | protected void initShmCache() { 32 | if (this.shmCache != null) { 33 | return; 34 | } 35 | 36 | if (this.charset != null) { 37 | ShmCache.setCharset(this.charset); 38 | } 39 | ShmCache.setLibraryFilename(this.libraryFilename); 40 | this.shmCache = ShmCache.getInstance(this.configFilename); 41 | } 42 | 43 | @Override 44 | protected void loadProperties(Properties props) throws IOException { 45 | super.loadProperties(props); 46 | 47 | this.initShmCache(); 48 | ShmCachePropertyConfigAnnotation.setDebug(this.debug); 49 | ShmCachePropertyConfigAnnotation.processProperties(shmCache, props); 50 | } 51 | 52 | public String getConfig(String key) { 53 | this.initShmCache(); 54 | return this.shmCache.getString(key); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /libshmcache.spec: -------------------------------------------------------------------------------- 1 | 2 | %define LibShmcacheTool libshmcache-tool 3 | %define LibShmcacheConfig libshmcache-config 4 | %define LibShmcacheDevel libshmcache-devel 5 | %define CommitVersion %(echo $COMMIT_VERSION) 6 | 7 | Name: libshmcache 8 | Version: 1.0.8 9 | Release: 1%{?dist} 10 | Summary: a high performance local share memory cache for multi processes 11 | License: LGPL 12 | Group: Arch/Tech 13 | URL: http://github.com/happyfish100/libshmcache/ 14 | Source: http://github.com/happyfish100/libshmcache/%{name}-%{version}.tar.gz 15 | 16 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 17 | 18 | BuildRequires: libfastcommon-devel >= 1.0.56 19 | 20 | Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id 21 | Requires: libfastcommon >= 1.0.56 22 | 23 | %description 24 | libshmcache is a local share memory cache for multi processes. 25 | it is high performance because read is lockless. 26 | this project contains C library and PHP extension. 27 | commit version: %{CommitVersion} 28 | 29 | %package tool 30 | Summary: tool commands 31 | Requires: %{name}%{?_isa} = %{version}-%{release} 32 | 33 | %description tool 34 | This package provides tool commands 35 | commit version: %{CommitVersion} 36 | 37 | %package config 38 | Summary: libshmcache config file 39 | 40 | %description config 41 | This package provides libshmcache config file 42 | commit version: %{CommitVersion} 43 | 44 | %package devel 45 | Summary: Development header file 46 | Requires: %{name}%{?_isa} = %{version}-%{release} 47 | 48 | %description devel 49 | This package provides the header files of libshmcache 50 | commit version: %{CommitVersion} 51 | 52 | %prep 53 | 54 | %setup -q 55 | 56 | %build 57 | cd src && make clean && make 58 | cd tools && make clean && make 59 | 60 | %install 61 | rm -rf %{buildroot} 62 | cd src && DESTDIR=$RPM_BUILD_ROOT make install 63 | cd tools && DESTDIR=$RPM_BUILD_ROOT make install 64 | mkdir -p $RPM_BUILD_ROOT/etc/ 65 | cp -f ../../conf/libshmcache.conf $RPM_BUILD_ROOT/etc/ 66 | 67 | %post 68 | 69 | %preun 70 | 71 | %postun 72 | 73 | %clean 74 | rm -rf %{buildroot} 75 | 76 | %files 77 | %defattr(-,root,root,-) 78 | /usr/lib64/libshmcache.so* 79 | /usr/lib/libshmcache.so* 80 | 81 | %files tool 82 | /usr/bin/shmcache_set 83 | /usr/bin/shmcache_get 84 | /usr/bin/shmcache_delete 85 | /usr/bin/shmcache_stats 86 | /usr/bin/shmcache_remove_all 87 | 88 | %files config 89 | /etc/libshmcache.conf 90 | 91 | %files devel 92 | %defattr(-,root,root,-) 93 | /usr/include/shmcache/* 94 | 95 | %changelog 96 | * Fri Dec 23 2016 Yu Qing <384681@qq.com> 97 | - first RPM release (1.0) 98 | -------------------------------------------------------------------------------- /php-shmcache/bench/redis.php: -------------------------------------------------------------------------------- 1 | 'yuqing', 'score' => 90.5, 'city' => 'beijing', 'gender' => 'male'); 5 | 6 | $host = '10.0.11.89'; 7 | $port = 6379; 8 | $connectTimeout = 1; 9 | $redis = new \Redis(); 10 | $redis->connect($host, $port, $connectTimeout); 11 | $redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_IGBINARY); 12 | var_dump($redis->set($key, $value, 300)); 13 | for ($i=0; $i<10000; $i++) { 14 | //$redis->set($key, $value, 300); 15 | $v = $redis->get($key); 16 | } 17 | var_dump($redis->get($key)); 18 | var_dump($redis->delete($key)); 19 | 20 | -------------------------------------------------------------------------------- /php-shmcache/bench/shmcache.php: -------------------------------------------------------------------------------- 1 | 'yuqing', 'score' => 90.5, 'city' => 'beijing', 'gender' => 'male'); 5 | 6 | $cache = new ShmCache('/etc/libshmcache.conf', ShmCache::SERIALIZER_IGBINARY); 7 | $cache->set($key, $value, 300); 8 | for ($i=0; $i<1000000; $i++) { 9 | //$cache->set($key, $value, 300); 10 | $v = $cache->get($key); 11 | } 12 | var_dump($cache->get($key)); 13 | var_dump($cache->delete($key)); 14 | 15 | //var_dump($cache->stats()); 16 | //echo json_encode($cache->stats(), JSON_PRETTY_PRINT); 17 | -------------------------------------------------------------------------------- /php-shmcache/config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension shmcache 2 | 3 | PHP_ARG_WITH(shmcache, wapper for libshmcache 4 | [ --with-shmcache Include shmcache wapper for libshmcache]) 5 | 6 | if test "$PHP_SHMCACHE" != "no"; then 7 | PHP_SUBST(SHMCACHE_SHARED_LIBADD) 8 | 9 | if test -z "$ROOT"; then 10 | ROOT=/usr 11 | fi 12 | 13 | PHP_ADD_INCLUDE($ROOT/include/fastcommon) 14 | PHP_ADD_INCLUDE($ROOT/include/shmcache) 15 | 16 | PHP_ADD_LIBRARY_WITH_PATH(fastcommon, $ROOT/lib, SHMCACHE_SHARED_LIBADD) 17 | PHP_ADD_LIBRARY_WITH_PATH(shmcache, $ROOT/lib, SHMCACHE_SHARED_LIBADD) 18 | 19 | PHP_NEW_EXTENSION(shmcache, serializer/igbinary.c serializer/msgpack.c serializer/php.c serializer/shmcache_serializer.c php_shmcache.c, $ext_shared) 20 | 21 | CFLAGS="$CFLAGS -Wall" 22 | fi 23 | -------------------------------------------------------------------------------- /php-shmcache/php-shmcache.spec.in: -------------------------------------------------------------------------------- 1 | %define php_inidir %(php --ini | head -n 1 | awk -F ':' '{print $2;}' | sed 's/ //g') 2 | %define php_extdir %(php-config --extension-dir 2>/dev/null) 3 | Name: php-shmcache 4 | Version: 1.0.2 5 | Release: 1%{?dist} 6 | Summary: The php extension for libshmcache 7 | License: GPL 8 | Group: Arch/Tech 9 | URL: https://github.com/happyfish100/libshmcache 10 | Source: https://github.com/happyfish100/libshmcache/%{name}-%{version}.tar.gz 11 | 12 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 13 | 14 | BuildRequires: libshmcache-devel >= 1.0.2 15 | Requires: libshmcache >= 1.0.2 16 | 17 | %description 18 | This package provides the php extension for libshmcache 19 | 20 | %prep 21 | %setup -q 22 | 23 | %build 24 | phpize 25 | %configure 26 | make 27 | 28 | %install 29 | rm -rf %{buildroot} 30 | 31 | mkdir -p %{buildroot}%{php_extdir} 32 | mkdir -p %{buildroot}%{php_inidir}/php.d 33 | cp -f .libs/shmcache.so %{buildroot}%{php_extdir} 34 | cp -f shmcache.ini %{buildroot}%{php_inidir}/php.d/shmcache.ini 35 | 36 | %post 37 | 38 | %preun 39 | 40 | %postun 41 | 42 | %clean 43 | #rm -rf %{buildroot} 44 | 45 | %files 46 | %defattr(-,root,root,-) 47 | %{php_extdir}/* 48 | %{php_inidir}/php.d/* 49 | 50 | %changelog 51 | * Wed Dec 26 2016 Yu Qing 52 | - first RPM release (1.0) 53 | -------------------------------------------------------------------------------- /php-shmcache/php_shmcache.h: -------------------------------------------------------------------------------- 1 | #ifndef SHMCACHE_H 2 | #define SHMCACHE_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #ifdef PHP_WIN32 9 | #define PHP_SHMCACHE_API __declspec(dllexport) 10 | #else 11 | #define PHP_SHMCACHE_API 12 | #endif 13 | 14 | PHP_MINIT_FUNCTION(shmcache); 15 | PHP_RINIT_FUNCTION(shmcache); 16 | PHP_MSHUTDOWN_FUNCTION(shmcache); 17 | PHP_RSHUTDOWN_FUNCTION(shmcache); 18 | PHP_MINFO_FUNCTION(shmcache); 19 | 20 | ZEND_FUNCTION(shmcache_version); 21 | 22 | PHP_SHMCACHE_API zend_class_entry *php_shmcache_get_ce(void); 23 | PHP_SHMCACHE_API zend_class_entry *php_shmcache_get_exception(void); 24 | PHP_SHMCACHE_API zend_class_entry *php_shmcache_get_exception_base(int root TSRMLS_DC); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /* SHMCACHE_H */ 31 | -------------------------------------------------------------------------------- /php-shmcache/serializer/igbinary.c: -------------------------------------------------------------------------------- 1 | #include "shmcache_serializer.h" 2 | 3 | int shmcache_igbinary_pack(zval *pzval, char **buf, int *len) 4 | { 5 | int ret; 6 | size_t out_len; 7 | 8 | if ((ret=igbinary_pack_func((uint8_t **)buf, &out_len, pzval TSRMLS_CC)) == 0) { 9 | *len = out_len; 10 | } else { 11 | *len = 0; 12 | *buf = NULL; 13 | } 14 | return ret; 15 | } 16 | 17 | int shmcache_igbinary_unpack(char *content, size_t len, zval *rv) 18 | { 19 | #if (PHP_MAJOR_VERSION < 7) 20 | INIT_PZVAL(rv); 21 | return igbinary_unpack_func((const uint8_t *)content, (size_t)len, &rv TSRMLS_CC); 22 | #else 23 | return igbinary_unpack_func((const uint8_t *)content, (size_t)len, rv TSRMLS_CC); 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /php-shmcache/serializer/msgpack.c: -------------------------------------------------------------------------------- 1 | #include "shmcache_serializer.h" 2 | 3 | int shmcache_msgpack_pack(zval *pzval, smart_str *buf) 4 | { 5 | msgpack_serialize_func(buf, pzval); 6 | return 0; 7 | } 8 | 9 | int shmcache_msgpack_unpack(char *content, size_t len, zval *rv) 10 | { 11 | ZVAL_NULL(rv); 12 | msgpack_unserialize_func(rv, content, len); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /php-shmcache/serializer/php.c: -------------------------------------------------------------------------------- 1 | #include "shmcache_serializer.h" 2 | #include "ext/standard/php_var.h" /* for serialize */ 3 | #include "logger.h" 4 | 5 | #if PHP_MAJOR_VERSION < 7 6 | #define GET_ZVAL_PTR(v) &v 7 | #else 8 | #define GET_ZVAL_PTR(v) v 9 | #endif 10 | 11 | int shmcache_php_pack(zval *pzval, smart_str *buf) 12 | { 13 | php_serialize_data_t var_hash; 14 | 15 | PHP_VAR_SERIALIZE_INIT(var_hash); 16 | php_var_serialize(buf, GET_ZVAL_PTR(pzval), &var_hash TSRMLS_CC); 17 | PHP_VAR_SERIALIZE_DESTROY(var_hash); 18 | return 0; 19 | } 20 | 21 | int shmcache_php_unpack(char *content, size_t len, zval *rv) 22 | { 23 | const unsigned char *p; 24 | php_unserialize_data_t var_hash; 25 | 26 | p = (const unsigned char*)content; 27 | ZVAL_FALSE(rv); 28 | PHP_VAR_UNSERIALIZE_INIT(var_hash); 29 | if (!php_var_unserialize(GET_ZVAL_PTR(rv), &p, p + len, &var_hash TSRMLS_CC)) { 30 | zval_ptr_dtor(GET_ZVAL_PTR(rv)); 31 | PHP_VAR_UNSERIALIZE_DESTROY(var_hash); 32 | logError("file: "__FILE__", line: %d, " 33 | "unpack error at offset %ld of %ld bytes", 34 | __LINE__, (long)((char*)p - content), len); 35 | return EINVAL; 36 | } 37 | PHP_VAR_UNSERIALIZE_DESTROY(var_hash); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /php-shmcache/serializer/shmcache_serializer.c: -------------------------------------------------------------------------------- 1 | #include "shmcache_types.h" 2 | #include "logger.h" 3 | #include "shmcache_serializer.h" 4 | 5 | php_msgpack_serialize_func msgpack_serialize_func = NULL; 6 | php_msgpack_unserialize_func msgpack_unserialize_func = NULL; 7 | igbinary_serialize_func igbinary_pack_func = NULL; 8 | igbinary_unserialize_func igbinary_unpack_func = NULL; 9 | 10 | int shmcache_serializers = SHMCACHE_SERIALIZER_STRING | 11 | SHMCACHE_SERIALIZER_LIST | 12 | SHMCACHE_SERIALIZER_MAP | 13 | SHMCACHE_SERIALIZER_INTEGER | 14 | SHMCACHE_SERIALIZER_PHP; 15 | 16 | extern int shmcache_php_pack(zval *pzval, smart_str *buf); 17 | extern int shmcache_php_unpack(char *content, size_t len, zval *rv); 18 | 19 | extern int shmcache_msgpack_pack(zval *pzval, smart_str *buf); 20 | extern int shmcache_msgpack_unpack(char *content, size_t len, zval *rv); 21 | 22 | extern int shmcache_igbinary_pack(zval *pzval, char **buf, int *len); 23 | extern int shmcache_igbinary_unpack(char *content, size_t len, zval *rv); 24 | 25 | int shmcache_load_functions() 26 | { 27 | void *handle; 28 | 29 | handle = dlopen(NULL, RTLD_LAZY); 30 | if (handle == NULL) { 31 | logError("file: "__FILE__", line: %d, " 32 | "call dlopen fail, error: %s", 33 | __LINE__, dlerror()); 34 | return errno != 0 ? errno : EFAULT; 35 | } 36 | msgpack_serialize_func = (php_msgpack_serialize_func)dlsym(handle, 37 | "php_msgpack_serialize"); 38 | msgpack_unserialize_func = (php_msgpack_unserialize_func)dlsym(handle, 39 | "php_msgpack_unserialize"); 40 | igbinary_pack_func = (igbinary_serialize_func)dlsym(handle, 41 | "igbinary_serialize"); 42 | igbinary_unpack_func = (igbinary_unserialize_func)dlsym(handle, 43 | "igbinary_unserialize"); 44 | if (msgpack_serialize_func != NULL && msgpack_unserialize_func != NULL) { 45 | shmcache_serializers |= SHMCACHE_SERIALIZER_MSGPACK; 46 | } 47 | if (igbinary_pack_func != NULL && igbinary_unpack_func != NULL) { 48 | shmcache_serializers |= SHMCACHE_SERIALIZER_IGBINARY; 49 | } 50 | 51 | dlclose(handle); 52 | return 0; 53 | } 54 | 55 | int shmcache_serialize(zval *pzval, 56 | struct shmcache_serialize_output *output) 57 | { 58 | int result; 59 | int serializer; 60 | 61 | switch (ZEND_TYPE_OF(pzval)) { 62 | case IS_STRING: 63 | output->value->options = SHMCACHE_SERIALIZER_STRING; 64 | output->value->data = Z_STRVAL_P(pzval); 65 | output->value->length = Z_STRLEN_P(pzval); 66 | return 0; 67 | case IS_LONG: 68 | output->value->options = SHMCACHE_SERIALIZER_INTEGER; 69 | output->value->data = output->tmp; 70 | output->value->length = sprintf(output->tmp, "%"PRId64, 71 | (int64_t)Z_LVAL_P(pzval)); 72 | return 0; 73 | default: 74 | break; 75 | } 76 | 77 | serializer = output->value->options; 78 | switch (serializer) { 79 | case SHMCACHE_SERIALIZER_IGBINARY: 80 | return shmcache_igbinary_pack(pzval, &output->value->data, 81 | &output->value->length); 82 | case SHMCACHE_SERIALIZER_MSGPACK: 83 | case SHMCACHE_SERIALIZER_PHP: 84 | if (serializer == SHMCACHE_SERIALIZER_MSGPACK) { 85 | result = shmcache_msgpack_pack(pzval, output->buf); 86 | } else { 87 | result = shmcache_php_pack(pzval, output->buf); 88 | } 89 | 90 | if (result == 0) { 91 | #if PHP_MAJOR_VERSION < 7 92 | output->value->data = output->buf->c; 93 | output->value->length = output->buf->len; 94 | #else 95 | output->value->data = output->buf->s->val; 96 | output->value->length = output->buf->s->len; 97 | #endif 98 | } else { 99 | output->value->data = NULL; 100 | output->value->length = 0; 101 | } 102 | return result; 103 | default: 104 | output->value->data = NULL; 105 | output->value->length = 0; 106 | logError("file: "__FILE__", line: %d, " 107 | "invalid serializer: %d", 108 | __LINE__, serializer); 109 | return EINVAL; 110 | } 111 | } 112 | 113 | void shmcache_free_serialize_output(struct shmcache_serialize_output *output) 114 | { 115 | switch (output->value->options) { 116 | case SHMCACHE_SERIALIZER_IGBINARY: 117 | efree(output->value->data); 118 | break; 119 | case SHMCACHE_SERIALIZER_MSGPACK: 120 | case SHMCACHE_SERIALIZER_PHP: 121 | smart_str_free(output->buf); 122 | break; 123 | default: 124 | break; 125 | } 126 | } 127 | 128 | int shmcache_unserialize(struct shmcache_value_info *value, zval *rv) 129 | { 130 | char buff[32]; 131 | int len; 132 | 133 | switch (value->options) { 134 | case SHMCACHE_SERIALIZER_STRING: 135 | ZEND_ZVAL_STRINGL(rv, value->data, value->length, 1); 136 | return 0; 137 | case SHMCACHE_SERIALIZER_INTEGER: 138 | if (value->length < (int)sizeof(buff)) { 139 | len = value->length; 140 | } else { 141 | len = sizeof(buff) - 1; 142 | } 143 | memcpy(buff, value->data, len); 144 | buff[len] = '\0'; 145 | ZVAL_LONG(rv, strtoll(buff, NULL, 10)); 146 | return 0; 147 | case SHMCACHE_SERIALIZER_IGBINARY: 148 | return shmcache_igbinary_unpack(value->data, value->length, rv); 149 | case SHMCACHE_SERIALIZER_MSGPACK: 150 | return shmcache_msgpack_unpack(value->data, value->length, rv); 151 | case SHMCACHE_SERIALIZER_PHP: 152 | return shmcache_php_unpack(value->data, value->length, rv); 153 | default: 154 | logError("file: "__FILE__", line: %d, " 155 | "invalid serializer: %d", 156 | __LINE__, value->options); 157 | return EINVAL; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /php-shmcache/serializer/shmcache_serializer.h: -------------------------------------------------------------------------------- 1 | #ifndef SHMCACHE_SERIALIZER_H 2 | #define SHMCACHE_SERIALIZER_H 3 | 4 | #include "php7_ext_wrapper.h" 5 | 6 | #if PHP_MAJOR_VERSION < 7 7 | #include "ext/standard/php_smart_str.h" 8 | #else 9 | #include "Zend/zend_smart_str.h" 10 | #endif 11 | 12 | #include "shmcache_types.h" 13 | 14 | struct shmcache_serialize_output { 15 | smart_str *buf; 16 | struct shmcache_value_info *value; 17 | char tmp[32]; //temp buffer for number convert 18 | }; 19 | 20 | typedef void (*php_msgpack_serialize_func)(smart_str *buf, zval *val); 21 | typedef void (*php_msgpack_unserialize_func)(zval *return_value, char *str, size_t str_len); 22 | 23 | /** Serialize zval. 24 | * * Return buffer is allocated by this function with emalloc. 25 | * @param[out] ret Return buffer 26 | * @param[out] ret_len Size of return buffer 27 | * @param[in] z Variable to be serialized 28 | * @return 0 on success, 1 elsewhere. 29 | */ 30 | typedef int (*igbinary_serialize_func)(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC); 31 | 32 | /** Unserialize to zval. 33 | * @param[in] buf Buffer with serialized data. 34 | * @param[in] buf_len Buffer length. 35 | * @param[out] z Unserialized zval 36 | * @return 0 on success, 1 elsewhere. 37 | */ 38 | #if (PHP_MAJOR_VERSION < 7) 39 | typedef int (*igbinary_unserialize_func)(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC); 40 | #else 41 | typedef int (*igbinary_unserialize_func)(const uint8_t *buf, size_t buf_len, zval *z TSRMLS_DC); 42 | #endif 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | extern php_msgpack_serialize_func msgpack_serialize_func; 49 | extern php_msgpack_unserialize_func msgpack_unserialize_func; 50 | extern igbinary_serialize_func igbinary_pack_func; 51 | extern igbinary_unserialize_func igbinary_unpack_func; 52 | extern int shmcache_serializers; 53 | 54 | static inline bool shmcache_serializer_enabled(const int serializer) 55 | { 56 | if (serializer == SHMCACHE_SERIALIZER_STRING) { 57 | return true; 58 | } 59 | return (shmcache_serializers & serializer) != 0; 60 | } 61 | 62 | int shmcache_load_functions(); 63 | 64 | int shmcache_serialize(zval *pzval, struct shmcache_serialize_output *output); 65 | 66 | void shmcache_free_serialize_output(struct shmcache_serialize_output *output); 67 | 68 | int shmcache_unserialize(struct shmcache_value_info *value, zval *rv); 69 | 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /php-shmcache/shmcache.ini: -------------------------------------------------------------------------------- 1 | extension = shmcache.so 2 | -------------------------------------------------------------------------------- /php-shmcache/test.php: -------------------------------------------------------------------------------- 1 | 'yuqing', 'score' => 90.5, 'city' => 'beijing', 'gender' => 'male'); 5 | //$value = json_encode($value, JSON_PRETTY_PRINT); 6 | 7 | $cache = new ShmCache('/etc/libshmcache.conf', ShmCache::SERIALIZER_IGBINARY); 8 | $cache->set($key, $value, 300); 9 | for ($i=0; $i<10240; $i++) { 10 | $cache->set($key, $value, 300); 11 | //$v = $cache->get($key); 12 | } 13 | 14 | echo "key $key expires: " . $cache->getExpires($key) . "\n"; 15 | var_dump($cache->setExpires($key, time() + 600)); 16 | echo "key $key expires: " . $cache->getExpires($key) . "\n"; 17 | 18 | var_dump($cache->setTTL($key, 900)); 19 | echo "key $key expires: " . $cache->getExpires($key) . "\n"; 20 | 21 | $key1 = 'key_00002'; 22 | var_dump($cache->incr($key1, -1, 300)); 23 | var_dump($cache->get($key)); 24 | var_dump($cache->delete($key)); 25 | 26 | //var_dump($cache->stats()); 27 | echo json_encode($cache->stats(), JSON_PRETTY_PRINT); 28 | //var_dump($cache->clear()); 29 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .c .o .lo 2 | 3 | COMPILE = $(CC) -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -g -O3 4 | INC_PATH = -I/usr/local/include 5 | LIB_PATH = -lm -lpthread -lfastcommon 6 | 7 | SHMCACHE_SHARED_OBJS = shmcache.lo shmopt.lo shm_striping_allocator.lo shm_object_pool.lo \ 8 | shm_hashtable.lo shm_value_allocator.lo shm_op_wrapper.lo shm_lock.lo \ 9 | shmcache_ini_annotation.lo 10 | 11 | SHMCACHE_STATIC_OBJS = shmcache.o shmopt.o shm_striping_allocator.o shm_object_pool.o \ 12 | shm_hashtable.o shm_value_allocator.o shm_op_wrapper.o shm_lock.o \ 13 | shmcache_ini_annotation.o 14 | 15 | HEADER_FILES = shmcache.h shmcache_types.h shm_list.h shm_striping_allocator.h \ 16 | shm_value_allocator.h shm_op_wrapper.h shmopt.h shm_hashtable.h \ 17 | shm_lock.h shmcache_ini_annotation.h 18 | 19 | ALL_OBJS = $(SHMCACHE_STATIC_OBJS) $(SHMCACHE_SHARED_OBJS) 20 | 21 | ALL_PRGS = 22 | SHARED_LIBS = libshmcache.so 23 | STATIC_LIBS = libshmcache.a 24 | ALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS) 25 | 26 | all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) 27 | libshmcache.so: 28 | $(COMPILE) -o $@ $< -shared $(SHMCACHE_SHARED_OBJS) $(LIB_PATH) 29 | libshmcache.a: $(SHMCACHE_STATIC_OBJS) 30 | ar rcs $@ $(SHMCACHE_STATIC_OBJS) 31 | .o: 32 | $(COMPILE) -o $@ $< $(SHMCACHE_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) 33 | .c: 34 | $(COMPILE) -o $@ $< $(SHMCACHE_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) 35 | .c.o: 36 | $(COMPILE) -c -o $@ $< $(INC_PATH) 37 | .c.lo: 38 | $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) 39 | install: 40 | mkdir -p $(DESTDIR)/usr/lib64 41 | mkdir -p $(DESTDIR)/usr/lib 42 | install -m 755 $(SHARED_LIBS) $(DESTDIR)/usr/lib64 43 | install -m 755 $(SHARED_LIBS) $(DESTDIR)/usr/lib 44 | mkdir -p $(DESTDIR)/usr/include/shmcache 45 | install -m 644 $(HEADER_FILES) $(DESTDIR)/usr/include/shmcache 46 | clean: 47 | rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) 48 | 49 | -------------------------------------------------------------------------------- /src/shm_hashtable.c: -------------------------------------------------------------------------------- 1 | //shm_hashtable.c 2 | 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "fastcommon/sched_thread.h" 8 | #include "shmopt.h" 9 | #include "shm_lock.h" 10 | #include "shm_object_pool.h" 11 | #include "shm_striping_allocator.h" 12 | #include "shm_hashtable.h" 13 | 14 | int shm_ht_get_capacity(const int max_count) 15 | { 16 | unsigned int *capacity; 17 | capacity = fc_hash_get_prime_capacity(max_count); 18 | if (capacity == NULL) { 19 | return max_count; 20 | } 21 | return *capacity; 22 | } 23 | 24 | void shm_ht_init(struct shmcache_context *context, const int capacity) 25 | { 26 | context->memory->hashtable.capacity = capacity; 27 | context->memory->hashtable.count = 0; 28 | } 29 | 30 | #define HT_GET_BUCKET_INDEX(context, key) \ 31 | ((unsigned int)context->config.hash_func(key->data, key->length) % \ 32 | context->memory->hashtable.capacity) 33 | 34 | #define HT_KEY_EQUALS(hentry, pkey) (hentry->key_len == pkey->length && \ 35 | memcmp(hentry->key, pkey->data, pkey->length) == 0) 36 | 37 | #define HT_VALUE_EQUALS(hvalue, hv_len, pvalue) (hv_len == pvalue->length \ 38 | && memcmp(hvalue, pvalue->data, pvalue->length) == 0) 39 | 40 | int shm_ht_set(struct shmcache_context *context, 41 | const struct shmcache_key_info *key, 42 | const struct shmcache_value_info *value) 43 | { 44 | int result; 45 | unsigned int index; 46 | int64_t old_offset; 47 | int64_t new_offset; 48 | struct shm_hash_entry *old_entry; 49 | struct shm_hash_entry *new_entry; 50 | struct shm_hash_entry *previous; 51 | char *hvalue; 52 | bool found; 53 | 54 | if (key->length > SHMCACHE_MAX_KEY_SIZE) { 55 | logError("file: "__FILE__", line: %d, " 56 | "invalid key length: %d exceeds %d", __LINE__, 57 | key->length, SHMCACHE_MAX_KEY_SIZE); 58 | return ENAMETOOLONG; 59 | } 60 | 61 | if (value->length > context->config.max_value_size) { 62 | logError("file: "__FILE__", line: %d, " 63 | "invalid value length: %d exceeds %d", __LINE__, 64 | value->length, context->config.max_value_size); 65 | return EINVAL; 66 | } 67 | 68 | if (context->memory->hashtable.count >= context->config.max_key_count) { 69 | if ((result=shm_value_allocator_recycle(context, &context->memory-> 70 | stats.memory.recycle.key, context-> 71 | config.recycle_key_once)) != 0) 72 | { 73 | return result; 74 | } 75 | } 76 | 77 | if (!g_schedule_flag) { 78 | g_current_time = time(NULL); 79 | } 80 | 81 | if ((new_entry=shm_value_allocator_alloc(context, 82 | key->length, value->length)) == NULL) 83 | { 84 | return ENOMEM; 85 | } 86 | 87 | previous = NULL; 88 | old_entry = NULL; 89 | found = false; 90 | index = HT_GET_BUCKET_INDEX(context, key); 91 | old_offset = context->memory->hashtable.buckets[index]; 92 | while (old_offset > 0) { 93 | old_entry = shm_get_hentry_ptr(context, old_offset); 94 | if (HT_KEY_EQUALS(old_entry, key)) { 95 | found = true; 96 | break; 97 | } 98 | 99 | old_offset = old_entry->ht_next; 100 | previous = old_entry; 101 | } 102 | 103 | if (found) { 104 | bool recycled = false; 105 | new_entry->ht_next = old_entry->ht_next; 106 | shm_ht_free_entry(context, old_entry, old_offset, &recycled); 107 | } else { 108 | new_entry->ht_next = 0; 109 | } 110 | 111 | new_offset = shm_get_hentry_offset(new_entry); 112 | memcpy(new_entry->key, key->data, key->length); 113 | new_entry->key_len = key->length; 114 | 115 | hvalue = shm_get_value_ptr(context, new_entry); 116 | memcpy(hvalue, value->data, value->length); 117 | new_entry->value.length = value->length; 118 | new_entry->value.options = value->options; 119 | new_entry->expires = value->expires; 120 | 121 | if (previous != NULL) { //add to tail 122 | previous->ht_next = new_offset; 123 | } else { 124 | context->memory->hashtable.buckets[index] = new_offset; 125 | } 126 | context->memory->hashtable.count++; 127 | context->memory->usage.used.value += value->length; 128 | context->memory->usage.used.key += new_entry->key_len; 129 | shm_list_add_tail(context, new_offset); 130 | 131 | return 0; 132 | } 133 | 134 | int shm_ht_get(struct shmcache_context *context, 135 | const struct shmcache_key_info *key, 136 | struct shmcache_value_info *value) 137 | { 138 | unsigned int index; 139 | int64_t entry_offset; 140 | struct shm_hash_entry *entry; 141 | 142 | index = HT_GET_BUCKET_INDEX(context, key); 143 | entry_offset = context->memory->hashtable.buckets[index]; 144 | while (entry_offset > 0) { 145 | entry = shm_get_hentry_ptr(context, entry_offset); 146 | if (HT_KEY_EQUALS(entry, key)) { 147 | value->data = shm_get_value_ptr(context, entry); 148 | value->length = entry->value.length; 149 | value->options = entry->value.options; 150 | value->expires = entry->expires; 151 | if (HT_ENTRY_IS_VALID(entry, get_current_time())) { 152 | return 0; 153 | } else { 154 | return ETIMEDOUT; 155 | } 156 | } 157 | 158 | entry_offset = entry->ht_next; 159 | } 160 | 161 | return ENOENT; 162 | } 163 | 164 | void shm_ht_free_entry(struct shmcache_context *context, 165 | struct shm_hash_entry *entry, const int64_t entry_offset, 166 | bool *recycled) 167 | { 168 | context->memory->hashtable.count--; 169 | context->memory->usage.used.value -= entry->value.length; 170 | context->memory->usage.used.key -= entry->key_len; 171 | shm_list_delete(context, entry_offset); 172 | shm_value_allocator_free(context, entry, recycled); 173 | entry->ht_next = 0; 174 | } 175 | 176 | int shm_ht_delete_ex(struct shmcache_context *context, 177 | const struct shmcache_key_info *key, bool *recycled) 178 | { 179 | int result; 180 | unsigned int index; 181 | int64_t entry_offset; 182 | struct shm_hash_entry *entry; 183 | struct shm_hash_entry *previous; 184 | 185 | previous = NULL; 186 | result = ENOENT; 187 | index = HT_GET_BUCKET_INDEX(context, key); 188 | entry_offset = context->memory->hashtable.buckets[index]; 189 | while (entry_offset > 0) { 190 | entry = shm_get_hentry_ptr(context, entry_offset); 191 | if (HT_KEY_EQUALS(entry, key)) { 192 | if (previous != NULL) { 193 | previous->ht_next = entry->ht_next; 194 | } else { 195 | context->memory->hashtable.buckets[index] = entry->ht_next; 196 | } 197 | 198 | shm_ht_free_entry(context, entry, entry_offset, recycled); 199 | result = 0; 200 | break; 201 | } 202 | 203 | entry_offset = entry->ht_next; 204 | previous = entry; 205 | } 206 | 207 | return result; 208 | } 209 | 210 | int shm_ht_set_expires(struct shmcache_context *context, 211 | const struct shmcache_key_info *key, const time_t expires) 212 | { 213 | int result; 214 | unsigned int index; 215 | int64_t entry_offset; 216 | struct shm_hash_entry *entry; 217 | 218 | result = ENOENT; 219 | index = HT_GET_BUCKET_INDEX(context, key); 220 | entry_offset = context->memory->hashtable.buckets[index]; 221 | while (entry_offset > 0) { 222 | entry = shm_get_hentry_ptr(context, entry_offset); 223 | if (HT_KEY_EQUALS(entry, key)) { 224 | if (HT_ENTRY_IS_VALID(entry, get_current_time())) { 225 | entry->expires = expires; 226 | result = 0; 227 | } else { 228 | result = ETIMEDOUT; 229 | } 230 | break; 231 | } 232 | 233 | entry_offset = entry->ht_next; 234 | } 235 | 236 | return result; 237 | } 238 | 239 | int shm_ht_clear(struct shmcache_context *context) 240 | { 241 | struct shm_striping_allocator *allocator; 242 | struct shm_striping_allocator *end; 243 | int64_t allocator_offset; 244 | int ht_count; 245 | 246 | context->memory->stats.hashtable.last_clear_time = 247 | context->memory->stats.last.calc_time = get_current_time(); 248 | ht_count = context->memory->hashtable.count; 249 | memset(context->memory->hashtable.buckets, 0, sizeof(int64_t) * 250 | context->memory->hashtable.capacity); 251 | context->memory->hashtable.count = 0; 252 | shm_list_init(context); 253 | 254 | shm_object_pool_init_empty(&context->value_allocator.doing); 255 | shm_object_pool_init_empty(&context->value_allocator.done); 256 | end = context->value_allocator.allocators + 257 | context->memory->vm_info.striping.count.current; 258 | for (allocator=context->value_allocator.allocators; allocatorin_which_pool = SHMCACHE_STRIPING_ALLOCATOR_POOL_DOING; 261 | allocator_offset = (char *)allocator - context->segments.hashtable.base; 262 | shm_object_pool_push(&context->value_allocator.doing, allocator_offset); 263 | } 264 | context->memory->usage.used.key = 0; 265 | context->memory->usage.used.value = 0; 266 | context->memory->usage.used.entry = 0; 267 | 268 | logInfo("file: "__FILE__", line: %d, pid: %d, " 269 | "clear hashtable, %d entries be cleared!", 270 | __LINE__, context->pid, ht_count); 271 | return ht_count; 272 | } 273 | 274 | static bool shm_ht_match_key(struct shmcache_match_key_info *key_info, 275 | struct shm_hash_entry *entry) 276 | { 277 | int loop; 278 | int start; 279 | 280 | if (entry->key_len < key_info->length) { 281 | return false; 282 | } 283 | switch (key_info->op_type) { 284 | case SHMCACHE_MATCH_KEY_OP_EXACT: 285 | return key_info->length == entry->key_len && 286 | memcmp(key_info->key, entry->key, key_info->length) == 0; 287 | case SHMCACHE_MATCH_KEY_OP_LEFT: 288 | return memcmp(key_info->key, entry->key, key_info->length) == 0; 289 | case SHMCACHE_MATCH_KEY_OP_RIGHT: 290 | return memcmp(key_info->key, entry->key + 291 | (entry->key_len - key_info->length), key_info->length) == 0; 292 | case SHMCACHE_MATCH_KEY_OP_ANYWHERE: 293 | loop = entry->key_len - key_info->length + 1; 294 | for (start=0; startkey, entry->key + start, 296 | key_info->length) == 0) 297 | { 298 | return true; 299 | } 300 | } 301 | return false; 302 | default: 303 | return false; 304 | } 305 | } 306 | 307 | #define HT_KEY_MATCHED(key_info, entry) \ 308 | (key_info == NULL || shm_ht_match_key(key_info, entry)) 309 | 310 | int shm_ht_to_array_ex(struct shmcache_context *context, 311 | struct shmcache_hentry_array *array, 312 | struct shmcache_match_key_info *key_info, 313 | const int offset, const int count) 314 | { 315 | int64_t entry_offset; 316 | struct shm_hash_entry *src; 317 | struct shmcache_hash_entry *dest; 318 | char *value_data; 319 | int row_count; 320 | int i; 321 | int bytes; 322 | int current_time; 323 | 324 | if (offset < 0) { 325 | logError("file: "__FILE__", line: %d, " 326 | "invalid offset: %d", __LINE__, offset); 327 | array->count = 0; 328 | array->entries = NULL; 329 | return EINVAL; 330 | } 331 | 332 | row_count = context->memory->hashtable.count - offset; 333 | if (count > 0 && count < row_count) { 334 | row_count = count; 335 | } 336 | 337 | if (row_count <= 0) { 338 | array->count = 0; 339 | array->entries = NULL; 340 | return 0; 341 | } 342 | 343 | bytes = sizeof(struct shmcache_hash_entry) * row_count; 344 | array->entries = (struct shmcache_hash_entry *)malloc(bytes); 345 | if (array->entries == NULL) { 346 | logError("file: "__FILE__", line: %d, " 347 | "malloc %d bytes fail", __LINE__, bytes); 348 | array->count = 0; 349 | return ENOMEM; 350 | } 351 | 352 | current_time = time(NULL); 353 | i = 0; 354 | entry_offset = shm_list_first(context); 355 | while (entry_offset > 0 && i < offset) { 356 | src = shm_get_hentry_ptr(context, entry_offset); 357 | if (HT_ENTRY_IS_VALID(src, current_time) && 358 | HT_KEY_MATCHED(key_info, src)) 359 | { 360 | ++i; 361 | } 362 | entry_offset = shm_list_next(context, entry_offset); 363 | } 364 | 365 | memset(array->entries, 0, bytes); 366 | array->count = 0; 367 | dest = array->entries; 368 | 369 | while (entry_offset > 0) { 370 | src = shm_get_hentry_ptr(context, entry_offset); 371 | if (!(HT_ENTRY_IS_VALID(src, current_time) && 372 | HT_KEY_MATCHED(key_info, src))) 373 | { 374 | entry_offset = shm_list_next(context, entry_offset); 375 | continue; 376 | } 377 | 378 | bytes = src->key_len + src->value.length; 379 | dest->key.data = (char *)malloc(bytes); 380 | if (dest->key.data == NULL) { 381 | logError("file: "__FILE__", line: %d, " 382 | "malloc %d bytes fail", __LINE__, bytes); 383 | shm_ht_free_array(array); 384 | return ENOMEM; 385 | } 386 | 387 | dest->key.length = src->key_len; 388 | memcpy(dest->key.data, src->key, src->key_len); 389 | 390 | value_data = shm_get_value_ptr(context, src); 391 | dest->value.length = src->value.length; 392 | dest->value.options = src->value.options; 393 | dest->value.expires = src->expires; 394 | 395 | dest->value.data = dest->key.data + src->key_len; 396 | memcpy(dest->value.data, value_data, src->value.length); 397 | 398 | entry_offset = shm_list_next(context, entry_offset); 399 | dest++; 400 | array->count++; 401 | if (array->count == row_count) { 402 | break; 403 | } 404 | } 405 | 406 | return 0; 407 | } 408 | 409 | void shm_ht_free_array(struct shmcache_hentry_array *array) 410 | { 411 | struct shmcache_hash_entry *entry; 412 | struct shmcache_hash_entry *end; 413 | 414 | if (array->entries == NULL) { 415 | return; 416 | } 417 | 418 | end = array->entries + array->count; 419 | for (entry=array->entries; entrykey.data); 421 | } 422 | 423 | free(array->entries); 424 | array->entries = NULL; 425 | array->count = 0; 426 | } 427 | -------------------------------------------------------------------------------- /src/shm_hashtable.h: -------------------------------------------------------------------------------- 1 | //shm_hashtable.h 2 | 3 | #ifndef _SHM_HASH_TABLE_H 4 | #define _SHM_HASH_TABLE_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fastcommon/common_define.h" 13 | #include "shmcache_types.h" 14 | #include "shm_list.h" 15 | #include "shm_value_allocator.h" 16 | 17 | #define HT_CALC_EXPIRES(current_time, ttl) \ 18 | (ttl == SHMCACHE_NEVER_EXPIRED ? 0 : current_time + ttl) 19 | 20 | #define HT_ENTRY_IS_VALID(entry, current_time) \ 21 | (entry->expires == 0 || entry->expires >= current_time) 22 | 23 | #define SHMCACHE_MATCH_KEY_OP_EXACT 0 24 | #define SHMCACHE_MATCH_KEY_OP_LEFT 1 25 | #define SHMCACHE_MATCH_KEY_OP_RIGHT 2 26 | #define SHMCACHE_MATCH_KEY_OP_ANYWHERE \ 27 | (SHMCACHE_MATCH_KEY_OP_LEFT | SHMCACHE_MATCH_KEY_OP_RIGHT) 28 | 29 | struct shmcache_match_key_info { 30 | char *key; 31 | int length; 32 | int op_type; 33 | }; 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | static inline int64_t shm_ht_get_memory_size(const int capacity) 40 | { 41 | return sizeof(int64_t) * (int64_t)capacity; 42 | } 43 | 44 | /** 45 | get hashtable capacity 46 | parameters: 47 | max_count: max entry counti 48 | return hashtable capacity 49 | */ 50 | int shm_ht_get_capacity(const int max_count); 51 | 52 | /** 53 | ht init 54 | parameters: 55 | context: the context pointer 56 | capacity: the ht capacity 57 | return none 58 | */ 59 | void shm_ht_init(struct shmcache_context *context, const int capacity); 60 | 61 | /** 62 | set value 63 | parameters: 64 | context: the context pointer 65 | key: the key 66 | value: the value, include expires field 67 | return error no, 0 for success, != 0 for fail 68 | */ 69 | int shm_ht_set(struct shmcache_context *context, 70 | const struct shmcache_key_info *key, 71 | const struct shmcache_value_info *value); 72 | 73 | /** 74 | set expires 75 | parameters: 76 | context: the context pointer 77 | key: the key 78 | value: the expires timestamp 79 | return error no, 0 for success, != 0 for fail 80 | */ 81 | int shm_ht_set_expires(struct shmcache_context *context, 82 | const struct shmcache_key_info *key, const time_t expires); 83 | 84 | /** 85 | get value 86 | parameters: 87 | context: the context pointer 88 | key: the key 89 | value: store the returned value 90 | return error no, 0 for success, != 0 for fail 91 | */ 92 | int shm_ht_get(struct shmcache_context *context, 93 | const struct shmcache_key_info *key, 94 | struct shmcache_value_info *value); 95 | 96 | /** 97 | delete the key for internal usage 98 | parameters: 99 | context: the context pointer 100 | key: the key 101 | recycled: if recycled 102 | return error no, 0 for success, != 0 for fail 103 | */ 104 | int shm_ht_delete_ex(struct shmcache_context *context, 105 | const struct shmcache_key_info *key, bool *recycled); 106 | 107 | /** 108 | hashtable entry to array 109 | parameters: 110 | context: the context pointer 111 | array: the array, should call shm_ht_free_array after use 112 | key_info: the match key info, NULL for match all 113 | offset: start offset, based 0 114 | count: row count limit 115 | return error no, 0 for success, != 0 for fail 116 | */ 117 | int shm_ht_to_array_ex(struct shmcache_context *context, 118 | struct shmcache_hentry_array *array, 119 | struct shmcache_match_key_info *key_info, 120 | const int offset, const int count); 121 | 122 | /** 123 | hashtable entry to array 124 | parameters: 125 | context: the context pointer 126 | array: the array, should call shm_ht_free_array after use 127 | return error no, 0 for success, != 0 for fail 128 | */ 129 | static inline int shm_ht_to_array(struct shmcache_context *context, 130 | struct shmcache_hentry_array *array) 131 | { 132 | return shm_ht_to_array_ex(context, array, NULL, 0, 0); 133 | } 134 | 135 | /** 136 | free the array return by shm_ht_to_array 137 | parameters: 138 | context: the context pointer 139 | array: the array to free 140 | return none 141 | */ 142 | void shm_ht_free_array(struct shmcache_hentry_array *array); 143 | 144 | 145 | /** 146 | delete the key 147 | parameters: 148 | context: the context pointer 149 | key: the key 150 | return error no, 0 for success, != 0 for fail 151 | */ 152 | static inline int shm_ht_delete(struct shmcache_context *context, 153 | const struct shmcache_key_info *key) 154 | { 155 | bool recycled; 156 | return shm_ht_delete_ex(context, key, &recycled); 157 | } 158 | 159 | /** 160 | free hashtable entry 161 | parameters: 162 | context: the context pointer 163 | entry: the hashtable entry 164 | entry_offset: the entry offset 165 | recycled: if recycled 166 | return none 167 | */ 168 | void shm_ht_free_entry(struct shmcache_context *context, 169 | struct shm_hash_entry *entry, const int64_t entry_offset, 170 | bool *recycled); 171 | 172 | /** 173 | remove all hashtable entries 174 | parameters: 175 | context: the context pointer 176 | return cleared entries count 177 | */ 178 | int shm_ht_clear(struct shmcache_context *context); 179 | 180 | static inline int shm_ht_count(struct shmcache_context *context) 181 | { 182 | return context->memory->hashtable.count; 183 | } 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #endif 190 | 191 | -------------------------------------------------------------------------------- /src/shm_list.h: -------------------------------------------------------------------------------- 1 | //shm_list.h 2 | 3 | #ifndef _SHM_LIST_H 4 | #define _SHM_LIST_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "fastcommon/logger.h" 12 | #include "shmcache_types.h" 13 | #include "shm_value_allocator.h" 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | static inline struct shm_list *shm_list_ptr(struct shmcache_context *context, 20 | int64_t obj_offset) 21 | { 22 | if (obj_offset == context->list.head.offset) { 23 | return context->list.head.ptr; 24 | } else { 25 | return (struct shm_list *)shm_get_hentry_ptr(context, obj_offset); 26 | } 27 | } 28 | 29 | #define SHM_LIST_TYPE_PTR(context, type, offset) ((type *)shm_list_ptr(context, offset)) 30 | 31 | /** 32 | list set 33 | parameters: 34 | list: the list 35 | return none 36 | */ 37 | static inline void shm_list_set(struct shmcache_context *context, 38 | char *base, struct shm_list *head) 39 | { 40 | union shm_hentry_offset conv; 41 | conv.segment.index = -1; 42 | conv.segment.offset = (char *)head - base; 43 | 44 | context->list.head.ptr = head; 45 | context->list.head.offset = conv.offset; 46 | } 47 | 48 | /** 49 | init list to empty 50 | parameters: 51 | list: the list 52 | return none 53 | */ 54 | static inline void shm_list_init(struct shmcache_context *context) 55 | { 56 | context->list.head.ptr->prev = context->list.head.offset; 57 | context->list.head.ptr->next = context->list.head.offset; 58 | } 59 | 60 | #define SHM_LIST_ADD_TO_TAIL(context, node, obj_offset) \ 61 | do { \ 62 | node->next = context->list.head.offset; \ 63 | node->prev = context->list.head.ptr->prev; \ 64 | shm_list_ptr(context, context->list.head.ptr->prev)->next = obj_offset; \ 65 | context->list.head.ptr->prev = obj_offset; \ 66 | } while (0) 67 | 68 | /** 69 | add an element to list tail 70 | parameters: 71 | list: the list 72 | obj_offset: the object offset 73 | return none 74 | */ 75 | static inline void shm_list_add_tail(struct shmcache_context *context, int64_t obj_offset) 76 | { 77 | struct shm_list *node; 78 | 79 | node = shm_list_ptr(context, obj_offset); 80 | SHM_LIST_ADD_TO_TAIL(context, node, obj_offset); 81 | } 82 | 83 | /** 84 | remove an element from list 85 | parameters: 86 | list: the list 87 | obj_offset: the object offset 88 | return none 89 | */ 90 | static inline void shm_list_delete(struct shmcache_context *context, int64_t obj_offset) 91 | { 92 | struct shm_list *node; 93 | 94 | node = shm_list_ptr(context, obj_offset); 95 | if (node->next == obj_offset) { 96 | logError("file: " __FILE__", line: %d, " 97 | "do NOT need remove from list, obj: %" PRId64, 98 | __LINE__, obj_offset); 99 | return; 100 | } 101 | 102 | shm_list_ptr(context, node->prev)->next = node->next; 103 | shm_list_ptr(context, node->next)->prev = node->prev; 104 | node->prev = node->next = obj_offset; 105 | } 106 | 107 | /** 108 | move an element to tail 109 | parameters: 110 | list: the list 111 | obj_offset: the object offset 112 | return none 113 | */ 114 | static inline void shm_list_move_tail(struct shmcache_context *context, int64_t obj_offset) 115 | { 116 | struct shm_list *node; 117 | 118 | node = shm_list_ptr(context, obj_offset); 119 | shm_list_ptr(context, node->prev)->next = node->next; 120 | shm_list_ptr(context, node->next)->prev = node->prev; 121 | 122 | SHM_LIST_ADD_TO_TAIL(context, node, obj_offset); 123 | } 124 | 125 | /** 126 | is empty 127 | parameters: 128 | list: the list 129 | return true for empty, false for NOT empty 130 | */ 131 | static inline bool shm_list_empty(struct shmcache_context *context) 132 | { 133 | return (context->list.head.ptr->next == context->list.head.offset); 134 | } 135 | 136 | /** 137 | get first element 138 | parameters: 139 | list: the list 140 | return first object offset 141 | */ 142 | static inline int64_t shm_list_first(struct shmcache_context *context) 143 | { 144 | if (context->list.head.ptr->next == context->list.head.offset) { //empty 145 | return -1; 146 | } 147 | 148 | return context->list.head.ptr->next; 149 | } 150 | 151 | /** 152 | get next element 153 | parameters: 154 | list: the list 155 | return next object offset 156 | */ 157 | static inline int64_t shm_list_next(struct shmcache_context *context, 158 | const int64_t current_offset) 159 | { 160 | struct shm_list *node; 161 | node = shm_list_ptr(context, current_offset); 162 | if (node->next == context->list.head.offset) { 163 | return -1; 164 | } 165 | return node->next; 166 | } 167 | 168 | /** 169 | get count 170 | parameters: 171 | list: the list 172 | return count 173 | */ 174 | static inline int shm_list_count(struct shmcache_context *context) 175 | { 176 | int64_t offset; 177 | int count; 178 | 179 | count = 0; 180 | offset = context->list.head.ptr->next; 181 | while (offset != context->list.head.offset) { 182 | count++; 183 | offset = shm_list_ptr(context, offset)->next; 184 | } 185 | 186 | return count; 187 | } 188 | 189 | #define SHM_LIST_FOR_EACH(context, current, member) \ 190 | for (current=SHM_LIST_TYPE_PTR(context, typeof(*current), \ 191 | context->list.head.ptr->next); \ 192 | ¤t->member != context->list.head.ptr; \ 193 | current=SHM_LIST_TYPE_PTR(context, typeof(*current), \ 194 | current->member.next)) 195 | 196 | #ifdef __cplusplus 197 | } 198 | #endif 199 | 200 | #endif 201 | 202 | -------------------------------------------------------------------------------- /src/shm_lock.c: -------------------------------------------------------------------------------- 1 | //shm_lock.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "fastcommon/logger.h" 8 | #include "fastcommon/shared_func.h" 9 | #include "fastcommon/sched_thread.h" 10 | #include "shm_hashtable.h" 11 | #include "shm_lock.h" 12 | 13 | int shm_lock_init(struct shmcache_context *context) 14 | { 15 | pthread_mutexattr_t mat; 16 | int result; 17 | 18 | if ((result=pthread_mutexattr_init(&mat)) != 0) { 19 | logError("file: "__FILE__", line: %d, " 20 | "call pthread_mutexattr_init fail, " 21 | "errno: %d, error info: %s", 22 | __LINE__, result, strerror(result)); 23 | return result; 24 | } 25 | if ((result=pthread_mutexattr_setpshared(&mat, 26 | PTHREAD_PROCESS_SHARED)) != 0) 27 | { 28 | logError("file: "__FILE__", line: %d, " 29 | "call pthread_mutexattr_setpshared fail, " 30 | "errno: %d, error info: %s", 31 | __LINE__, result, strerror(result)); 32 | return result; 33 | } 34 | if ((result=pthread_mutexattr_settype(&mat, 35 | PTHREAD_MUTEX_NORMAL)) != 0) 36 | { 37 | logError("file: "__FILE__", line: %d, " 38 | "call pthread_mutexattr_settype fail, " 39 | "errno: %d, error info: %s", 40 | __LINE__, result, strerror(result)); 41 | return result; 42 | } 43 | if ((result=pthread_mutex_init(&context->memory->lock.mutex, 44 | &mat)) != 0) 45 | { 46 | logError("file: "__FILE__", line: %d, " 47 | "call pthread_mutex_init fail, " 48 | "errno: %d, error info: %s", 49 | __LINE__, result, strerror(result)); 50 | return result; 51 | } 52 | pthread_mutexattr_destroy(&mat); 53 | 54 | context->memory->lock.pid = 0; 55 | return 0; 56 | } 57 | 58 | int shm_lock_file(struct shmcache_context *context) 59 | { 60 | int result; 61 | mode_t old_mast; 62 | 63 | if (context->lock_fd > 0) { 64 | close(context->lock_fd); 65 | } 66 | 67 | old_mast = umask(0); 68 | context->lock_fd = open(context->config.filename, O_WRONLY | O_CREAT, 0666); 69 | umask(old_mast); 70 | if (context->lock_fd < 0) { 71 | result = errno != 0 ? errno : EPERM; 72 | logError("file: "__FILE__", line: %d, " 73 | "open filename: %s fail, " 74 | "errno: %d, error info: %s", __LINE__, 75 | context->config.filename, result, strerror(result)); 76 | return result; 77 | } 78 | 79 | if ((result=file_write_lock(context->lock_fd)) != 0) { 80 | close(context->lock_fd); 81 | context->lock_fd = -1; 82 | logError("file: "__FILE__", line: %d, " 83 | "lock filename: %s fail, " 84 | "errno: %d, error info: %s", __LINE__, 85 | context->config.filename, result, strerror(result)); 86 | return result; 87 | } 88 | 89 | return result; 90 | } 91 | 92 | void shm_unlock_file(struct shmcache_context *context) 93 | { 94 | close(context->lock_fd); 95 | context->lock_fd = -1; 96 | } 97 | 98 | static int shm_detect_deadlock(struct shmcache_context *context, 99 | const pid_t last_pid) 100 | { 101 | int result; 102 | if ((result=shm_lock_file(context)) != 0) { 103 | return result; 104 | } 105 | 106 | if (last_pid == context->memory->lock.pid) { 107 | context->memory->lock.pid = 0; 108 | if (shm_ht_clear(context) > 0) { 109 | if (context->config.va_policy.sleep_us_when_recycle_valid_entries > 0) { 110 | usleep(context->config.va_policy.sleep_us_when_recycle_valid_entries); 111 | } 112 | } 113 | if ((result=pthread_mutex_unlock(&context->memory->lock.mutex)) != 0) { 114 | logError("file: "__FILE__", line: %d, " 115 | "call pthread_mutex_unlock fail, " 116 | "errno: %d, error info: %s", 117 | __LINE__, result, strerror(result)); 118 | } else { 119 | __sync_add_and_fetch(&context->memory->stats. 120 | lock.unlock_deadlock, 1); 121 | context->memory->stats.lock. 122 | last_unlock_deadlock_time = get_current_time(); 123 | logInfo("file: "__FILE__", line: %d, " 124 | "my pid: %d, unlock deadlock by process: %d", 125 | __LINE__, context->pid, last_pid); 126 | } 127 | } 128 | 129 | shm_unlock_file(context); 130 | return 0; 131 | } 132 | 133 | int shm_lock(struct shmcache_context *context) 134 | { 135 | int result; 136 | pid_t pid; 137 | int clocks; 138 | 139 | __sync_add_and_fetch(&context->memory->stats.lock.total, 1); 140 | clocks = 0; 141 | while ((result=pthread_mutex_trylock(&context->memory->lock.mutex)) == EBUSY) { 142 | __sync_add_and_fetch(&context->memory->stats.lock.retry, 1); 143 | usleep(context->config.lock_policy.trylock_interval_us); 144 | ++clocks; 145 | if (clocks > context->detect_deadlock_clocks && 146 | (pid=context->memory->lock.pid) > 0) 147 | { 148 | clocks = 0; 149 | if (kill(pid, 0) != 0) { 150 | if (errno == ESRCH || errno == ENOENT) { 151 | __sync_add_and_fetch(&context->memory->stats. 152 | lock.detect_deadlock, 1); 153 | context->memory->stats.lock. 154 | last_detect_deadlock_time = get_current_time(); 155 | shm_detect_deadlock(context, pid); 156 | } 157 | } 158 | } 159 | } 160 | if (result == 0) { 161 | context->memory->lock.pid = context->pid; 162 | } else { 163 | logError("file: "__FILE__", line: %d, " 164 | "call pthread_mutex_trylock fail, " 165 | "errno: %d, error info: %s", 166 | __LINE__, result, strerror(result)); 167 | } 168 | return result; 169 | } 170 | 171 | int shm_unlock(struct shmcache_context *context) 172 | { 173 | int result; 174 | 175 | context->memory->lock.pid = 0; 176 | if ((result=pthread_mutex_unlock(&context->memory->lock.mutex)) != 0) { 177 | logError("file: "__FILE__", line: %d, " 178 | "call pthread_mutex_unlock fail, " 179 | "errno: %d, error info: %s", 180 | __LINE__, result, strerror(result)); 181 | } 182 | return result; 183 | } 184 | 185 | -------------------------------------------------------------------------------- /src/shm_lock.h: -------------------------------------------------------------------------------- 1 | //shm_lock.h 2 | 3 | #ifndef _SHM_LOCK_H 4 | #define _SHM_LOCK_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | lock init 19 | parameters: 20 | context: the context pointer 21 | return errno, 0 for success, != 0 fail 22 | */ 23 | int shm_lock_init(struct shmcache_context *context); 24 | 25 | /** 26 | lock 27 | parameters: 28 | context: the context pointer 29 | return errno, 0 for success, != 0 fail 30 | */ 31 | int shm_lock(struct shmcache_context *context); 32 | 33 | /** 34 | unlock 35 | parameters: 36 | context: the context pointer 37 | return errno, 0 for success, != 0 fail 38 | */ 39 | int shm_unlock(struct shmcache_context *context); 40 | 41 | /** 42 | lock file 43 | parameters: 44 | context: the context pointer 45 | return errno, 0 for success, != 0 fail 46 | */ 47 | int shm_lock_file(struct shmcache_context *context); 48 | 49 | /** 50 | unlock file 51 | parameters: 52 | context: the context pointer 53 | return none 54 | */ 55 | void shm_unlock_file(struct shmcache_context *context); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif 62 | 63 | -------------------------------------------------------------------------------- /src/shm_object_pool.c: -------------------------------------------------------------------------------- 1 | //shm_object_pool.c 2 | 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "shm_object_pool.h" 7 | 8 | void shm_object_pool_set(struct shmcache_object_pool_context *op, 9 | struct shm_object_pool_info *obj_pool_info, 10 | int64_t *offsets) 11 | { 12 | op->obj_pool_info = obj_pool_info; 13 | op->offsets = offsets; 14 | op->index = -1; 15 | } 16 | 17 | void shm_object_pool_init_full(struct shmcache_object_pool_context *op) 18 | { 19 | int64_t *p; 20 | int64_t *end; 21 | int64_t offset; 22 | 23 | end = op->offsets + (op->obj_pool_info->queue.capacity - 1); 24 | offset = op->obj_pool_info->object.base_offset; 25 | for (p=op->offsets; pobj_pool_info->object.element_size; 28 | } 29 | 30 | op->obj_pool_info->queue.head = 0; 31 | op->obj_pool_info->queue.tail = op->obj_pool_info->queue.capacity - 1; 32 | } 33 | 34 | void shm_object_pool_init_empty(struct shmcache_object_pool_context *op) 35 | { 36 | op->obj_pool_info->queue.head = op->obj_pool_info->queue.tail = 0; 37 | } 38 | 39 | int shm_object_pool_get_count(struct shmcache_object_pool_context *op) 40 | { 41 | if (op->obj_pool_info->queue.head == op->obj_pool_info->queue.tail) { 42 | return 0; 43 | } else if (op->obj_pool_info->queue.head < op->obj_pool_info->queue.tail) { 44 | return op->obj_pool_info->queue.tail - op->obj_pool_info->queue.head; 45 | } else { 46 | return (op->obj_pool_info->queue.capacity - op->obj_pool_info->queue.head) 47 | + op->obj_pool_info->queue.tail; 48 | } 49 | } 50 | 51 | int64_t shm_object_pool_alloc(struct shmcache_object_pool_context *op) 52 | { 53 | int64_t obj_offset; 54 | if (op->obj_pool_info->queue.head == op->obj_pool_info->queue.tail) { 55 | return -1; 56 | } 57 | 58 | obj_offset = op->offsets[op->obj_pool_info->queue.head]; 59 | op->obj_pool_info->queue.head = (op->obj_pool_info->queue.head + 1) % 60 | op->obj_pool_info->queue.capacity; 61 | 62 | return obj_offset; 63 | } 64 | 65 | int shm_object_pool_free(struct shmcache_object_pool_context *op, 66 | const int64_t obj_offset) 67 | { 68 | int next_tail; 69 | next_tail = (op->obj_pool_info->queue.tail + 1) % 70 | op->obj_pool_info->queue.capacity; 71 | if (next_tail == op->obj_pool_info->queue.head) { 72 | return ENOSPC; 73 | } 74 | op->offsets[op->obj_pool_info->queue.tail] = obj_offset; 75 | op->obj_pool_info->queue.tail = next_tail; 76 | 77 | return 0; 78 | } 79 | 80 | #define SHM_OP_DEC_INDEX(index, capacity) \ 81 | (index == 0 ? (capacity - 1) : (index - 1)) 82 | 83 | int64_t shm_object_pool_remove(struct shmcache_object_pool_context *op) 84 | { 85 | int index; 86 | int previous; 87 | int current; 88 | 89 | if (op->obj_pool_info->queue.head == op->obj_pool_info->queue.tail) { 90 | logError("file: "__FILE__", line: %d, " 91 | "object pool is empty", __LINE__); 92 | return -1; 93 | } 94 | if (op->index < 0) { 95 | logError("file: "__FILE__", line: %d, " 96 | "current index: %d < 0", __LINE__, op->index); 97 | return -1; 98 | } 99 | 100 | index = op->index; 101 | current = index; 102 | while (current != op->obj_pool_info->queue.head) { 103 | previous = SHM_OP_DEC_INDEX(current, op->obj_pool_info->queue.capacity); 104 | op->offsets[current] = op->offsets[previous]; 105 | current = previous; 106 | } 107 | 108 | op->obj_pool_info->queue.head = (op->obj_pool_info->queue.head + 1) % 109 | op->obj_pool_info->queue.capacity; 110 | return op->offsets[index]; 111 | } 112 | 113 | int64_t shm_object_pool_remove_by(struct shmcache_object_pool_context *op, 114 | const int64_t obj_offset) 115 | { 116 | int64_t current_offset; 117 | 118 | current_offset = shm_object_pool_first(op); 119 | while (current_offset > 0) { 120 | if (current_offset == obj_offset) { 121 | return shm_object_pool_remove(op); 122 | } 123 | current_offset = shm_object_pool_next(op); 124 | } 125 | 126 | return -1; 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/shm_object_pool.h: -------------------------------------------------------------------------------- 1 | //shm_object_pool.h 2 | 3 | #ifndef _SHM_OBJECT_POOL_H 4 | #define _SHM_OBJECT_POOL_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | get object pool memory size for queue 19 | parameters: 20 | max_count: the max count 21 | return memory size 22 | */ 23 | static inline int64_t shm_object_pool_get_queue_memory_size(const int max_count) 24 | { 25 | return sizeof(int64_t) * (int64_t)max_count; 26 | } 27 | 28 | /** 29 | get object pool memory size for object 30 | parameters: 31 | element_size: the element size 32 | max_count: the max count 33 | return memory size 34 | */ 35 | static inline int64_t shm_object_pool_get_object_memory_size( 36 | const int element_size, const int max_count) 37 | { 38 | return (int64_t)element_size * (int64_t)max_count; 39 | } 40 | 41 | /** 42 | set object pool 43 | parameters: 44 | op: the object pool 45 | obj_pool_info: fifo pool in share memory 46 | offsets: the array offsets 47 | return error no, 0 for success, != 0 fail 48 | */ 49 | void shm_object_pool_set(struct shmcache_object_pool_context *op, 50 | struct shm_object_pool_info *obj_pool_info, 51 | int64_t *offsets); 52 | 53 | /** 54 | init object pool to empty queue 55 | parameters: 56 | op: the object pool 57 | return error no, 0 for success, != 0 fail 58 | */ 59 | void shm_object_pool_init_empty(struct shmcache_object_pool_context *op); 60 | 61 | /** 62 | init object pool to full queue 63 | parameters: 64 | op: the object pool 65 | return error no, 0 for success, != 0 fail 66 | */ 67 | void shm_object_pool_init_full(struct shmcache_object_pool_context *op); 68 | 69 | /** 70 | get object count in object pool 71 | parameters: 72 | op: the object pool 73 | return object count 74 | */ 75 | int shm_object_pool_get_count(struct shmcache_object_pool_context *op); 76 | 77 | /** 78 | alloc a node from the object pool 79 | parameters: 80 | op: the object pool 81 | return the alloced object offset, return -1 if fail 82 | */ 83 | int64_t shm_object_pool_alloc(struct shmcache_object_pool_context *op); 84 | 85 | #define shm_object_pool_pop(op) shm_object_pool_alloc(op) 86 | 87 | /** 88 | free a node to the object pool 89 | parameters: 90 | op: the object pool 91 | obj_offset: the object offset 92 | return 0 for success, != 0 fail 93 | */ 94 | int shm_object_pool_free(struct shmcache_object_pool_context *op, 95 | const int64_t obj_offset); 96 | 97 | #define shm_object_pool_push(op, obj_offset) shm_object_pool_free(op, obj_offset) 98 | 99 | /** 100 | is object pool empty 101 | parameters: 102 | op: the object pool 103 | return return true if empty, otherwise false 104 | */ 105 | static inline bool shm_object_pool_is_empty(struct shmcache_object_pool_context *op) 106 | { 107 | return (op->obj_pool_info->queue.head == op->obj_pool_info->queue.tail); 108 | } 109 | 110 | /** 111 | get first object 112 | parameters: 113 | op: the object pool 114 | return object offset, return -1 if empty 115 | */ 116 | static inline int64_t shm_object_pool_first(struct shmcache_object_pool_context *op) 117 | { 118 | if (op->obj_pool_info->queue.head == op->obj_pool_info->queue.tail) { 119 | op->index = -1; 120 | return -1; 121 | } 122 | op->index = op->obj_pool_info->queue.head; 123 | return op->offsets[op->index]; 124 | } 125 | 126 | /** 127 | get next object 128 | parameters: 129 | op: the object pool 130 | return object offset, return -1 if empty 131 | */ 132 | static inline int64_t shm_object_pool_next(struct shmcache_object_pool_context *op) 133 | { 134 | if (op->index == -1) { 135 | return -1; 136 | } 137 | if (op->index == op->obj_pool_info->queue.tail) { 138 | op->index = -1; 139 | return -1; 140 | } 141 | 142 | op->index = (op->index + 1) % op->obj_pool_info->queue.capacity; 143 | if (op->index != op->obj_pool_info->queue.tail) { 144 | return op->offsets[op->index]; 145 | } else { 146 | return -1; 147 | } 148 | } 149 | 150 | /** 151 | remove the object, if op->index is -1, remove the tail 152 | parameters: 153 | op: the object pool 154 | return the removed object offset, return -1 if empty 155 | */ 156 | int64_t shm_object_pool_remove(struct shmcache_object_pool_context *op); 157 | 158 | /** 159 | remove the object 160 | parameters: 161 | op: the object pool 162 | obj_offset: the object offset 163 | return the removed object offset, return -1 if empty 164 | */ 165 | int64_t shm_object_pool_remove_by(struct shmcache_object_pool_context *op, 166 | const int64_t obj_offset); 167 | 168 | #ifdef __cplusplus 169 | } 170 | #endif 171 | 172 | #endif 173 | 174 | -------------------------------------------------------------------------------- /src/shm_op_wrapper.c: -------------------------------------------------------------------------------- 1 | //shm_op_wrapper.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/logger.h" 11 | #include "fastcommon/shared_func.h" 12 | #include "shm_op_wrapper.h" 13 | 14 | #define SHM_GET_MMAP_FILENAME(true_filename, filename, proj_id) \ 15 | snprintf(true_filename, sizeof(true_filename), "%s.%d", \ 16 | filename, proj_id - 1) 17 | 18 | static void *shm_do_mmap(const char *filename, int proj_id, 19 | const int64_t size, const bool create_segment, int *err_no) 20 | { 21 | char true_filename[MAX_PATH_SIZE]; 22 | void *addr; 23 | int fd; 24 | mode_t old_mast; 25 | bool need_truncate; 26 | 27 | SHM_GET_MMAP_FILENAME(true_filename, filename, proj_id); 28 | fd = open(true_filename, O_RDWR); 29 | if (fd >= 0) { 30 | struct stat st; 31 | if (fstat(fd, &st) != 0) { 32 | *err_no = errno != 0 ? errno : EPERM; 33 | logError("file: "__FILE__", line: %d, " 34 | "stat file: %s fail, " 35 | "errno: %d, error info: %s", __LINE__, 36 | true_filename, *err_no, strerror(*err_no)); 37 | return NULL; 38 | } 39 | if (st.st_size > size) { 40 | logWarning("file: "__FILE__", line: %d, " 41 | "file %s size: %"PRId64" > expect size: %"PRId64, 42 | __LINE__, true_filename, st.st_size, size); 43 | need_truncate = false; 44 | } else if (st.st_size < size) { 45 | logWarning("file: "__FILE__", line: %d, " 46 | "file %s size: %"PRId64" < expect size: %"PRId64 47 | ", auto extend file size", __LINE__, 48 | true_filename, st.st_size, size); 49 | need_truncate = true; 50 | } else { 51 | need_truncate = false; 52 | } 53 | } else { 54 | if (!(create_segment && errno == ENOENT)) { 55 | *err_no = errno != 0 ? errno : EPERM; 56 | logError("file: "__FILE__", line: %d, " 57 | "open file: %s fail, " 58 | "errno: %d, error info: %s", __LINE__, 59 | true_filename, *err_no, strerror(*err_no)); 60 | return NULL; 61 | } 62 | 63 | old_mast = umask(0); 64 | fd = open(true_filename, O_RDWR | O_CREAT, 0666); 65 | umask(old_mast); 66 | 67 | if (fd < 0) { 68 | *err_no = errno != 0 ? errno : EPERM; 69 | logError("file: "__FILE__", line: %d, " 70 | "open file: %s fail, " 71 | "errno: %d, error info: %s", __LINE__, 72 | true_filename, *err_no, strerror(*err_no)); 73 | return NULL; 74 | } 75 | need_truncate = true; 76 | } 77 | if (need_truncate) { 78 | if (ftruncate(fd, size) != 0) { 79 | *err_no = errno != 0 ? errno : EPERM; 80 | close(fd); 81 | logError("file: "__FILE__", line: %d, " 82 | "truncate file: %s to size %"PRId64" fail, " 83 | "errno: %d, error info: %s", __LINE__, 84 | true_filename, size, *err_no, strerror(*err_no)); 85 | return NULL; 86 | } 87 | } 88 | 89 | addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 90 | if (addr == NULL || addr == MAP_FAILED) { 91 | *err_no = errno != 0 ? errno : EPERM; 92 | logError("file: "__FILE__", line: %d, " 93 | "mmap file: %s with size: %"PRId64" fail, " 94 | "errno: %d, error info: %s", __LINE__, 95 | true_filename, size, *err_no, strerror(*err_no)); 96 | } 97 | close(fd); 98 | *err_no = 0; 99 | return addr; 100 | } 101 | 102 | void *shm_do_shmmap(const key_t key, const int64_t size, 103 | const bool create_segment, int *err_no) 104 | { 105 | int shmid; 106 | void *addr; 107 | 108 | if (create_segment) { 109 | shmid = shmget(key, size, IPC_CREAT | 0666); 110 | } else { 111 | shmid = shmget(key, 0, 0666); 112 | } 113 | if (shmid < 0) { 114 | *err_no = errno != 0 ? errno : EPERM; 115 | logError("file: "__FILE__", line: %d, " 116 | "shmget with key %08x fail, " 117 | "errno: %d, error info: %s", __LINE__, 118 | key, *err_no, strerror(*err_no)); 119 | return NULL; 120 | } 121 | 122 | addr = shmat(shmid, NULL, 0); 123 | if (addr == NULL || addr == (void *)-1) { 124 | *err_no = errno != 0 ? errno : EPERM; 125 | logError("file: "__FILE__", line: %d, " 126 | "shmat with key %08x fail, " 127 | "errno: %d, error info: %s", __LINE__, 128 | key, *err_no, strerror(*err_no)); 129 | return NULL; 130 | } 131 | *err_no = 0; 132 | return addr; 133 | } 134 | 135 | static int shm_get_key(const char *filename, const int proj_id, 136 | key_t *key) 137 | { 138 | int result; 139 | if (access(filename, F_OK) != 0) { 140 | result = errno != 0 ? errno : ENOENT; 141 | if (result != ENOENT) { 142 | logError("file: "__FILE__", line: %d, " 143 | "access filename: %s fail, " 144 | "errno: %d, error info: %s", __LINE__, 145 | filename, result, strerror(result)); 146 | return result; 147 | } 148 | 149 | result = writeToFile(filename, "FOR LOCK", 8); 150 | if (result != 0) { 151 | return result; 152 | } 153 | if (chmod(filename, 0666) != 0) { 154 | result = errno != 0 ? errno : EFAULT; 155 | logError("file: "__FILE__", line: %d, " 156 | "chmod filename: %s fail, " 157 | "errno: %d, error info: %s", __LINE__, 158 | filename, result, strerror(result)); 159 | return result; 160 | } 161 | } 162 | 163 | *key = fc_ftok(filename, proj_id); 164 | if (*key == -1) { 165 | result = errno != 0 ? errno : EFAULT; 166 | logError("file: "__FILE__", line: %d, " 167 | "call fc_ftok fail, filename: %s, proj_id: %d, " 168 | "errno: %d, error info: %s", __LINE__, 169 | filename, proj_id, result, strerror(result)); 170 | return result; 171 | } 172 | return 0; 173 | } 174 | 175 | void *shm_mmap(const int type, const char *filename, 176 | const int proj_id, const int64_t size, key_t *key, 177 | const bool create_segment, int *err_no) 178 | { 179 | if ((*err_no=shm_get_key(filename, proj_id, key)) != 0) { 180 | return NULL; 181 | } 182 | if (type == SHMCACHE_TYPE_MMAP) { 183 | return shm_do_mmap(filename, proj_id, size, create_segment, err_no); 184 | } else { 185 | return shm_do_shmmap(*key, size, create_segment, err_no); 186 | } 187 | } 188 | 189 | bool shm_exists(const int type, const char *filename, const int proj_id) 190 | { 191 | key_t key; 192 | char true_filename[MAX_PATH_SIZE]; 193 | 194 | if (shm_get_key(filename, proj_id, &key) != 0) { 195 | return false; 196 | } 197 | if (type == SHMCACHE_TYPE_MMAP) { 198 | SHM_GET_MMAP_FILENAME(true_filename, filename, proj_id); 199 | return access(true_filename, F_OK) == 0; 200 | } else { 201 | return shmget(key, 0, 0666) >= 0; 202 | } 203 | } 204 | 205 | int shm_munmap(const int type, void *addr, const int64_t size) 206 | { 207 | int result; 208 | if (type == SHMCACHE_TYPE_MMAP) { 209 | if (munmap(addr, size) == 0) { 210 | result = 0; 211 | } else { 212 | result = errno != 0 ? errno : EACCES; 213 | logError("file: "__FILE__", line: %d, " 214 | "munmap addr: %p, size: %"PRId64" fail, " 215 | "errno: %d, error info: %s", __LINE__, 216 | addr, size, errno, strerror(errno)); 217 | } 218 | } else { 219 | if (shmdt(addr) == 0) { 220 | result = 0; 221 | } else { 222 | result = errno != 0 ? errno : EACCES; 223 | logError("file: "__FILE__", line: %d, " 224 | "munmap addr: %p, size: %"PRId64" fail, " 225 | "errno: %d, error info: %s", __LINE__, 226 | addr, size, errno, strerror(errno)); 227 | } 228 | } 229 | return result; 230 | } 231 | 232 | int shm_remove(const int type, const char *filename, 233 | const int proj_id, const int64_t size, const key_t key) 234 | { 235 | int result; 236 | char true_filename[MAX_PATH_SIZE]; 237 | 238 | if (type == SHMCACHE_TYPE_MMAP) { 239 | snprintf(true_filename, sizeof(true_filename), "%s.%d", 240 | filename, proj_id - 1); 241 | if (unlink(true_filename) != 0) { 242 | result = errno != 0 ? errno : EPERM; 243 | logError("file: "__FILE__", line: %d, " 244 | "unlink file: %s fail, " 245 | "errno: %d, error info: %s", __LINE__, 246 | true_filename, errno, strerror(errno)); 247 | return result; 248 | } 249 | } else { 250 | int shmid; 251 | 252 | shmid = shmget(key, 0, 0666); 253 | if (shmid < 0) { 254 | result = errno != 0 ? errno : EACCES; 255 | logError("file: "__FILE__", line: %d, " 256 | "shmget with key %08x fail, " 257 | "errno: %d, error info: %s", __LINE__, 258 | key, errno, strerror(errno)); 259 | return result; 260 | } 261 | 262 | if (shmctl(shmid, IPC_RMID, NULL) != 0) { 263 | result = errno != 0 ? errno : EACCES; 264 | logError("file: "__FILE__", line: %d, " 265 | "remove shm with key %08x fail, " 266 | "errno: %d, error info: %s", __LINE__, 267 | key, errno, strerror(errno)); 268 | return result; 269 | } 270 | } 271 | return 0; 272 | } 273 | 274 | -------------------------------------------------------------------------------- /src/shm_op_wrapper.h: -------------------------------------------------------------------------------- 1 | //shm_op_wrapper.h 2 | 3 | #ifndef _SHM_OP_WRAPPER_H 4 | #define _SHM_OP_WRAPPER_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | mmap or shmget & shmat 19 | parameters: 20 | type: mmap or shm 21 | filename: the filename 22 | proj_id: the project id to generate key 23 | size: the share memory size 24 | key: return the key 25 | create_segment: if create segment when segment not exist 26 | err_no: return errno 27 | return share memory pointer, NULL for fail 28 | */ 29 | void *shm_mmap(const int type, const char *filename, 30 | const int proj_id, const int64_t size, key_t *key, 31 | const bool create_segment, int *err_no); 32 | 33 | /** 34 | munmap or shmdt 35 | parameters: 36 | type: mmap or shm 37 | addr: the address to munmap 38 | size: the share memory size 39 | return: errno, 0 for success, != 0 fail 40 | */ 41 | int shm_munmap(const int type, void *addr, const int64_t size); 42 | 43 | /** 44 | remove shm 45 | parameters: 46 | type: mmap or shm 47 | filename: the filename 48 | proj_id: the project id to generate key 49 | size: the share memory size 50 | key: the key 51 | return: errno, 0 for success, != 0 fail 52 | */ 53 | int shm_remove(const int type, const char *filename, 54 | const int proj_id, const int64_t size, const key_t key); 55 | 56 | /** 57 | if shm exists 58 | parameters: 59 | type: mmap or shm 60 | filename: the filename 61 | proj_id: the project id to generate key 62 | return: errno, 0 for success, != 0 fail 63 | */ 64 | bool shm_exists(const int type, const char *filename, const int proj_id); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif 71 | 72 | -------------------------------------------------------------------------------- /src/shm_striping_allocator.c: -------------------------------------------------------------------------------- 1 | //shm_striping_allocator.c 2 | 3 | #include 4 | #include "fastcommon/sched_thread.h" 5 | #include "shm_striping_allocator.h" 6 | 7 | void shm_striping_allocator_init(struct shm_striping_allocator *allocator, 8 | const struct shm_segment_striping_pair *ssp_index, 9 | const int64_t base_offset, const int total_size) 10 | { 11 | allocator->index = *ssp_index; 12 | allocator->offset.base = base_offset; 13 | allocator->size.total = total_size; 14 | allocator->offset.end = base_offset + total_size; 15 | 16 | shm_striping_allocator_reset(allocator); 17 | } 18 | 19 | int64_t shm_striping_allocator_alloc(struct shm_striping_allocator *allocator, 20 | const int size) 21 | { 22 | int64_t ptr_offset; 23 | if (allocator->offset.end - allocator->offset.free < size) { 24 | allocator->fail_times++; 25 | return -1; 26 | } 27 | 28 | allocator->last_alloc_time = g_current_time; 29 | ptr_offset = allocator->offset.free; 30 | allocator->size.used += size; 31 | allocator->offset.free += size; 32 | return ptr_offset; 33 | } 34 | -------------------------------------------------------------------------------- /src/shm_striping_allocator.h: -------------------------------------------------------------------------------- 1 | //shm_striping_allocator.h 2 | 3 | #ifndef _SHM_STRIPING_ALLOCATOR_H 4 | #define _SHM_STRIPING_ALLOCATOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | allocator init 19 | parameters: 20 | allocator: the allocator pointer 21 | ssp_index: segment and striping pair 22 | base_offset: the base offset 23 | total_size: the memory size 24 | return error no, 0 for success, != 0 fail 25 | */ 26 | void shm_striping_allocator_init(struct shm_striping_allocator *allocator, 27 | const struct shm_segment_striping_pair *ssp_index, 28 | const int64_t base_offset, const int total_size); 29 | 30 | /** 31 | reset for recycle use 32 | parameters: 33 | allocator: the allocator pointer 34 | */ 35 | static inline void shm_striping_allocator_reset(struct shm_striping_allocator *allocator) 36 | { 37 | allocator->last_alloc_time = 0; 38 | allocator->fail_times = 0; 39 | allocator->size.used = 0; 40 | allocator->offset.free = allocator->offset.base; 41 | } 42 | 43 | /** 44 | alloc memory from the allocator 45 | parameters: 46 | allocator: the allocator pointer 47 | size: alloc bytes 48 | return the alloced memory offset, return -1 if fail 49 | */ 50 | int64_t shm_striping_allocator_alloc(struct shm_striping_allocator *allocator, 51 | const int size); 52 | 53 | /** 54 | free memory to the allocator 55 | parameters: 56 | allocator: the allocator pointer 57 | size: alloc bytes 58 | return used size 59 | */ 60 | static inline int64_t shm_striping_allocator_free(struct shm_striping_allocator 61 | *allocator, const int size) 62 | { 63 | allocator->size.used -= size; 64 | return allocator->size.used; 65 | } 66 | 67 | /** 68 | get free memory size 69 | parameters: 70 | allocator: the allocator pointer 71 | return free memory size 72 | */ 73 | static inline int64_t shm_striping_allocator_free_size(struct shm_striping_allocator 74 | *allocator) 75 | { 76 | return allocator->offset.end - allocator->offset.free; 77 | } 78 | 79 | /** 80 | get used memory size 81 | parameters: 82 | allocator: the allocator pointer 83 | return used memory size 84 | */ 85 | static inline int64_t shm_striping_allocator_used_size(struct shm_striping_allocator 86 | *allocator) 87 | { 88 | return allocator->size.used; 89 | } 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif 96 | 97 | -------------------------------------------------------------------------------- /src/shm_value_allocator.c: -------------------------------------------------------------------------------- 1 | //shm_value_allocator.c 2 | 3 | #include 4 | #include 5 | #include "fastcommon/sched_thread.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "shm_object_pool.h" 8 | #include "shm_striping_allocator.h" 9 | #include "shm_list.h" 10 | #include "shmopt.h" 11 | #include "shm_hashtable.h" 12 | #include "shm_value_allocator.h" 13 | 14 | static struct shm_hash_entry *shm_value_striping_alloc( 15 | struct shmcache_context *context, 16 | struct shm_striping_allocator *allocator, const int size) 17 | { 18 | int64_t offset; 19 | char *base; 20 | struct shm_hash_entry *entry; 21 | offset = shm_striping_allocator_alloc(allocator, size); 22 | if (offset < 0) { 23 | return NULL; 24 | } 25 | 26 | base = shmopt_get_value_segment(context, allocator->index.segment); 27 | if (base == NULL) { 28 | return NULL; 29 | } 30 | 31 | entry = (struct shm_hash_entry *)(base + offset); 32 | entry->memory.offset = offset; 33 | entry->memory.index = allocator->index; 34 | entry->memory.size = size; 35 | return entry; 36 | } 37 | 38 | static struct shm_hash_entry *shm_value_allocator_do_alloc(struct shmcache_context *context, 39 | const int size) 40 | { 41 | int64_t allocator_offset; 42 | int64_t removed_offset; 43 | struct shm_striping_allocator *allocator; 44 | struct shm_hash_entry *entry; 45 | 46 | allocator_offset = shm_object_pool_first(&context->value_allocator.doing); 47 | while (allocator_offset > 0) { 48 | allocator = (struct shm_striping_allocator *)(context->segments. 49 | hashtable.base + allocator_offset); 50 | if ((entry=shm_value_striping_alloc(context, allocator, size)) != NULL) { 51 | context->memory->usage.used.entry += size; 52 | return entry; 53 | } 54 | 55 | if ((shm_striping_allocator_free_size(allocator) <= context->config. 56 | va_policy.discard_memory_size) || (allocator->fail_times > 57 | context->config.va_policy.max_fail_times)) 58 | { 59 | removed_offset = shm_object_pool_remove(&context->value_allocator.doing); 60 | if (removed_offset == allocator_offset) { 61 | allocator->in_which_pool = SHMCACHE_STRIPING_ALLOCATOR_POOL_DONE; 62 | shm_object_pool_push(&context->value_allocator.done, allocator_offset); 63 | } else { 64 | logCrit("file: "__FILE__", line: %d, " 65 | "shm_object_pool_remove fail, " 66 | "offset: %"PRId64" != expect: %"PRId64, __LINE__, 67 | removed_offset, allocator_offset); 68 | } 69 | } 70 | 71 | allocator_offset = shm_object_pool_next(&context->value_allocator.doing); 72 | } 73 | 74 | return NULL; 75 | } 76 | 77 | 78 | static int shm_value_allocator_do_recycle(struct shmcache_context *context, 79 | struct shm_striping_allocator *allocator) 80 | { 81 | int64_t allocator_offset; 82 | if (allocator->in_which_pool == SHMCACHE_STRIPING_ALLOCATOR_POOL_DONE) { 83 | allocator_offset = (char *)allocator - context->segments.hashtable.base; 84 | if (shm_object_pool_remove_by(&context->value_allocator.done, 85 | allocator_offset) >= 0) { 86 | allocator->in_which_pool = SHMCACHE_STRIPING_ALLOCATOR_POOL_DOING; 87 | shm_object_pool_push(&context->value_allocator.doing, allocator_offset); 88 | } else { 89 | logCrit("file: "__FILE__", line: %d, " 90 | "shm_object_pool_remove_by fail, " 91 | "index: %d, offset: %"PRId64, __LINE__, 92 | allocator->index.striping, allocator_offset); 93 | return EFAULT; 94 | } 95 | } 96 | return 0; 97 | } 98 | 99 | int shm_value_allocator_recycle(struct shmcache_context *context, 100 | struct shm_recycle_stats *recycle_stats, 101 | const int recycle_keys_once) 102 | { 103 | int64_t entry_offset; 104 | int64_t current_offset; 105 | int64_t start_time; 106 | struct shm_hash_entry *entry; 107 | struct shmcache_key_info key; 108 | int result; 109 | int index; 110 | int scan_count; 111 | int clear_count; 112 | int valid_count; 113 | bool valid; 114 | bool recycled; 115 | char buff[32]; 116 | 117 | result = ENOMEM; 118 | scan_count = clear_count = valid_count = 0; 119 | start_time = get_current_time_us(); 120 | g_current_time = start_time / 1000000; 121 | recycled = false; 122 | entry_offset = shm_list_first(context); 123 | while (entry_offset > 0) { 124 | scan_count++; 125 | entry = shm_get_hentry_ptr(context, entry_offset); 126 | index = entry->memory.index.striping; 127 | key.data = entry->key; 128 | key.length = entry->key_len; 129 | valid = HT_ENTRY_IS_VALID(entry, g_current_time); 130 | if (valid && !context->config.recycle_valid_entries) { 131 | entry_offset = shm_list_next(context, entry_offset); 132 | continue; 133 | } 134 | 135 | current_offset = entry_offset; 136 | entry_offset = shm_list_next(context, entry_offset); 137 | 138 | if (shm_ht_delete_ex(context, &key, &recycled) != 0) { 139 | logError("file: "__FILE__", line: %d, " 140 | "shm_ht_delete fail, index: %d, " 141 | "entry offset: %"PRId64", " 142 | "key: %.*s, key length: %d", __LINE__, 143 | index, current_offset, entry->key_len, 144 | entry->key, entry->key_len); 145 | 146 | shm_ht_free_entry(context, entry, current_offset, &recycled); 147 | } 148 | 149 | clear_count++; 150 | if (valid) { 151 | valid_count++; 152 | } 153 | 154 | if (recycle_keys_once > 0) { 155 | if (clear_count >= recycle_keys_once) { 156 | result = 0; 157 | break; 158 | } 159 | } else if (recycled) { 160 | long_to_comma_str(get_current_time_us() - start_time, buff); 161 | logInfo("file: "__FILE__", line: %d, " 162 | "recycle #%d striping memory, " 163 | "scan entries: %d, clear total entries: %d, " 164 | "clear valid entries: %d, " 165 | "time used: %s us", __LINE__, index, 166 | scan_count, clear_count, valid_count, buff); 167 | result = 0; 168 | break; 169 | } 170 | } 171 | 172 | context->memory->stats.memory.clear_ht_entry.total += clear_count; 173 | if (valid_count > 0) { 174 | context->memory->stats.memory.clear_ht_entry.valid += valid_count; 175 | } 176 | 177 | recycle_stats->last_recycle_time = g_current_time; 178 | recycle_stats->total++; 179 | if (result == 0) { 180 | recycle_stats->success++; 181 | if (valid_count > 0) { 182 | recycle_stats->force++; 183 | if (context->config.va_policy. 184 | sleep_us_when_recycle_valid_entries > 0) 185 | { 186 | usleep(context->config.va_policy. 187 | sleep_us_when_recycle_valid_entries); 188 | } 189 | } 190 | } else { 191 | long_to_comma_str(get_current_time_us() - start_time, buff); 192 | logError("file: "__FILE__", line: %d, " 193 | "unable to recycle memory, " 194 | "scan entries: %d, clear total entries: %d, " 195 | "cleared valid entries: %d, " 196 | "time used: %s us", __LINE__, scan_count, 197 | clear_count, valid_count, buff); 198 | } 199 | return result; 200 | } 201 | 202 | static int shm_value_allocator_try_rearrange(struct shmcache_context *context) 203 | { 204 | int64_t used; 205 | int64_t free; 206 | int64_t start_time; 207 | int result; 208 | struct shmcache_hentry_array array; 209 | struct shmcache_hash_entry *entry; 210 | struct shmcache_hash_entry *end; 211 | 212 | used = context->memory->usage.used.common + context->memory->usage.used.entry; 213 | free = context->memory->usage.alloced - used; 214 | if (free < (SHMCACHE_MAX_KEY_SIZE + context->config.max_value_size) + 215 | (context->memory->vm_info.striping.count.current * 216 | context->config.va_policy.discard_memory_size) + 217 | context->memory->vm_info.striping.size) 218 | { 219 | return ENOSPC; 220 | } 221 | 222 | start_time = get_current_time_us(); 223 | logInfo("file: "__FILE__", line: %d, " 224 | "try rearrange shm, key count: %d", 225 | __LINE__, context->memory->hashtable.count); 226 | 227 | if ((result=shm_ht_to_array(context, &array)) != 0) { 228 | return result; 229 | } 230 | 231 | if (shm_ht_clear(context) > 0) { 232 | if (context->config.va_policy.sleep_us_when_recycle_valid_entries > 0) { 233 | usleep(context->config.va_policy.sleep_us_when_recycle_valid_entries); 234 | } 235 | } 236 | 237 | end = array.entries + array.count; 238 | for (entry=array.entries; entrykey, &entry->value)) != 0) { 240 | break; 241 | } 242 | } 243 | 244 | if (result == 0) { 245 | char buff[32]; 246 | long_to_comma_str(get_current_time_us() - start_time, buff); 247 | logInfo("file: "__FILE__", line: %d, " 248 | "rearrange shm done, key count: %d, time used: %s us", 249 | __LINE__, context->memory->hashtable.count, buff); 250 | } else { 251 | shm_ht_clear(context); 252 | } 253 | 254 | shm_ht_free_array(&array); 255 | return result; 256 | } 257 | 258 | struct shm_hash_entry *shm_value_allocator_alloc(struct shmcache_context *context, 259 | const int key_len, const int value_len) 260 | { 261 | int result; 262 | int size; 263 | bool recycle; 264 | int64_t allocator_offset; 265 | struct shm_striping_allocator *allocator; 266 | struct shm_hash_entry *entry; 267 | 268 | size = sizeof(struct shm_hash_entry) + MEM_ALIGN(key_len) + MEM_ALIGN(value_len); 269 | if ((entry=shm_value_allocator_do_alloc(context, size)) != NULL) { 270 | return entry; 271 | } 272 | 273 | if (context->memory->vm_info.segment.count.current >= 274 | context->memory->vm_info.segment.count.max) 275 | { 276 | recycle = true; 277 | } else { 278 | allocator_offset = shm_object_pool_first(&context->value_allocator.done); 279 | if (allocator_offset > 0) { 280 | allocator = (struct shm_striping_allocator *)(context->segments. 281 | hashtable.base + allocator_offset); 282 | recycle = (context->config.va_policy.avg_key_ttl > 0 && g_current_time - 283 | allocator->last_alloc_time >= context->config.va_policy.avg_key_ttl); 284 | } else { 285 | recycle = false; 286 | } 287 | } 288 | 289 | if (recycle ) { 290 | result = shm_value_allocator_recycle(context, 291 | &context->memory->stats.memory.recycle.value_striping, -1); 292 | 293 | if (result != 0 && !context->config.recycle_valid_entries) { 294 | if (context->memory->vm_info.segment.count.current < 295 | context->memory->vm_info.segment.count.max) 296 | { 297 | result = shmopt_create_value_segment(context); 298 | } else { 299 | result = shm_value_allocator_try_rearrange(context); 300 | if (result != 0) { 301 | logError("file: "__FILE__", line: %d, " 302 | "try rearrange shm fail, " 303 | "errno: %d, error info: %s", 304 | __LINE__, result, strerror(result)); 305 | } 306 | } 307 | } 308 | } else { 309 | result = shmopt_create_value_segment(context); 310 | } 311 | if (result == 0) { 312 | entry = shm_value_allocator_do_alloc(context, size); 313 | } 314 | if (entry == NULL) { 315 | logError("file: "__FILE__", line: %d, " 316 | "malloc %d bytes from shm fail", __LINE__, size); 317 | } 318 | return entry; 319 | } 320 | 321 | int shm_value_allocator_free(struct shmcache_context *context, 322 | struct shm_hash_entry *entry, bool *recycled) 323 | { 324 | struct shm_striping_allocator *allocator; 325 | int64_t used; 326 | 327 | allocator = context->value_allocator.allocators + entry->memory.index.striping; 328 | used = shm_striping_allocator_free(allocator, entry->memory.size); 329 | context->memory->usage.used.entry -= entry->memory.size; 330 | if (used <= 0) { 331 | if (used < 0) { 332 | logError("file: "__FILE__", line: %d, " 333 | "striping used memory: %"PRId64" < 0, " 334 | "segment: %d, striping: %d, offset: %"PRId64", size: %d", 335 | __LINE__, used, entry->memory.index.segment, 336 | entry->memory.index.striping, entry->memory.offset, 337 | entry->memory.size); 338 | } 339 | *recycled = true; 340 | shm_striping_allocator_reset(allocator); 341 | return shm_value_allocator_do_recycle(context, allocator); 342 | } 343 | 344 | return 0; 345 | } 346 | -------------------------------------------------------------------------------- /src/shm_value_allocator.h: -------------------------------------------------------------------------------- 1 | //shm_value_allocator.h 2 | 3 | #ifndef _SHM_VALUE_ALLOCATOR_H 4 | #define _SHM_VALUE_ALLOCATOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | #include "shmopt.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | alloc memory from the allocator 20 | parameters: 21 | context: the shm context 22 | key_len: the key length 23 | value_len: the value length 24 | return error no, 0 for success, != 0 fail 25 | */ 26 | struct shm_hash_entry *shm_value_allocator_alloc(struct shmcache_context *context, 27 | const int key_len, const int value_len); 28 | 29 | /** 30 | free memory to the allocator 31 | parameters: 32 | context: the shm context 33 | value: the value to free 34 | recycled: if recycled 35 | return error no, 0 for success, != 0 fail 36 | */ 37 | int shm_value_allocator_free(struct shmcache_context *context, 38 | struct shm_hash_entry *entry, bool *recycled); 39 | 40 | /** 41 | recycle oldest hashtable entries 42 | parameters: 43 | context: the shm context 44 | recycle_stats: the recycle stats 45 | recycle_key_once: recycle key number once, 46 | <= 0 for until recycle a value allocator 47 | return error no, 0 for success, != 0 fail 48 | */ 49 | int shm_value_allocator_recycle(struct shmcache_context *context, 50 | struct shm_recycle_stats *recycle_stats, 51 | const int recycle_keys_once); 52 | 53 | static inline char *shm_get_value_ptr(struct shmcache_context *context, 54 | struct shm_hash_entry *entry) 55 | { 56 | char *base; 57 | base = shmopt_get_value_segment(context, entry->memory.index.segment); 58 | if (base != NULL) { 59 | return base + entry->memory.offset + sizeof(struct shm_hash_entry) 60 | + MEM_ALIGN(entry->key_len); 61 | } else { 62 | return NULL; 63 | } 64 | } 65 | 66 | static inline struct shm_hash_entry *shm_get_hentry_ptr(struct shmcache_context *context, 67 | const int64_t offset) 68 | { 69 | char *base; 70 | union shm_hentry_offset conv; 71 | 72 | conv.offset = offset; 73 | base = shmopt_get_value_segment(context, conv.segment.index & 0xFF); 74 | if (base != NULL) { 75 | return (struct shm_hash_entry *)(base + conv.segment.offset); 76 | } else { 77 | return NULL; 78 | } 79 | } 80 | 81 | static inline int64_t shm_get_hentry_offset(struct shm_hash_entry *entry) 82 | { 83 | union shm_hentry_offset conv; 84 | conv.segment.index = entry->memory.index.segment | 0x4000; 85 | conv.segment.offset = entry->memory.offset; 86 | 87 | return conv.offset; 88 | } 89 | 90 | #ifdef __cplusplus 91 | } 92 | #endif 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/shmcache.h: -------------------------------------------------------------------------------- 1 | //shmcache.h 2 | 3 | #ifndef _SHMCACHE_H 4 | #define _SHMCACHE_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fastcommon/common_define.h" 13 | #include "shmcache_types.h" 14 | #include "shm_hashtable.h" 15 | #include "shm_lock.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | context init 23 | parameters: 24 | context: the context pointer 25 | config: the config parameters 26 | create_segment: if create segment when segment not exist 27 | check_segment: if check segment 28 | return error no, 0 for success, != 0 for fail 29 | */ 30 | int shmcache_init(struct shmcache_context *context, 31 | struct shmcache_config *config, const bool create_segment, 32 | const bool check_segment); 33 | 34 | /** 35 | context init from config file 36 | parameters: 37 | context: the context pointer 38 | config_filename: the config filename 39 | create_segment: if create segment when segment not exist 40 | check_segment: if check segment 41 | return error no, 0 for success, != 0 for fail 42 | */ 43 | int shmcache_init_from_file_ex(struct shmcache_context *context, 44 | const char *config_filename, const bool create_segment, 45 | const bool check_segment); 46 | 47 | /** 48 | context init from config file 49 | parameters: 50 | context: the context pointer 51 | config_filename: the config filename 52 | return error no, 0 for success, != 0 for fail 53 | */ 54 | static inline int shmcache_init_from_file(struct shmcache_context *context, 55 | const char *config_filename) 56 | { 57 | return shmcache_init_from_file_ex(context, config_filename, true, true); 58 | } 59 | 60 | /** 61 | load config from file 62 | parameters: 63 | config: the config pointer 64 | config_filename: the config filename 65 | return error no, 0 for success, != 0 for fail 66 | */ 67 | int shmcache_load_config(struct shmcache_config *config, 68 | const char *config_filename); 69 | 70 | /** 71 | context destroy 72 | parameters: 73 | context: the context pointer 74 | */ 75 | void shmcache_destroy(struct shmcache_context *context); 76 | 77 | /** 78 | set value 79 | parameters: 80 | context: the context pointer 81 | key: the key 82 | value: the value, include expire filed 83 | return error no, 0 for success, != 0 for fail 84 | */ 85 | int shmcache_set_ex(struct shmcache_context *context, 86 | const struct shmcache_key_info *key, 87 | const struct shmcache_value_info *value); 88 | 89 | /** 90 | set value 91 | parameters: 92 | context: the context pointer 93 | key: the key 94 | data: the value string ptr 95 | data_len: the value length 96 | ttl: the time to live in seconds 97 | return error no, 0 for success, != 0 for fail 98 | */ 99 | int shmcache_set(struct shmcache_context *context, 100 | const struct shmcache_key_info *key, 101 | const char *data, const int data_len, const int ttl); 102 | 103 | /** 104 | set TTL 105 | parameters: 106 | context: the context pointer 107 | key: the key 108 | ttl: the time to live in seconds 109 | return error no, 0 for success, != 0 for fail 110 | */ 111 | int shmcache_set_ttl(struct shmcache_context *context, 112 | const struct shmcache_key_info *key, const int ttl); 113 | 114 | 115 | /** 116 | set expires timestamp 117 | parameters: 118 | context: the context pointer 119 | key: the key 120 | expires: the expires timestamp 121 | return error no, 0 for success, != 0 for fail 122 | */ 123 | int shmcache_set_expires(struct shmcache_context *context, 124 | const struct shmcache_key_info *key, const int expires); 125 | 126 | /** 127 | increase integer value 128 | parameters: 129 | context: the context pointer 130 | key: the key 131 | incr: the incremental number 132 | ttl: the time to live in seconds 133 | value: return the new value 134 | return error no, 0 for success, != 0 for fail 135 | */ 136 | int shmcache_incr(struct shmcache_context *context, 137 | const struct shmcache_key_info *key, 138 | const int64_t increment, 139 | const int ttl, int64_t *new_value); 140 | 141 | /** 142 | get value 143 | parameters: 144 | context: the context pointer 145 | key: the key 146 | value: store the returned value 147 | return error no, 0 for success, != 0 for fail 148 | */ 149 | int shmcache_get(struct shmcache_context *context, 150 | const struct shmcache_key_info *key, 151 | struct shmcache_value_info *value); 152 | 153 | /** 154 | delte the key 155 | parameters: 156 | context: the context pointer 157 | key: the key 158 | return error no, 0 for success, != 0 for fail 159 | */ 160 | int shmcache_delete(struct shmcache_context *context, 161 | const struct shmcache_key_info *key); 162 | 163 | 164 | /** 165 | remove all share memory 166 | parameters: 167 | context: the context pointer 168 | return error no, 0 for success, != 0 for fail 169 | */ 170 | int shmcache_remove_all(struct shmcache_context *context); 171 | 172 | /** 173 | get stats 174 | parameters: 175 | context: the context pointer 176 | stats: return the stats 177 | calc_hit_ratio: if calculate hit_ratio 178 | return none 179 | */ 180 | void shmcache_stats_ex(struct shmcache_context *context, struct shmcache_stats *stats, 181 | const bool calc_hit_ratio); 182 | 183 | static inline void shmcache_stats(struct shmcache_context *context, 184 | struct shmcache_stats *stats) 185 | { 186 | shmcache_stats_ex(context, stats, true); 187 | } 188 | 189 | /** 190 | clear stats 191 | parameters: 192 | context: the context pointer 193 | return none 194 | */ 195 | void shmcache_clear_stats(struct shmcache_context *context); 196 | 197 | /** 198 | get serializer label 199 | parameters: 200 | serializer: the serializer 201 | return serializer label 202 | */ 203 | const char *shmcache_get_serializer_label(const int serializer); 204 | 205 | /** 206 | clear hashtable 207 | parameters: 208 | context: the context pointer 209 | return error no, 0 for success, != 0 for fail 210 | */ 211 | int shmcache_clear(struct shmcache_context *context); 212 | 213 | /** 214 | get last clear time 215 | parameters: 216 | context: the context pointer 217 | return last clear time 218 | */ 219 | static inline int64_t shmcache_get_last_clear_time(struct shmcache_context *context) 220 | { 221 | return context->memory->stats.hashtable.last_clear_time; 222 | } 223 | 224 | /** 225 | get shm init time 226 | parameters: 227 | context: the context pointer 228 | return the init time 229 | */ 230 | static inline int64_t shmcache_get_init_time(struct shmcache_context *context) 231 | { 232 | return context->memory->init_time; 233 | } 234 | 235 | #ifdef __cplusplus 236 | } 237 | #endif 238 | 239 | #endif 240 | -------------------------------------------------------------------------------- /src/shmcache_ini_annotation.c: -------------------------------------------------------------------------------- 1 | //shmcache_ini_annotation.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include "fastcommon/logger.h" 7 | #include "fastcommon/shared_func.h" 8 | #include "fastcommon/json_parser.h" 9 | #include "shmcache.h" 10 | #include "shmcache_ini_annotation.h" 11 | 12 | typedef struct { 13 | struct shmcache_context shm_ctx; 14 | fc_json_context_t json_ctx; 15 | } ShmcacheAnnotationArgs; 16 | 17 | static int config_get(IniContext *context, 18 | struct ini_annotation_entry *annotation, 19 | const IniItem *item, char **pOutValue, int max_values) 20 | { 21 | ShmcacheAnnotationArgs *args; 22 | int count; 23 | int result; 24 | struct shmcache_key_info key; 25 | struct shmcache_value_info value; 26 | char *output; 27 | 28 | args = (ShmcacheAnnotationArgs *)annotation->arg; 29 | if (item->value[0] != '\0') { 30 | key.data = (char *)item->value; 31 | } else { 32 | key.data = (char *)item->name; 33 | } 34 | 35 | value.options = SHMCACHE_SERIALIZER_STRING; 36 | key.length = strlen(key.data); 37 | if ((result= shmcache_get(&args->shm_ctx, &key, &value)) != 0) { 38 | if (result == ENOENT || result == ETIMEDOUT) { 39 | logError("file: "__FILE__", line: %d, " 40 | "shmcache_get fail, key: %s not exist", 41 | __LINE__, key.data); 42 | } else { 43 | logError("file: "__FILE__", line: %d, " 44 | "shmcache_get fail, key: %s, error info: %s", 45 | __LINE__, key.data, strerror(result)); 46 | } 47 | value.data = ""; 48 | value.length = 0; 49 | } else if ((value.options & SHMCACHE_SERIALIZER_STRING) == 0) { 50 | logError("file: "__FILE__", line: %d, " 51 | "unsupport serilizer %s (%d)", __LINE__, 52 | shmcache_get_serializer_label(value.options), 53 | value.options); 54 | value.data = ""; 55 | value.length = 0; 56 | } 57 | 58 | count = 0; 59 | if ((value.options & SHMCACHE_SERIALIZER_LIST) == 60 | SHMCACHE_SERIALIZER_LIST) 61 | { 62 | string_t input; 63 | const fc_json_array_t *array; 64 | 65 | input.str = value.data; 66 | input.len = value.length; 67 | if ((array=fc_decode_json_array(&args->json_ctx, &input)) != NULL) { 68 | string_t *el; 69 | string_t *end; 70 | 71 | end = array->elements + array->count; 72 | if (array->count > max_values) { 73 | end = array->elements + max_values; 74 | } 75 | 76 | for (el=array->elements; elstr, el->len); 78 | if (output == NULL) { 79 | return count; 80 | } 81 | pOutValue[count++] = output; 82 | } 83 | } else { 84 | logError("file: "__FILE__", line: %d, " 85 | "decode json array fail, %s", __LINE__, 86 | fc_json_parser_get_error_info(&args->json_ctx)->str); 87 | pOutValue[count++] = strdup(""); 88 | } 89 | } else { 90 | output = fc_strdup1(value.data, value.length); 91 | if (output == NULL) { 92 | return count; 93 | } 94 | pOutValue[count++] = output; 95 | } 96 | 97 | return count; 98 | } 99 | 100 | static void annotation_destroy(struct ini_annotation_entry *annotation) 101 | { 102 | ShmcacheAnnotationArgs *args; 103 | 104 | if (annotation->arg != NULL) { 105 | args = (ShmcacheAnnotationArgs *)annotation->arg; 106 | shmcache_destroy(&args->shm_ctx); 107 | fc_destroy_json_context(&args->json_ctx); 108 | annotation->arg = NULL; 109 | } 110 | } 111 | 112 | int CONFIG_GET_init_annotation(AnnotationEntry *annotation, 113 | const char *config_filename) 114 | { 115 | ShmcacheAnnotationArgs *args; 116 | int result; 117 | 118 | memset(annotation, 0, sizeof(AnnotationEntry)); 119 | annotation->func_name = "CONFIG_GET"; 120 | annotation->func_get = config_get; 121 | annotation->func_free = iniAnnotationFreeValues; 122 | annotation->func_destroy = annotation_destroy; 123 | 124 | args = (ShmcacheAnnotationArgs *)malloc( 125 | sizeof(ShmcacheAnnotationArgs)); 126 | if (args == NULL) { 127 | logError("file: "__FILE__", line: %d, " 128 | "malloc %d bytes fail", __LINE__, 129 | (int)sizeof(ShmcacheAnnotationArgs)); 130 | return ENOMEM; 131 | } 132 | 133 | if ((result=shmcache_init_from_file_ex(&args->shm_ctx, 134 | config_filename, false, true)) != 0) 135 | { 136 | free(args); 137 | return result; 138 | } 139 | 140 | if ((result=fc_init_json_context(&args->json_ctx)) != 0) { 141 | free(args); 142 | return result; 143 | } 144 | 145 | annotation->arg = args; 146 | return 0; 147 | } 148 | -------------------------------------------------------------------------------- /src/shmcache_ini_annotation.h: -------------------------------------------------------------------------------- 1 | //shmcache_ini_annotation.h 2 | 3 | #ifndef _SHMCACHE_INI_ANNOTATION_H 4 | #define _SHMCACHE_INI_ANNOTATION_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "shmcache_types.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | parameters: 19 | annotation: the annotation entry 20 | config_filename: the libshmcache config filename 21 | return errno, 0 for success, != 0 fail 22 | */ 23 | int CONFIG_GET_init_annotation(AnnotationEntry *annotation, 24 | const char *config_filename); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /src/shmcache_types.h: -------------------------------------------------------------------------------- 1 | //shmcache_types.h 2 | 3 | #ifndef _SHMCACHE_TYPES_H 4 | #define _SHMCACHE_TYPES_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fastcommon/common_define.h" 13 | #include "fastcommon/hash.h" 14 | 15 | #define SHMCACHE_MAJOR_VERSION 1 16 | #define SHMCACHE_MINOR_VERSION 0 17 | #define SHMCACHE_PATCH_VERSION 6 18 | 19 | #define SHMCACHE_MAX_KEY_SIZE 64 20 | 21 | #define SHMCACHE_STATUS_INIT 0 22 | #define SHMCACHE_STATUS_NORMAL 0x12345678 23 | 24 | #define SHMCACHE_TYPE_SHM 1 25 | #define SHMCACHE_TYPE_MMAP 2 26 | 27 | #define SHMCACHE_NEVER_EXPIRED 0 28 | 29 | #define SHMCACHE_SERIALIZER_STRING 0x1 //string type 30 | #define SHMCACHE_SERIALIZER_LIST (SHMCACHE_SERIALIZER_STRING | 0x2) //list 31 | #define SHMCACHE_SERIALIZER_MAP (SHMCACHE_SERIALIZER_STRING | 0x4) //map 32 | #define SHMCACHE_SERIALIZER_INTEGER (SHMCACHE_SERIALIZER_STRING | 0x8) //integer type 33 | #define SHMCACHE_SERIALIZER_IGBINARY 0x200 34 | #define SHMCACHE_SERIALIZER_MSGPACK 0x400 35 | #define SHMCACHE_SERIALIZER_PHP 0x800 36 | 37 | #define SHMCACHE_STRIPING_ALLOCATOR_POOL_DOING 0 38 | #define SHMCACHE_STRIPING_ALLOCATOR_POOL_DONE 1 39 | 40 | struct shmcache_config { 41 | char filename[MAX_PATH_SIZE]; 42 | int64_t min_memory; 43 | int64_t max_memory; 44 | int64_t segment_size; 45 | int max_key_count; 46 | int max_value_size; 47 | int type; //shm or mmap 48 | 49 | int recycle_key_once; //recycle key number once when reach max keys 50 | 51 | bool recycle_valid_entries; //if recycle valid entries by FIFO 52 | 53 | struct { 54 | /* avg. key TTL threshold for recycling memory 55 | * unit: second 56 | * <= 0 for never recycle memory until reach memory limit (max_memory) 57 | */ 58 | int avg_key_ttl; 59 | 60 | /* when the remain memory <= this parameter, discard it. 61 | * put this allocator in the doing queue to the done queue 62 | */ 63 | int discard_memory_size; 64 | 65 | /* when a allocator in the doing queue allocate fail times > this parameter, 66 | * means it is almost full, put it to the done queue 67 | */ 68 | int max_fail_times; 69 | 70 | /* sleep time to avoid other processes read dirty data when recycle 71 | * more than one valid (in TTL / not expired) KV entries. 72 | * 0 for never sleep 73 | */ 74 | int sleep_us_when_recycle_valid_entries; 75 | } va_policy; //value allocator policy 76 | 77 | struct { 78 | bool read_within_lock; 79 | int trylock_interval_us; 80 | int detect_deadlock_interval_ms; 81 | } lock_policy; 82 | 83 | HashFunc hash_func; 84 | }; 85 | 86 | struct shm_segment_striping_pair { 87 | short segment; //shm segment index 88 | short striping; //shm striping index 89 | }; 90 | 91 | struct shm_value { 92 | int length; //value length 93 | int options; //options for application 94 | }; 95 | 96 | struct shm_list { 97 | int64_t prev; 98 | int64_t next; 99 | }; 100 | 101 | union shm_hentry_offset { 102 | int64_t offset; 103 | struct { 104 | int64_t index :16; 105 | int64_t offset :48; 106 | } segment; 107 | }; 108 | 109 | struct shm_hash_entry { 110 | struct shm_list list; //for recycle, must be first 111 | 112 | int key_len; 113 | time_t expires; 114 | struct shm_value value; 115 | 116 | struct { 117 | int size; //alloc size 118 | struct shm_segment_striping_pair index; 119 | int64_t offset; //value segment offset 120 | } memory; 121 | 122 | int64_t ht_next; //for hashtable 123 | char key[0]; 124 | }; 125 | 126 | struct shm_ring_queue { 127 | int capacity; 128 | int head; //for pop 129 | int tail; //for push 130 | }; 131 | 132 | struct shm_object_pool_info { 133 | struct { 134 | int64_t base_offset; 135 | int element_size; 136 | } object; 137 | struct shm_ring_queue queue; 138 | }; 139 | 140 | struct shm_hashtable { 141 | struct shm_list head; //for recycle 142 | int capacity; 143 | int count; 144 | int64_t buckets[0]; //entry offset 145 | }; 146 | 147 | struct shm_striping_allocator { 148 | time_t last_alloc_time; //record the timestamp of first allocate 149 | int fail_times; //allocate fail times 150 | short in_which_pool; //in doing or done 151 | struct shm_segment_striping_pair index; 152 | struct { 153 | int total; 154 | int used; 155 | } size; 156 | 157 | struct { 158 | int64_t base; 159 | int64_t free; 160 | int64_t end; 161 | } offset; 162 | }; 163 | 164 | struct shm_value_allocator { 165 | struct shm_object_pool_info doing; 166 | struct shm_object_pool_info done; 167 | }; 168 | 169 | struct shm_value_size_info { 170 | int64_t size; 171 | struct { 172 | int current; 173 | int max; 174 | } count; 175 | }; 176 | 177 | struct shm_value_memory_info { 178 | struct shm_value_size_info segment; 179 | struct shm_value_size_info striping; 180 | }; 181 | 182 | struct shm_lock { 183 | pid_t pid; 184 | pthread_mutex_t mutex; 185 | }; 186 | 187 | struct shm_counter { 188 | volatile int64_t total; 189 | volatile int64_t success; 190 | }; 191 | 192 | struct shm_recycle_stats { 193 | int64_t total; //total count 194 | int64_t success; //succes count 195 | int64_t force; //force recycle count (clear valid entries) 196 | int64_t last_recycle_time; 197 | }; 198 | 199 | struct shm_stats { 200 | struct { 201 | struct shm_counter set; 202 | struct shm_counter get; 203 | struct shm_counter del; 204 | struct shm_counter incr; 205 | int64_t last_clear_time; 206 | } hashtable; 207 | 208 | struct { 209 | struct { 210 | int64_t total; 211 | int64_t valid; 212 | } clear_ht_entry; //clear for recycle 213 | 214 | struct { 215 | struct shm_recycle_stats key; 216 | struct shm_recycle_stats value_striping; 217 | } recycle; 218 | } memory; 219 | 220 | struct { 221 | volatile int64_t total; 222 | volatile int64_t retry; 223 | volatile int64_t detect_deadlock; 224 | volatile int64_t unlock_deadlock; 225 | int64_t last_detect_deadlock_time; 226 | int64_t last_unlock_deadlock_time; 227 | } lock; 228 | 229 | //for calculate hit ratio 230 | struct { 231 | struct shm_counter get; 232 | int64_t calc_time; 233 | } last; 234 | 235 | int64_t init_time; //init unix timestamp 236 | }; 237 | 238 | struct shm_memory_usage { 239 | int64_t alloced; 240 | struct { 241 | int64_t common; //first segment include hashtable 242 | int64_t entry; //whole entry include key and value 243 | int64_t key; 244 | int64_t value; 245 | } used; 246 | }; 247 | 248 | struct shm_memory_info { 249 | int size; //sizeof(struct shm_memory_info) 250 | int status; 251 | time_t init_time; //init unix timestamp 252 | int max_key_count; 253 | struct shm_lock lock; 254 | struct shm_value_memory_info vm_info; //value memory info 255 | struct shm_value_allocator value_allocator; 256 | struct shm_stats stats; 257 | struct shm_memory_usage usage; 258 | struct shm_hashtable hashtable; //must be last 259 | }; 260 | 261 | struct shmcache_key_info { 262 | char *data; 263 | int length; 264 | }; 265 | 266 | struct shmcache_value_info { 267 | char *data; 268 | int length; 269 | int options; //options for application 270 | time_t expires; //expire time 271 | }; 272 | 273 | struct shmcache_hash_entry { 274 | struct shmcache_key_info key; 275 | struct shmcache_value_info value; 276 | }; 277 | 278 | struct shmcache_hentry_array { 279 | struct shmcache_hash_entry *entries; 280 | int count; 281 | }; 282 | 283 | struct shmcache_segment_info { 284 | int proj_id; 285 | key_t key; //shm key 286 | int64_t size; //memory size 287 | char *base; 288 | }; 289 | 290 | struct shmcache_object_pool_context { 291 | struct shm_object_pool_info *obj_pool_info; 292 | int64_t *offsets; //object offset array 293 | int index; //for iterator 294 | }; 295 | 296 | struct shmcache_value_allocator_context { 297 | struct shmcache_object_pool_context doing; //doing queue 298 | struct shmcache_object_pool_context done; //done queue 299 | struct shm_striping_allocator *allocators; //base address 300 | }; 301 | 302 | struct shmcache_list { 303 | struct { 304 | struct shm_list *ptr; 305 | int64_t offset; 306 | } head; 307 | }; 308 | 309 | struct shmcache_context { 310 | pid_t pid; 311 | int lock_fd; //for file lock 312 | int detect_deadlock_clocks; 313 | struct shmcache_config config; 314 | struct shm_memory_info *memory; 315 | struct { 316 | struct shmcache_segment_info hashtable; 317 | struct { 318 | int count; 319 | struct shmcache_segment_info *items; 320 | } values; 321 | } segments; 322 | 323 | struct shmcache_value_allocator_context value_allocator; 324 | struct shmcache_list list; //for value recycle 325 | bool create_segment; //if check segment size 326 | }; 327 | 328 | struct shmcache_stats { 329 | struct shm_stats shm; 330 | struct { 331 | int64_t segment_size; //segment memory size 332 | int capacity; 333 | int count; //key count 334 | } hashtable; 335 | int max_key_count; 336 | 337 | struct { 338 | int64_t max; 339 | int64_t used; 340 | struct shm_memory_usage usage; 341 | } memory; 342 | 343 | struct { 344 | double ratio; 345 | double get_qps; 346 | int seconds; 347 | } hit; 348 | }; 349 | 350 | #ifdef __cplusplus 351 | extern "C" { 352 | #endif 353 | 354 | #ifdef __cplusplus 355 | } 356 | #endif 357 | 358 | #endif 359 | 360 | -------------------------------------------------------------------------------- /src/shmopt.c: -------------------------------------------------------------------------------- 1 | //shmopt.c 2 | 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "shm_op_wrapper.h" 7 | #include "shm_striping_allocator.h" 8 | #include "shm_object_pool.h" 9 | #include "shmopt.h" 10 | 11 | int shmopt_init_segment(struct shmcache_context *context, 12 | struct shmcache_segment_info *segment, 13 | const int proj_id, const int64_t size) 14 | { 15 | int result; 16 | segment->proj_id = proj_id; 17 | segment->base = shm_mmap(context->config.type, 18 | context->config.filename, proj_id, size, &segment->key, 19 | context->create_segment, &result); 20 | if (segment->base == NULL) { 21 | return result; 22 | } 23 | 24 | segment->size = size; 25 | return 0; 26 | } 27 | 28 | static int shmopt_init_value_segment(struct shmcache_context *context, 29 | const int segment_index) 30 | { 31 | int proj_id; 32 | struct shmcache_segment_info *segment; 33 | 34 | //proj_id 1 for hashtable segment, value segments start from 2 35 | proj_id = segment_index + 2; 36 | segment = context->segments.values.items + segment_index; 37 | return shmopt_init_segment(context, segment, proj_id, 38 | context->memory->vm_info.segment.size); 39 | } 40 | 41 | int shmopt_create_value_segment(struct shmcache_context *context) 42 | { 43 | int result; 44 | int striping_count; 45 | int segment_index; 46 | int striping_index; 47 | int i; 48 | int64_t striping_offset; 49 | int64_t allocator_offset; 50 | struct shm_segment_striping_pair index_pair; 51 | struct shm_striping_allocator *allocator; 52 | 53 | if (context->memory->vm_info.segment.count.current >= 54 | context->memory->vm_info.segment.count.max) 55 | { 56 | logError("file: "__FILE__", line: %d, " 57 | "no value segments, reach max: %d", __LINE__, 58 | context->memory->vm_info.segment.count.max); 59 | return ENOSPC; 60 | } 61 | if (context->segments.values.count != context->memory->vm_info. 62 | segment.count.current) 63 | { 64 | logError("file: "__FILE__", line: %d, " 65 | "my value segments count: %d != that of shm: %d", 66 | __LINE__, context->segments.values.count, 67 | context->memory->vm_info.segment.count.current); 68 | return EINVAL; 69 | } 70 | 71 | segment_index = context->memory->vm_info.segment.count.current; 72 | if ((result=shmopt_init_value_segment(context, segment_index)) != 0) { 73 | return result; 74 | } 75 | context->memory->vm_info.segment.count.current++; 76 | 77 | striping_offset = 0; 78 | index_pair.segment = segment_index; 79 | striping_count = context->memory->vm_info.segment.size / 80 | context->memory->vm_info.striping.size; 81 | 82 | logInfo("file: "__FILE__", line: %d, " 83 | "striping_count: %d, striping.count.current: %d, " 84 | "context->memory->vm_info.segment.size: %"PRId64", " 85 | "context->memory->vm_info.striping.size: %"PRId64, 86 | __LINE__, striping_count, context->memory->vm_info.striping.count.current, 87 | context->memory->vm_info.segment.size, context->memory->vm_info.striping.size); 88 | 89 | for (i=0; imemory->vm_info.striping.count.current++; 91 | allocator = context->value_allocator.allocators + striping_index; 92 | 93 | index_pair.striping = striping_index; 94 | shm_striping_allocator_init(allocator, &index_pair, 95 | striping_offset, context->memory->vm_info.striping.size); 96 | 97 | //add to doing queue 98 | allocator_offset = (char *)allocator - context->segments.hashtable.base; 99 | allocator->in_which_pool = SHMCACHE_STRIPING_ALLOCATOR_POOL_DOING; 100 | shm_object_pool_push(&context->value_allocator.doing, allocator_offset); 101 | 102 | striping_offset += context->memory->vm_info.striping.size; 103 | } 104 | context->segments.values.count = segment_index + 1; 105 | context->memory->usage.alloced += context->memory->vm_info.segment.size; 106 | 107 | logInfo("file: "__FILE__", line: %d, pid: %d, " 108 | "create value segment #%d, size: %"PRId64, 109 | __LINE__, context->pid, segment_index + 1, 110 | context->memory->vm_info.segment.size); 111 | 112 | return 0; 113 | } 114 | 115 | int shmopt_open_value_segments(struct shmcache_context *context) 116 | { 117 | int result; 118 | int segment_index; 119 | 120 | for (segment_index = context->segments.values.count; 121 | segment_index < context->memory->vm_info.segment.count.current; 122 | segment_index++) 123 | { 124 | if ((result=shmopt_init_value_segment(context, segment_index)) != 0) { 125 | return result; 126 | } 127 | context->segments.values.count++; 128 | } 129 | 130 | return 0; 131 | } 132 | 133 | int shmopt_remove_all(struct shmcache_context *context) 134 | { 135 | int result; 136 | int r; 137 | int segment_index; 138 | struct shmcache_segment_info *segment; 139 | 140 | result = shm_remove(context->config.type, context->config.filename, 141 | context->segments.hashtable.proj_id, 142 | context->segments.hashtable.size, 143 | context->segments.hashtable.key); 144 | 145 | for (segment_index=0; segment_indexmemory->vm_info.segment. 146 | count.current; segment_index++) 147 | { 148 | segment = context->segments.values.items + segment_index; 149 | if ((result=shmopt_init_value_segment(context, segment_index)) != 0) { 150 | return result; 151 | } 152 | 153 | if ((r=shm_remove(context->config.type, context->config.filename, 154 | segment->proj_id, segment->size, 155 | segment->key)) != 0) 156 | { 157 | result = r; 158 | } 159 | } 160 | return result; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /src/shmopt.h: -------------------------------------------------------------------------------- 1 | //shmopt.h 2 | 3 | #ifndef _SHMOPT_H 4 | #define _SHMOPT_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/common_define.h" 11 | #include "fastcommon/logger.h" 12 | #include "shmcache_types.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | init share memory segment 20 | parameters: 21 | context: the context pointer 22 | segment: the segment pointer 23 | proj_id: the project id to generate key 24 | size: the share memory size 25 | return error no, 0 for success, != 0 for fail 26 | */ 27 | int shmopt_init_segment(struct shmcache_context *context, 28 | struct shmcache_segment_info *segment, 29 | const int proj_id, const int64_t size); 30 | 31 | /** 32 | create and init share memory value segment 33 | parameters: 34 | context: the context pointer 35 | return error no, 0 for success, != 0 for fail 36 | */ 37 | int shmopt_create_value_segment(struct shmcache_context *context); 38 | 39 | 40 | /** 41 | open share memory value segment 42 | parameters: 43 | context: the context pointer 44 | return error no, 0 for success, != 0 for fail 45 | */ 46 | int shmopt_open_value_segments(struct shmcache_context *context); 47 | 48 | /** 49 | alloc a node from the context 50 | parameters: 51 | context: the context pointer 52 | index: value segment index 53 | return the value segment ptr, return NULL if fail 54 | */ 55 | static inline char *shmopt_get_value_segment(struct shmcache_context *context, 56 | const int index) 57 | { 58 | if (index < context->segments.values.count) { 59 | return context->segments.values.items[index].base; 60 | } else if (index < context->memory->vm_info.segment.count.current) { 61 | if (shmopt_open_value_segments(context) != 0) { 62 | return NULL; 63 | } 64 | return context->segments.values.items[index].base; 65 | } else { 66 | logError("file: " __FILE__", line: %d, " 67 | "invalid index: %d >= %d", __LINE__, 68 | index, context->memory->vm_info.segment.count.current); 69 | return NULL; 70 | } 71 | } 72 | 73 | /** 74 | remove all share memory 75 | parameters: 76 | context: the context pointer 77 | return error no, 0 for success, != 0 for fail 78 | */ 79 | int shmopt_remove_all(struct shmcache_context *context); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /src/tests/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .c .o 2 | 3 | COMPILE = $(CC) -g -O1 -Wall -D_FILE_OFFSET_BITS=64 -g -DDEBUG_FLAG 4 | INC_PATH = -I/usr/local/include 5 | LIB_PATH = -lfastcommon -lshmcache -lpthread -ldl 6 | 7 | ALL_PRGS = tests 8 | 9 | all: $(ALL_PRGS) 10 | .c: 11 | $(COMPILE) -o $@ $< $(LIB_PATH) $(INC_PATH) 12 | .c.o: 13 | $(COMPILE) -c -o $@ $< $(INC_PATH) 14 | clean: 15 | rm -f $(ALL_PRGS) 16 | 17 | -------------------------------------------------------------------------------- /src/tests/tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/logger.h" 11 | #include "fastcommon/hash.h" 12 | #include "shmcache/shm_hashtable.h" 13 | #include "shmcache/shmcache.h" 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | #define MAX_VALUE_SIZE (8 * 1024) 18 | #define KEY_COUNT 13000 19 | 20 | int result; 21 | int current_time; 22 | struct shmcache_context context; 23 | char szKey[SHMCACHE_MAX_KEY_SIZE]; 24 | char szValue[MAX_VALUE_SIZE]; 25 | struct shmcache_hash_entry *entries; 26 | struct shmcache_hash_entry tmp; 27 | int bytes; 28 | int ttl = 300; 29 | int i, k; 30 | int i1, i2; 31 | 32 | log_init(); 33 | g_log_context.log_level = LOG_INFO; 34 | 35 | printf("sizeof(struct shm_hash_entry): %d\n", 36 | (int)sizeof(struct shm_hash_entry)); 37 | 38 | if ((result=shmcache_init_from_file(&context, 39 | "/usr/local/etc/libshmcache.conf")) != 0) 40 | { 41 | return result; 42 | } 43 | 44 | bytes = sizeof(struct shmcache_hash_entry) * KEY_COUNT; 45 | entries = (struct shmcache_hash_entry *)malloc(bytes); 46 | if (entries == NULL) { 47 | logError("file: "__FILE__", line: %d, " 48 | "malloc %d bytes fail", __LINE__, bytes); 49 | return ENOMEM; 50 | } 51 | 52 | current_time = time(NULL); 53 | srand(current_time); 54 | rand(); 55 | memset(szValue, 'A', sizeof(szValue)); 56 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "shmcache/shmcache.h" 8 | 9 | static void usage(const char *prog) 10 | { 11 | fprintf(stderr, "shmcache delete key.\n" 12 | "Usage: %s [config_filename] \n", prog); 13 | } 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | int result; 18 | int index; 19 | char *config_filename; 20 | struct shmcache_context context; 21 | struct shmcache_key_info key; 22 | 23 | if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || 24 | strcmp(argv[1], "help") == 0 || 25 | strcmp(argv[1], "--help") == 0)) 26 | { 27 | usage(argv[0]); 28 | return 0; 29 | } 30 | if (argc < 2) { 31 | usage(argv[0]); 32 | return EINVAL; 33 | } 34 | 35 | config_filename = "/etc/libshmcache.conf"; 36 | if (isFile(argv[1])) { 37 | if (argc < 3) { 38 | usage(argv[0]); 39 | return EINVAL; 40 | } 41 | config_filename = argv[1]; 42 | index = 2; 43 | } else { 44 | index = 1; 45 | } 46 | 47 | log_init(); 48 | if ((result=shmcache_init_from_file_ex(&context, 49 | config_filename, false, true)) != 0) 50 | { 51 | return result; 52 | } 53 | 54 | key.data = argv[index++]; 55 | key.length = strlen(key.data); 56 | result = shmcache_delete(&context, &key); 57 | if (result == 0) { 58 | printf("delete key: %s successfully.\n", key.data); 59 | } else { 60 | fprintf(stderr, "delete key: %s fail, errno: %d\n", key.data, result); 61 | } 62 | 63 | return result; 64 | } 65 | -------------------------------------------------------------------------------- /src/tools/shmcache_dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fastcommon/logger.h" 11 | #include "fastcommon/hash.h" 12 | #include "shmcache/shm_hashtable.h" 13 | #include "shmcache/shmcache.h" 14 | 15 | static char *config_filename = "/etc/libshmcache.conf"; 16 | static char *key_value_seperator = "="; 17 | static char *row_seperator = "\n"; 18 | static int start_offset = 0; 19 | static int row_count = 0; 20 | static char *search_key = NULL; 21 | static bool show_usage = false; 22 | static char *output_filename = NULL; 23 | 24 | static void usage(char *program) 25 | { 26 | fprintf(stderr, "Usage: %s options, the options as:\n" 27 | "\t -h help\n" 28 | "\t -c , default: /etc/libshmcache.conf\n" 29 | "\t -F , default: =\n" 30 | "\t -R , default: \\n\n" 31 | "\t -k , SQL like style such as " 32 | "MYKEY%% or %%MYKEY or %%MYKEY%%\n" 33 | "\t -s start offset, default: 0\n" 34 | "\t -n , 0 means NO limit, default: 0\n" 35 | "\t -o output filename, default: output to sdtout\n" 36 | "\n" 37 | "\t the seperators can use escape characters, such as: \\\\ for \\, \n" 38 | "\t \\t for tab, \\r for carriage return, \\n for new line\n" 39 | "\n", program); 40 | } 41 | 42 | static char *unescape_string(const char *s) 43 | { 44 | char *output; 45 | char *dest; 46 | const char *p; 47 | const char *end; 48 | const char *start; 49 | int src_len; 50 | int num_len; 51 | char buff[16]; 52 | 53 | src_len = strlen(s); 54 | output = (char *)malloc(src_len + 1); 55 | if (output == NULL) { 56 | fprintf(stderr, "malloc %d bytes fail", src_len + 1); 57 | return NULL; 58 | } 59 | 60 | dest = output; 61 | end = s + src_len; 62 | p = s; 63 | while (p < end) { 64 | if (*p == '\\' && p + 1 < end) { 65 | switch (*(p+1)) { 66 | case 'r': 67 | *dest++ = '\r'; 68 | break; 69 | case 'n': 70 | *dest++ = '\n'; 71 | break; 72 | case 't': 73 | *dest++ = '\t'; 74 | break; 75 | case 'f': 76 | *dest++ = '\f'; 77 | break; 78 | case '\\': 79 | *dest++ = '\\'; 80 | break; 81 | default: 82 | if (*(p+1) >= '0' && *(p+1) <= '9') { 83 | start = p + 1; 84 | p += 2; 85 | while ((p < end) && (*p >= '0' && *p <= '9')) { 86 | p++; 87 | if (p - start == 3) { 88 | break; 89 | } 90 | } 91 | num_len = p - start; 92 | memcpy(buff, start, num_len); 93 | *(buff + num_len) = '\0'; 94 | *dest++ = atoi(buff); 95 | } else { 96 | *dest++ = *p++; 97 | *dest++ = *p++; 98 | } 99 | continue; 100 | } 101 | p += 2; 102 | } else { 103 | *dest++ = *p++; 104 | } 105 | } 106 | 107 | *dest = '\0'; 108 | return output; 109 | } 110 | 111 | static void parse_args(int argc, char **argv) 112 | { 113 | int ch; 114 | 115 | while ((ch = getopt(argc, argv, "hc:F:R:k:s:n:o:")) != -1) { 116 | switch (ch) { 117 | case 'c': 118 | config_filename = optarg; 119 | break; 120 | case 'F': 121 | key_value_seperator = unescape_string(optarg); 122 | break; 123 | case 'R': 124 | row_seperator = unescape_string(optarg); 125 | break; 126 | case 'k': 127 | search_key = optarg; 128 | break; 129 | case 's': 130 | start_offset = atoi(optarg); 131 | break; 132 | case 'n': 133 | row_count = atoi(optarg); 134 | break; 135 | case 'o': 136 | output_filename = optarg; 137 | break; 138 | case 'h': 139 | default: 140 | show_usage = true; 141 | usage(argv[0]); 142 | return; 143 | } 144 | } 145 | 146 | if (optind != argc) { 147 | show_usage = true; 148 | usage(argv[0]); 149 | } 150 | } 151 | 152 | static struct shmcache_match_key_info *parse_search_key( 153 | struct shmcache_match_key_info *key_info) 154 | { 155 | if (search_key == NULL || *search_key == '\0') { 156 | return NULL; 157 | } 158 | 159 | key_info->op_type = 0; 160 | key_info->key = search_key; 161 | key_info->length = strlen(search_key); 162 | if (*search_key == '%') { 163 | key_info->op_type |= SHMCACHE_MATCH_KEY_OP_RIGHT; 164 | key_info->key++; 165 | key_info->length--; 166 | if (key_info->length == 0) { 167 | return NULL; 168 | } 169 | } 170 | 171 | if (key_info->key[key_info->length - 1] == '%') { 172 | key_info->op_type |= SHMCACHE_MATCH_KEY_OP_LEFT; 173 | key_info->length--; 174 | if (key_info->length == 0) { 175 | return NULL; 176 | } 177 | } 178 | 179 | return key_info; 180 | } 181 | 182 | static int do_dump(struct shmcache_context *context, FILE *fp) 183 | { 184 | int result; 185 | struct shmcache_hentry_array array; 186 | struct shmcache_hash_entry *entry; 187 | struct shmcache_hash_entry *end; 188 | struct shmcache_match_key_info key_info; 189 | struct shmcache_match_key_info *pkey; 190 | 191 | pkey = parse_search_key(&key_info); 192 | if ((result=shm_ht_to_array_ex(context, &array, pkey, 193 | start_offset, row_count)) != 0) 194 | { 195 | return result; 196 | } 197 | 198 | end = array.entries + array.count; 199 | for (entry=array.entries; entryvalue.options & SHMCACHE_SERIALIZER_STRING) != 0) { 201 | fprintf(fp, "%.*s%s%.*s%s", 202 | entry->key.length, entry->key.data, 203 | key_value_seperator, 204 | entry->value.length, entry->value.data, 205 | row_seperator); 206 | } else { 207 | fprintf(fp, "%.*s%s<%s serializer, data length: %d>%s", 208 | entry->key.length, entry->key.data, 209 | key_value_seperator, 210 | shmcache_get_serializer_label(entry->value.options), 211 | entry->value.length, row_seperator); 212 | } 213 | } 214 | 215 | shm_ht_free_array(&array); 216 | return 0; 217 | } 218 | 219 | int main(int argc, char **argv) 220 | { 221 | int result; 222 | struct shmcache_context context; 223 | FILE *fp; 224 | 225 | parse_args(argc, argv); 226 | if (show_usage) { 227 | return 0; 228 | } 229 | 230 | log_init(); 231 | if (output_filename != NULL) { 232 | fp = fopen(output_filename, "wb"); 233 | if (fp == NULL) { 234 | result = errno != 0 ? errno : EPERM; 235 | fprintf(stderr, "open file %s to write fail, " 236 | "error info: %s\n", output_filename, strerror(result)); 237 | return result; 238 | } 239 | } else { 240 | fp = stdout; 241 | } 242 | 243 | if ((result=shmcache_init_from_file_ex(&context, 244 | config_filename, false, true)) != 0) 245 | { 246 | return result; 247 | } 248 | 249 | if (context.config.lock_policy.read_within_lock) { 250 | if ((result=shm_lock(&context)) != 0) { 251 | return result; 252 | } 253 | result = do_dump(&context, fp); 254 | shm_unlock(&context); 255 | } else { 256 | result = do_dump(&context, fp); 257 | } 258 | if (fp != stdout) { 259 | fclose(fp); 260 | } 261 | shmcache_destroy(&context); 262 | return result; 263 | } 264 | -------------------------------------------------------------------------------- /src/tools/shmcache_get.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "shmcache/shmcache.h" 8 | 9 | static void usage(const char *prog) 10 | { 11 | fprintf(stderr, "shmcache get key value.\n" 12 | "Usage: %s [config_filename] \n", prog); 13 | } 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | int result; 18 | int index; 19 | char *config_filename; 20 | struct shmcache_context context; 21 | struct shmcache_key_info key; 22 | struct shmcache_value_info value; 23 | 24 | if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || 25 | strcmp(argv[1], "help") == 0 || 26 | strcmp(argv[1], "--help") == 0)) 27 | { 28 | usage(argv[0]); 29 | return 0; 30 | } 31 | if (argc < 2) { 32 | usage(argv[0]); 33 | return EINVAL; 34 | } 35 | 36 | config_filename = "/etc/libshmcache.conf"; 37 | if (isFile(argv[1])) { 38 | if (argc < 3) { 39 | usage(argv[0]); 40 | return EINVAL; 41 | } 42 | config_filename = argv[1]; 43 | index = 2; 44 | } else { 45 | index = 1; 46 | } 47 | 48 | log_init(); 49 | if ((result=shmcache_init_from_file_ex(&context, 50 | config_filename, false, true)) != 0) 51 | { 52 | return result; 53 | } 54 | 55 | key.data = argv[index++]; 56 | key.length = strlen(key.data); 57 | result = shmcache_get(&context, &key, &value); 58 | if (result == 0) { 59 | printf("value options: %d (%s serializer), value length: %d", 60 | value.options, shmcache_get_serializer_label(value.options), 61 | value.length); 62 | if ((value.options & SHMCACHE_SERIALIZER_STRING) != 0) { 63 | printf(", value:\n%.*s", value.length, value.data); 64 | } 65 | printf("\n"); 66 | } else { 67 | fprintf(stderr, "get key: %s fail, errno: %d\n", key.data, result); 68 | } 69 | 70 | return result; 71 | } 72 | -------------------------------------------------------------------------------- /src/tools/shmcache_remove_all.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fastcommon/logger.h" 5 | #include "shmcache/shmcache.h" 6 | #include "shmcache/shmopt.h" 7 | 8 | static void usage(const char *prog) 9 | { 10 | fprintf(stderr, "Remove all shmcache including share memories. \n" 11 | "Usage: %s [config_filename] [-y or --yes]\n", prog); 12 | 13 | } 14 | 15 | #define IS_YES(str) (strcmp(str, "--yes") == 0 || strcmp(str, "-y") == 0) 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | int result; 20 | bool ask; 21 | char confirm; 22 | char *config_filename; 23 | struct shmcache_context context; 24 | 25 | if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || 26 | strcmp(argv[1], "help") == 0 || 27 | strcmp(argv[1], "--help") == 0)) 28 | { 29 | usage(argv[0]); 30 | return 0; 31 | } 32 | if (argc > 3) { 33 | usage(argv[0]); 34 | return EINVAL; 35 | } 36 | 37 | ask = true; 38 | config_filename = "/etc/libshmcache.conf"; 39 | if (argc == 2) { 40 | if (IS_YES(argv[1])) { 41 | ask = false; 42 | } else { 43 | config_filename = argv[1]; 44 | } 45 | } else if (argc == 3) { 46 | if (IS_YES(argv[1])) { 47 | ask = false; 48 | config_filename = argv[2]; 49 | } else if (IS_YES(argv[2])) { 50 | config_filename = argv[1]; 51 | ask = false; 52 | } else { 53 | usage(argv[0]); 54 | return EINVAL; 55 | } 56 | } 57 | 58 | if (ask) { 59 | printf("Clear all share memory with config " 60 | "file: %s? : ", config_filename); 61 | if (!(scanf("%c", &confirm) == 1 && confirm == 'y')) { 62 | printf("share memory NOT removed.\n"); 63 | return ECANCELED; 64 | } 65 | } 66 | 67 | log_init(); 68 | if ((result=shmcache_init_from_file_ex(&context, 69 | config_filename, false, false)) != 0) 70 | { 71 | return result; 72 | } 73 | 74 | if ((result=shmcache_remove_all(&context)) == 0) { 75 | printf("ALL share memory segments are removed!\n"); 76 | } 77 | return result; 78 | } 79 | -------------------------------------------------------------------------------- /src/tools/shmcache_set.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "shmcache/shmcache.h" 8 | 9 | static void usage(const char *prog) 10 | { 11 | fprintf(stderr, "shmcache set key value.\n" 12 | "Usage: %s [config_filename] \n", prog); 13 | } 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | int result; 18 | int index; 19 | char *config_filename; 20 | struct shmcache_context context; 21 | struct shmcache_key_info key; 22 | char *value; 23 | int value_len; 24 | int ttl; 25 | 26 | if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || 27 | strcmp(argv[1], "help") == 0 || 28 | strcmp(argv[1], "--help") == 0)) 29 | { 30 | usage(argv[0]); 31 | return 0; 32 | } 33 | if (argc < 4) { 34 | usage(argv[0]); 35 | return EINVAL; 36 | } 37 | 38 | config_filename = "/etc/libshmcache.conf"; 39 | if (isFile(argv[1])) { 40 | if (argc < 5) { 41 | usage(argv[0]); 42 | return EINVAL; 43 | } 44 | config_filename = argv[1]; 45 | index = 2; 46 | } else { 47 | index = 1; 48 | } 49 | 50 | log_init(); 51 | if ((result=shmcache_init_from_file(&context, config_filename)) != 0) { 52 | return result; 53 | } 54 | 55 | key.data = argv[index++]; 56 | key.length = strlen(key.data); 57 | value = argv[index++]; 58 | value_len = strlen(value); 59 | ttl = atoi(argv[index++]); 60 | result = shmcache_set(&context, &key, value, value_len, ttl); 61 | if (result == 0) { 62 | printf("set key: %s success.\n", key.data); 63 | } else { 64 | fprintf(stderr, "set key: %s fail, errno: %d\n", key.data, result); 65 | } 66 | 67 | return result; 68 | } 69 | -------------------------------------------------------------------------------- /src/tools/shmcache_stats.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "fastcommon/logger.h" 6 | #include "fastcommon/shared_func.h" 7 | #include "fastcommon/sched_thread.h" 8 | #include "shmcache/shmcache.h" 9 | 10 | static void stats_output(struct shmcache_context *context); 11 | 12 | static void usage(const char *prog) 13 | { 14 | fprintf(stderr, "show shmcache stats.\n" 15 | "Usage: %s [config_filename] [clean|clear]\n" 16 | "\tclean or clear to reset the shm stats\n\n", prog); 17 | } 18 | 19 | int main(int argc, char *argv[]) 20 | { 21 | int result; 22 | int last_index; 23 | bool clear_stats; 24 | char *config_filename; 25 | struct shmcache_context context; 26 | 27 | if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || 28 | strcmp(argv[1], "help") == 0 || 29 | strcmp(argv[1], "--help") == 0)) 30 | { 31 | usage(argv[0]); 32 | return 0; 33 | } 34 | 35 | clear_stats = false; 36 | if (argc >= 2) { 37 | last_index = argc - 1; 38 | if (strcmp(argv[last_index], "clean") == 0 || 39 | strcmp(argv[last_index], "clear") == 0) 40 | { 41 | clear_stats = true; 42 | } 43 | } 44 | 45 | config_filename = "/etc/libshmcache.conf"; 46 | if (argc >= 2) { 47 | if (argc == 2) { 48 | if (!clear_stats) { 49 | config_filename = argv[1]; 50 | } 51 | } else { 52 | config_filename = argv[1]; 53 | } 54 | } 55 | 56 | log_init(); 57 | if ((result=shmcache_init_from_file_ex(&context, 58 | config_filename, false, true)) != 0) 59 | { 60 | return result; 61 | } 62 | 63 | if (clear_stats) { 64 | shmcache_clear_stats(&context); 65 | printf("shm stats cleared.\n\n"); 66 | } else { 67 | stats_output(&context); 68 | } 69 | return 0; 70 | } 71 | 72 | static void stats_output(struct shmcache_context *context) 73 | { 74 | struct shmcache_stats stats; 75 | int avg_key_len; 76 | int avg_value_len; 77 | char total_ratio[32]; 78 | char ratio[32]; 79 | char rw_ratio[32]; 80 | char time_buff[32]; 81 | 82 | g_current_time = time(NULL); 83 | shmcache_stats(context, &stats); 84 | if (stats.hashtable.count > 0) { 85 | avg_key_len = stats.memory.usage.used.key / stats.hashtable.count; 86 | avg_value_len = stats.memory.usage.used.value / stats.hashtable.count; 87 | } else { 88 | avg_key_len = 0; 89 | avg_value_len = 0; 90 | } 91 | if (stats.shm.hashtable.get.total > 0) { 92 | sprintf(total_ratio, "%.2f%%", 100.00 * stats.shm.hashtable.get.success 93 | / (double)stats.shm.hashtable.get.total); 94 | } else { 95 | total_ratio[0] = '-'; 96 | total_ratio[1] = '\0'; 97 | } 98 | if (stats.hit.ratio >= 0.00) { 99 | sprintf(ratio, "%.2f%%", stats.hit.ratio * 100.00); 100 | } else { 101 | ratio[0] = '-'; 102 | ratio[1] = '\0'; 103 | } 104 | if (stats.shm.hashtable.set.total > 0) { 105 | sprintf(rw_ratio, "%.2f / 1.00", stats.shm.hashtable.get.total 106 | / (double)stats.shm.hashtable.set.total); 107 | } else { 108 | rw_ratio[0] = '-'; 109 | rw_ratio[1] = '\0'; 110 | } 111 | 112 | printf("\ntimestamp info:\n"); 113 | printf("shm init time: %s\n", formatDatetime(context->memory->init_time, 114 | "%Y-%m-%d %H:%M:%S", time_buff, sizeof(time_buff))); 115 | if (context->memory->stats.hashtable.last_clear_time > 0) { 116 | printf("last clear time: %s\n", formatDatetime(context->memory->stats. 117 | hashtable.last_clear_time, "%Y-%m-%d %H:%M:%S", 118 | time_buff, sizeof(time_buff))); 119 | } 120 | printf("stats begin time: %s\n", formatDatetime(context->memory->stats. 121 | init_time, "%Y-%m-%d %H:%M:%S", 122 | time_buff, sizeof(time_buff))); 123 | 124 | if (context->memory->stats.memory.recycle.key.last_recycle_time > 0) { 125 | printf("last recycle by key time: %s\n", formatDatetime( 126 | context->memory->stats.memory.recycle.key. 127 | last_recycle_time, "%Y-%m-%d %H:%M:%S", 128 | time_buff, sizeof(time_buff))); 129 | } 130 | if (context->memory->stats.memory.recycle.value_striping.last_recycle_time > 0) { 131 | printf("last recycle by value time: %s\n", formatDatetime( 132 | context->memory->stats.memory.recycle.value_striping. 133 | last_recycle_time, "%Y-%m-%d %H:%M:%S", 134 | time_buff, sizeof(time_buff))); 135 | } 136 | if (context->memory->stats.lock.last_detect_deadlock_time > 0) { 137 | printf("last detect deadlock time: %s\n", formatDatetime( 138 | context->memory->stats.lock. 139 | last_detect_deadlock_time, "%Y-%m-%d %H:%M:%S", 140 | time_buff, sizeof(time_buff))); 141 | } 142 | if (context->memory->stats.lock.last_unlock_deadlock_time > 0) { 143 | printf("last unlock deadlock time: %s\n", formatDatetime( 144 | context->memory->stats.lock. 145 | last_unlock_deadlock_time, "%Y-%m-%d %H:%M:%S", 146 | time_buff, sizeof(time_buff))); 147 | } 148 | printf("\n"); 149 | 150 | printf("\nhash table stats:\n"); 151 | printf("max_key_count: %d\n" 152 | "current_key_count: %d\n" 153 | "segment_size: %.03f MB\n\n" 154 | "set.total_count: %"PRId64"\n" 155 | "set.success_count: %"PRId64"\n" 156 | "incr.total_count: %"PRId64"\n" 157 | "incr.success_count: %"PRId64"\n" 158 | "get.total_count: %"PRId64"\n" 159 | "get.success_count: %"PRId64"\n" 160 | "del.total_count: %"PRId64"\n" 161 | "del.success_count: %"PRId64"\n" 162 | "get.qps: %.2f\n" 163 | "hit ratio (last %d seconds): %s\n" 164 | "total hit ratio (last %d seconds): %s\n" 165 | "total RW ratio: %s\n\n", 166 | stats.max_key_count, 167 | stats.hashtable.count, 168 | (double)stats.hashtable.segment_size / (1024 * 1024), 169 | stats.shm.hashtable.set.total, 170 | stats.shm.hashtable.set.success, 171 | stats.shm.hashtable.incr.total, 172 | stats.shm.hashtable.incr.success, 173 | stats.shm.hashtable.get.total, 174 | stats.shm.hashtable.get.success, 175 | stats.shm.hashtable.del.total, 176 | stats.shm.hashtable.del.success, 177 | stats.hit.get_qps, stats.hit.seconds, ratio, 178 | (int)(g_current_time - context->memory->stats.init_time), 179 | total_ratio, rw_ratio); 180 | 181 | printf("\nmemory stats:\n"); 182 | printf("total: %.03f MB\n" 183 | "alloced: %.03f MB\n" 184 | "used: %.03f MB\n" 185 | "free: %.03f MB\n" 186 | "avg_key_len: %d\n" 187 | "avg_value_len: %d\n\n", 188 | (double)stats.memory.max / (1024 * 1024), 189 | (double)stats.memory.usage.alloced / (1024 * 1024), 190 | (double)stats.memory.used / (1024 * 1024), 191 | (double)(stats.memory.max - stats.memory.used) / 192 | (1024 * 1024), avg_key_len, avg_value_len); 193 | 194 | printf("\nmemory recycle stats:\n"); 195 | printf("clear_ht_entry.total_count: %"PRId64"\n" 196 | "clear_ht_entry.valid_count: %"PRId64"\n\n" 197 | "recycle.key.total_count: %"PRId64"\n" 198 | "recycle.key.success_count: %"PRId64"\n" 199 | "recycle.key.force_count: %"PRId64"\n\n" 200 | "recycle.value_striping.total_count: %"PRId64"\n" 201 | "recycle.value_striping.success_count: %"PRId64"\n" 202 | "recycle.value_striping.force_count: %"PRId64"\n\n", 203 | stats.shm.memory.clear_ht_entry.total, 204 | stats.shm.memory.clear_ht_entry.valid, 205 | stats.shm.memory.recycle.key.total, 206 | stats.shm.memory.recycle.key.success, 207 | stats.shm.memory.recycle.key.force, 208 | stats.shm.memory.recycle.value_striping.total, 209 | stats.shm.memory.recycle.value_striping.success, 210 | stats.shm.memory.recycle.value_striping.force); 211 | 212 | printf("\nlock stats:\n"); 213 | printf("total_count: %"PRId64"\n" 214 | "retry_count: %"PRId64"\n" 215 | "detect_deadlock: %"PRId64"\n" 216 | "unlock_deadlock: %"PRId64"\n\n", 217 | stats.shm.lock.total, 218 | stats.shm.lock.retry, 219 | stats.shm.lock.detect_deadlock, 220 | stats.shm.lock.unlock_deadlock); 221 | } 222 | -------------------------------------------------------------------------------- /tsar-shmcache-module/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -fPIC --shared -g -O2 2 | CC = gcc 3 | INCLUDE_DIR = /usr/local/tsar/devel 4 | LINK = $(CC) -I$(INCLUDE_DIR) -I/usr/include/shmcache -I/usr/include/fastcommon \ 5 | -lfastcommon -lshmcache $(CFLAGS) 6 | 7 | OBJS = mod_shmcache.so 8 | 9 | all: $(OBJS) 10 | 11 | $(OBJS): %.so: %.c 12 | $(LINK) $< -o $@ 13 | clean: 14 | rm -f *.so; 15 | install: 16 | mkdir -p $(PREFIX)/etc/tsar/conf.d/ 17 | mkdir -p $(PREFIX)/usr/local/tsar/modules/ 18 | cp ./mod_shmcache.so $(PREFIX)/usr/local/tsar/modules/ 19 | cp ./mod_shmcache.conf $(PREFIX)/etc/tsar/conf.d/shmcache.conf 20 | uninstall: 21 | rm $(PREFIX)/usr/local/tsar/modules/mod_shmcache.so 22 | rm $(PREFIX)/etc/tsar/conf.d/shmcache.conf 23 | -------------------------------------------------------------------------------- /tsar-shmcache-module/mod_shmcache.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * (C) 2010-2011 Alibaba Group Holding Limited 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "tsar.h" 23 | #include "logger.h" 24 | #include "shmcache.h" 25 | 26 | #define FILED_INDEX_GET_QPS 0 27 | #define FILED_INDEX_HIT_RATIO 1 28 | #define FILED_INDEX_SET_QPS 2 29 | #define FILED_INDEX_INCR_QPS 3 30 | #define FILED_INDEX_DEL_QPS 4 31 | #define FILED_INDEX_RETRY_LOCKS 5 32 | #define FILED_INDEX_RECYCLE_BY_KEY 6 33 | #define FILED_INDEX_BY_KEY_FORCE 7 34 | #define FILED_INDEX_RECYCLE_BY_VAL 8 35 | #define FILED_INDEX_BY_VAL_FORCE 9 36 | #define FILED_INDEX_CLEAR_HT_COUNT 10 37 | #define FILED_INDEX_CLEAR_HT_VALID 11 38 | #define FILED_INDEX_SET_TOTAL_COUNT 12 39 | #define FILED_INDEX_SET_SUCCESS_COUNT 13 40 | #define FILED_INDEX_INCR_TOTAL_COUNT 14 41 | #define FILED_INDEX_INCR_SUCCESS_COUNT 15 42 | #define FILED_INDEX_GET_TOTAL_COUNT 16 43 | #define FILED_INDEX_GET_SUCCESS_COUNT 17 44 | #define FILED_INDEX_DEL_TOTAL_COUNT 18 45 | #define FILED_INDEX_DEL_SUCCESS_COUNT 19 46 | #define FILEDS_TOTAL_COUNT 20 47 | 48 | #define FILED_SUB_START_INDEX FILED_INDEX_RETRY_LOCKS 49 | #define FILED_SUB_END_INDEX FILED_INDEX_CLEAR_HT_VALID 50 | 51 | #define FILED_KEEP_START_INDEX FILED_INDEX_SET_TOTAL_COUNT 52 | #define FILED_KEEP_END_INDEX FILED_INDEX_DEL_SUCCESS_COUNT 53 | 54 | static const char *shmcache_usage = " --shmcache shmcache information"; 55 | 56 | /* Structure for tsar */ 57 | static struct mod_info shmcache_info[] = { 58 | {"GetQPS" , SUMMARY_BIT, MERGE_NULL, STATS_NULL}, 59 | {"HitRat", SUMMARY_BIT, MERGE_NULL, STATS_NULL}, 60 | {"SetQPS", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 61 | {"IncQPS", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 62 | {"DelQPS", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 63 | {"Relock", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 64 | {"CycKey", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 65 | {"CKForce", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 66 | {"CycVal", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 67 | {"CVForce", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 68 | {"CLREle", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 69 | {"CLRVld", DETAIL_BIT, MERGE_NULL, STATS_NULL}, 70 | {"set.total_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 71 | {"set.success_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 72 | {"incr.total_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 73 | {"incr.success_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 74 | {"get.total_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 75 | {"get.success_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 76 | {"del.total_count", HIDE_BIT, MERGE_NULL, STATS_NULL}, 77 | {"del.success_count", HIDE_BIT, MERGE_NULL, STATS_NULL} 78 | }; 79 | 80 | static void read_shmcache_stats(struct module *mod, const char *parameter) 81 | { 82 | /* parameter actually equals to mod->parameter */ 83 | struct shmcache_context context; 84 | struct shmcache_stats stats; 85 | const char *config_filename = "/etc/libshmcache.conf"; 86 | char buf[512]; 87 | 88 | log_init(); 89 | if (shmcache_init_from_file_ex(&context, config_filename, false, true) == 0) { 90 | shmcache_stats_ex(&context, &stats, false); 91 | shmcache_destroy(&context); 92 | } else { 93 | memset(&stats, 0, sizeof(stats)); 94 | } 95 | 96 | snprintf(buf, sizeof(buf), 97 | "0,0,0,0,0," 98 | "%"PRId64"," 99 | "%"PRId64"," 100 | "%"PRId64"," 101 | "%"PRId64"," 102 | "%"PRId64"," 103 | "%"PRId64"," 104 | "%"PRId64"," 105 | "%"PRId64"," 106 | "%"PRId64"," 107 | "%"PRId64"," 108 | "%"PRId64"," 109 | "%"PRId64"," 110 | "%"PRId64"," 111 | "%"PRId64"," 112 | "%"PRId64, 113 | stats.shm.lock.retry, 114 | stats.shm.memory.recycle.key.total, 115 | stats.shm.memory.recycle.key.force, 116 | stats.shm.memory.recycle.value_striping.total, 117 | stats.shm.memory.recycle.value_striping.force, 118 | stats.shm.memory.clear_ht_entry.total, 119 | stats.shm.memory.clear_ht_entry.valid, 120 | stats.shm.hashtable.set.total, 121 | stats.shm.hashtable.set.success, 122 | stats.shm.hashtable.incr.total, 123 | stats.shm.hashtable.incr.success, 124 | stats.shm.hashtable.get.total, 125 | stats.shm.hashtable.get.success, 126 | stats.shm.hashtable.del.total, 127 | stats.shm.hashtable.del.success); 128 | 129 | log_destroy(); 130 | 131 | /* send data to tsar you can get it by pre_array&cur_array at set_shmcache_record */ 132 | set_mod_record(mod, buf); 133 | return; 134 | } 135 | 136 | #define CALC_INCR_FIELD_VAL(i, v) \ 137 | do { \ 138 | v = cur_array[i] - pre_array[i]; \ 139 | if (v < 0 || v > 1000000000L) { \ 140 | v = 0; \ 141 | } \ 142 | } while (0) 143 | 144 | #define SET_QPS_FIELD_VAL(current, total) \ 145 | do { \ 146 | st_array[current] = cur_array[total] - pre_array[total]; \ 147 | if (st_array[current] < 0.00 || st_array[current] / inter > 100000000.00) { \ 148 | st_array[current] = 0.00; \ 149 | } else if (st_array[current] > 0.01) { \ 150 | st_array[current] /= inter; \ 151 | } else { \ 152 | st_array[current] = 0.00; \ 153 | } \ 154 | } while (0) 155 | 156 | static void set_shmcache_record(struct module *mod, double st_array[], 157 | U_64 pre_array[], U_64 cur_array[], int inter) 158 | { 159 | int i; 160 | int64_t sub; 161 | int64_t total_sub; 162 | int64_t success_sub; 163 | 164 | SET_QPS_FIELD_VAL(FILED_INDEX_GET_QPS, FILED_INDEX_GET_TOTAL_COUNT); 165 | SET_QPS_FIELD_VAL(FILED_INDEX_SET_QPS, FILED_INDEX_SET_TOTAL_COUNT); 166 | SET_QPS_FIELD_VAL(FILED_INDEX_INCR_QPS, FILED_INDEX_INCR_TOTAL_COUNT); 167 | SET_QPS_FIELD_VAL(FILED_INDEX_DEL_QPS, FILED_INDEX_DEL_TOTAL_COUNT); 168 | 169 | CALC_INCR_FIELD_VAL(FILED_INDEX_GET_TOTAL_COUNT, total_sub); 170 | CALC_INCR_FIELD_VAL(FILED_INDEX_GET_SUCCESS_COUNT, success_sub); 171 | if (total_sub > 0) { 172 | st_array[FILED_INDEX_HIT_RATIO] = 100.00 * (double)success_sub / (double)total_sub; 173 | } else { 174 | st_array[FILED_INDEX_HIT_RATIO] = 0.00; 175 | } 176 | 177 | for (i = FILED_SUB_START_INDEX; i <= FILED_SUB_END_INDEX; i++) { 178 | CALC_INCR_FIELD_VAL(i, sub); 179 | st_array[i] = sub; 180 | } 181 | 182 | for (i = FILED_KEEP_START_INDEX; i <= FILED_KEEP_END_INDEX; i++) { 183 | st_array[i] = cur_array[i]; 184 | } 185 | } 186 | 187 | /* register mod to tsar */ 188 | void 189 | mod_register(struct module *mod) 190 | { 191 | register_mod_fields(mod, "--shmcache", shmcache_usage, shmcache_info, 192 | FILEDS_TOTAL_COUNT, read_shmcache_stats, set_shmcache_record); 193 | } 194 | -------------------------------------------------------------------------------- /tsar-shmcache-module/mod_shmcache.conf: -------------------------------------------------------------------------------- 1 | mod_shmcache on 2 | 3 | ####add it to tsar default output 4 | output_stdio_mod mod_shmcache 5 | 6 | ####add it to center db 7 | #output_db_mod mod_shmcache 8 | 9 | ####add it to nagios send 10 | ####set nagios threshold for alert 11 | #output_nagios_mod mod_shmcache 12 | 13 | #threshold shmcache.value1;N;N;N;N; 14 | #threshold shmcache.value2;N;N;N;N; 15 | #threshold shmcache.value3;N;N;N;N; 16 | -------------------------------------------------------------------------------- /tsar-shmcache-module/tsar-shmcache-module.spec.in: -------------------------------------------------------------------------------- 1 | Name: tsar-shmcache-module 2 | Version: 1.0.1 3 | Release: 1%{?dist} 4 | Summary: No Summary 5 | License: BSD 6 | Group: Arch/Tech 7 | URL: http://github.com/happyfish100/libshmcache/ 8 | Source: http://github.com/happyfish100/libshmcache/%{name}-%{version}.tar.gz 9 | 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 11 | 12 | BuildRequires: tsar-devel >= 2.1.1 13 | BuildRequires: libshmcache-devel >= 1.0.3 14 | Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id 15 | Requires: tsar >= 2.1.1 16 | Requires: libshmcache >= 1.0.3 17 | 18 | %description 19 | tsar shmcache module 20 | 21 | %prep 22 | %setup -q 23 | 24 | %build 25 | make 26 | 27 | %install 28 | rm -rf %{buildroot} 29 | export PREFIX=$RPM_BUILD_ROOT 30 | make install 31 | 32 | %post 33 | mkdir -p /etc/tsar/conf.d/ 34 | 35 | %preun 36 | 37 | %postun 38 | 39 | %clean 40 | rm -rf %{buildroot} 41 | 42 | %files 43 | %defattr(-,root,root,-) 44 | /usr/local/tsar/modules/mod_shmcache.so 45 | /etc/tsar/conf.d/shmcache.conf 46 | 47 | %changelog 48 | * Mon Feb 27 2017 Yu Qing 49 | - first RPM release (1.0) 50 | --------------------------------------------------------------------------------