From acdc4fdfec93c9121bdc12e52a9cd57f494f8dcd Mon Sep 17 00:00:00 2001 From: John Sully Date: Thu, 11 Apr 2019 19:55:08 -0400 Subject: [PATCH 01/24] Fix memory leak unsetting master --- src/replication.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/replication.cpp b/src/replication.cpp index c062c312c..e46d96d42 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -2263,6 +2263,13 @@ struct redisMaster *replicationAddMaster(char *ip, int port) { return mi; } +void freeMasterInfo(redisMaster *mi) +{ + zfree(mi->masterauth); + zfree(mi->masteruser); + zfree(mi); +} + /* Cancel replication, setting the instance as a master itself. */ void replicationUnsetMaster(redisMaster *mi) { serverAssert(mi->masterhost != NULL); @@ -2305,6 +2312,7 @@ void replicationUnsetMaster(redisMaster *mi) { listNode *ln = listSearchKey(server.masters, mi); serverAssert(ln != nullptr); listDelNode(server.masters, ln); + freeMasterInfo(mi); } /* This function is called when the slave lose the connection with the From c64ce7aa9c7783425430e08831f1c41346e315a9 Mon Sep 17 00:00:00 2001 From: John Sully Date: Mon, 15 Apr 2019 22:31:02 -0400 Subject: [PATCH 02/24] Fix replica authentication failure when masterauth is below replicaof in the config file --- src/config.cpp | 2 ++ src/replication.cpp | 19 +++++++++++++++++++ src/server.h | 1 + 3 files changed, 22 insertions(+) diff --git a/src/config.cpp b/src/config.cpp index bcd4052aa..a702a5645 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -399,6 +399,8 @@ void loadServerConfigFromString(char *config) { } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) { zfree(server.default_masterauth); server.default_masterauth = argv[1][0] ? zstrdup(argv[1]) : NULL; + // Loop through all existing master infos and update them (in case this came after the replicaof config) + updateMasterAuth(); } else if ((!strcasecmp(argv[0],"slave-serve-stale-data") || !strcasecmp(argv[0],"replica-serve-stale-data")) && argc == 2) diff --git a/src/replication.cpp b/src/replication.cpp index e46d96d42..4e431cf73 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -3194,4 +3194,23 @@ void replicaReplayCommand(client *c) // call() will not propogate this for us, so we do so here alsoPropagate(server.rreplayCommand,c->db->id,c->argv,c->argc,PROPAGATE_AOF|PROPAGATE_REPL); return; +} + +void updateMasterAuth() +{ + listIter li; + listNode *ln; + + listRewind(server.masters, &li); + while ((ln = listNext(&li))) + { + redisMaster *mi = (redisMaster*)listNodeValue(ln); + zfree(mi->masterauth); mi->masterauth = nullptr; + zfree(mi->masteruser); mi->masteruser = nullptr; + + if (server.default_masterauth) + mi->masterauth = zstrdup(server.default_masterauth); + if (server.default_masteruser) + mi->masteruser = zstrdup(server.default_masteruser); + } } \ No newline at end of file diff --git a/src/server.h b/src/server.h index 069077c96..41c5b3eb7 100644 --- a/src/server.h +++ b/src/server.h @@ -1824,6 +1824,7 @@ void clearReplicationId2(void); void chopReplicationBacklog(void); void replicationCacheMasterUsingMyself(struct redisMaster *mi); void feedReplicationBacklog(const void *ptr, size_t len); +void updateMasterAuth(); /* Generic persistence functions */ void startLoading(FILE *fp); From 1c953a4d5b332a0758d77d378c0caffa4d9bcfe4 Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 16 Apr 2019 13:34:22 -0400 Subject: [PATCH 03/24] Fix module linking and crashing issues --- src/Makefile | 4 ++-- src/module.cpp | 2 +- src/redismodule.h | 9 +++++++++ src/{zmalloc.c => zmalloc.cpp} | 31 +++++++++++++++++-------------- 4 files changed, 29 insertions(+), 17 deletions(-) rename src/{zmalloc.c => zmalloc.cpp} (94%) diff --git a/src/Makefile b/src/Makefile index c787de7f9..555d0c629 100644 --- a/src/Makefile +++ b/src/Makefile @@ -270,7 +270,7 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS) -dict-benchmark: dict.cpp zmalloc.c sds.c siphash.c +dict-benchmark: dict.cpp zmalloc.cpp sds.c siphash.c $(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS) # Because the jemalloc.h header is generated as a part of the jemalloc build, @@ -312,7 +312,7 @@ lcov: @genhtml --legend -o lcov-html redis.info test-sds: sds.c sds.h - $(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test + $(REDIS_CC) sds.c zmalloc.cpp -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test /tmp/sds_test .PHONY: lcov diff --git a/src/module.cpp b/src/module.cpp index 3a109c1d5..c3448e89f 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -5077,7 +5077,7 @@ dictType moduleAPIDictType = { NULL /* val destructor */ }; -int moduleRegisterApi(const char *funcname, void *funcptr) { +extern "C" int moduleRegisterApi(const char *funcname, void *funcptr) { return dictAdd(server.moduleapi, (char*)funcname, funcptr); } diff --git a/src/redismodule.h b/src/redismodule.h index 259a5f1db..217025319 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -5,6 +5,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /* ---------------- Defines common between core and modules --------------- */ /* Error status return values. */ @@ -538,4 +542,9 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int #define RedisModuleString robj #endif /* REDISMODULE_CORE */ + +#ifdef __cplusplus +} +#endif + #endif /* REDISMOUDLE_H */ diff --git a/src/zmalloc.c b/src/zmalloc.cpp similarity index 94% rename from src/zmalloc.c rename to src/zmalloc.cpp index 3847ee75a..cb402b072 100644 --- a/src/zmalloc.c +++ b/src/zmalloc.cpp @@ -36,7 +36,7 @@ * for instance to free results obtained by backtrace_symbols(). We need * to define this function before including zmalloc.h that may shadow the * free implementation if we use jemalloc or another non standard allocator. */ -void zlibc_free(void *ptr) { +extern "C" void zlibc_free(void *ptr) { free(ptr); } @@ -49,13 +49,16 @@ void zlibc_free(void *ptr) { #ifdef HAVE_MALLOC_SIZE #define PREFIX_SIZE (0) #else +#define PREFIX_SIZE 16 #if defined(__sun) || defined(__sparc) || defined(__sparc__) -#define PREFIX_SIZE (sizeof(long long)) +static_assert(PREFIX_SIZE >= (sizeof(long long)), ""); #else -#define PREFIX_SIZE (sizeof(size_t)) +static_assert(PREFIX_SIZE >= (sizeof(size_t)), ""); #endif #endif +static_assert((PREFIX_SIZE % 16) == 0, "Our prefix must be modulo 16-bytes or our pointers will not be aligned"); + /* Explicitly override malloc/free etc when using tcmalloc. */ #if defined(USE_MEMKIND) #define malloc(size, type) salloc(size, type) @@ -104,9 +107,9 @@ static void zmalloc_default_oom(size_t size) { static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom; -void *zmalloc(size_t size, enum MALLOC_CLASS class) { - (void)class; - void *ptr = malloc(size+PREFIX_SIZE, class); +void *zmalloc(size_t size, enum MALLOC_CLASS mclass) { + (void)mclass; + void *ptr = malloc(size+PREFIX_SIZE, mclass); if (!ptr) zmalloc_oom_handler(size); #ifdef HAVE_MALLOC_SIZE @@ -137,9 +140,9 @@ void zfree_no_tcache(void *ptr) { } #endif -void *zcalloc(size_t size, enum MALLOC_CLASS class) { - (void)(class); - void *ptr = calloc(1, size+PREFIX_SIZE, class); +void *zcalloc(size_t size, enum MALLOC_CLASS mclass) { + (void)(mclass); + void *ptr = calloc(1, size+PREFIX_SIZE, mclass); if (!ptr) zmalloc_oom_handler(size); #ifdef HAVE_MALLOC_SIZE @@ -152,7 +155,7 @@ void *zcalloc(size_t size, enum MALLOC_CLASS class) { #endif } -void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) { +void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS mclass) { #ifndef HAVE_MALLOC_SIZE void *realptr; #endif @@ -163,10 +166,10 @@ void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) { zfree(ptr); return NULL; } - if (ptr == NULL) return zmalloc(size, class); + if (ptr == NULL) return zmalloc(size, mclass); #ifdef HAVE_MALLOC_SIZE oldsize = zmalloc_size(ptr); - newptr = realloc(ptr,size, class); + newptr = realloc(ptr,size, mclass); if (!newptr) zmalloc_oom_handler(size); update_zmalloc_stat_free(oldsize); @@ -175,7 +178,7 @@ void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) { #else realptr = (char*)ptr-PREFIX_SIZE; oldsize = *((size_t*)realptr); - newptr = realloc(realptr,size+PREFIX_SIZE, class); + newptr = realloc(realptr,size+PREFIX_SIZE, mclass); if (!newptr) zmalloc_oom_handler(size); *((size_t*)newptr) = size; @@ -222,7 +225,7 @@ void zfree(void *ptr) { char *zstrdup(const char *s) { size_t l = strlen(s)+1; - char *p = zmalloc(l, MALLOC_SHARED); + char *p = (char*)zmalloc(l, MALLOC_SHARED); memcpy(p,s,l); return p; From c4621a5ed2a7d8ca5034f2fbe8b71550f290ea64 Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 16 Apr 2019 23:16:03 -0400 Subject: [PATCH 04/24] Start of MVCC support (and more C++) --- src/Makefile | 2 +- src/acl.cpp | 2 +- src/adlist.h | 2 +- src/bitops.cpp | 51 +++++++------ src/cluster.cpp | 14 ++-- src/db.cpp | 37 +++++---- src/debug.cpp | 78 ++++++++++++++++--- src/expire.cpp | 4 +- src/geo.cpp | 20 ++--- src/hyperloglog.cpp | 14 ++-- src/listpack.h | 8 ++ src/module.cpp | 2 +- src/networking.cpp | 16 ++-- src/object.cpp | 38 ++++++---- src/pubsub.cpp | 2 +- src/rdb-s3.cpp | 2 +- src/rdb.cpp | 6 +- src/rdb.h | 6 +- src/replication.cpp | 45 ++++++----- src/scripting.cpp | 2 +- src/sds.c | 4 +- src/sds.h | 8 +- src/server.cpp | 9 ++- src/server.h | 180 ++++++++++++++++++++++++++++++-------------- src/slowlog.cpp | 2 +- src/sort.cpp | 9 ++- src/t_hash.cpp | 74 +++++++++--------- src/t_list.cpp | 18 ++--- src/t_set.cpp | 61 ++++++++------- src/t_stream.cpp | 28 +++---- src/t_string.cpp | 21 +++--- src/t_zset.cpp | 46 +++++------ src/ziplist.c | 6 +- src/ziplist.h | 2 +- src/zmalloc.cpp | 6 +- src/zmalloc.h | 2 +- 36 files changed, 491 insertions(+), 336 deletions(-) diff --git a/src/Makefile b/src/Makefile index 555d0c629..345e1c04b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -21,7 +21,7 @@ NODEPS:=clean distclean # Default settings STD=-std=c99 -pedantic -DREDIS_STATIC='' -CXX_STD=-std=c++14 -pedantic -fno-rtti +CXX_STD=-std=c++14 -pedantic -fno-rtti -fno-exceptions ifneq (,$(findstring clang,$(CC))) ifneq (,$(findstring FreeBSD,$(uname_S))) STD+=-Wno-c11-extensions diff --git a/src/acl.cpp b/src/acl.cpp index 6478b74ec..5dfcf609a 100644 --- a/src/acl.cpp +++ b/src/acl.cpp @@ -161,7 +161,7 @@ int ACLListMatchSds(void *a, void *b) { } /* Method to free list elements from ACL users password/ptterns lists. */ -void ACLListFreeSds(void *item) { +void ACLListFreeSds(const void *item) { sdsfree((sds)item); } diff --git a/src/adlist.h b/src/adlist.h index e9de81ceb..99b0bca7a 100644 --- a/src/adlist.h +++ b/src/adlist.h @@ -52,7 +52,7 @@ typedef struct list { listNode *head; listNode *tail; void *(*dup)(void *ptr); - void (*free)(void *ptr); + void (*free)(const void *ptr); int (*match)(void *ptr, void *key); unsigned long len; } list; diff --git a/src/bitops.cpp b/src/bitops.cpp index ea9b9acfa..ac942b977 100644 --- a/src/bitops.cpp +++ b/src/bitops.cpp @@ -37,7 +37,7 @@ /* Count number of bits set in the binary array pointed by 's' and long * 'count' bytes. The implementation of this function is required to * work with a input string length up to 512 MB. */ -size_t redisPopcount(void *s, long count) { +size_t redisPopcount(const void *s, long count) { size_t bits = 0; unsigned char *p = (unsigned char*)s; uint32_t *p4; @@ -98,7 +98,7 @@ size_t redisPopcount(void *s, long count) { * no zero bit is found, it returns count*8 assuming the string is zero * padded on the right. However if 'bit' is 1 it is possible that there is * not a single set bit in the bitmap. In this special case -1 is returned. */ -long redisBitpos(void *s, unsigned long count, int bit) { +long redisBitpos(const void *s, unsigned long count, int bit) { unsigned long *l; unsigned char *c; unsigned long skipval, word = 0, one; @@ -503,17 +503,17 @@ robj *lookupStringForBitCommand(client *c, size_t maxbit) { * * If the source object is NULL the function is guaranteed to return NULL * and set 'len' to 0. */ -unsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) { +const unsigned char *getObjectReadOnlyString(robj_roptr o, long *len, char *llbuf) { serverAssert(o->type == OBJ_STRING); - unsigned char *p = NULL; + const unsigned char *p = NULL; /* Set the 'p' pointer to the string, that can be just a stack allocated * array if our string was integer encoded. */ if (o && o->encoding == OBJ_ENCODING_INT) { - p = (unsigned char*) llbuf; + p = (const unsigned char*) llbuf; if (len) *len = ll2string(llbuf,LONG_STR_SIZE,(long)ptrFromObj(o)); } else if (o) { - p = (unsigned char*) ptrFromObj(o); + p = (const unsigned char*) ptrFromObj(o); if (len) *len = sdslen(szFromObj(o)); } else { if (len) *len = 0; @@ -562,7 +562,7 @@ void setbitCommand(client *c) { /* GETBIT key offset */ void getbitCommand(client *c) { - robj *o; + robj_roptr o; char llbuf[32]; size_t bitoffset; size_t byte, bit; @@ -571,7 +571,7 @@ void getbitCommand(client *c) { if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK) return; - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr || checkType(c,o,OBJ_STRING)) return; byte = bitoffset >> 3; @@ -590,9 +590,10 @@ void getbitCommand(client *c) { /* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */ void bitopCommand(client *c) { char *opname = szFromObj(c->argv[1]); - robj *o, *targetkey = c->argv[2]; + robj *targetkey = c->argv[2]; + robj_roptr o; unsigned long op, j, numkeys; - robj **objects; /* Array of source objects. */ + robj_roptr *objects; /* Array of source objects. */ unsigned char **src; /* Array of source strings pointers. */ unsigned long *len, maxlen = 0; /* Array of length of src strings, and max len. */ @@ -623,12 +624,12 @@ void bitopCommand(client *c) { numkeys = c->argc - 3; src = (unsigned char**)zmalloc(sizeof(unsigned char*) * numkeys, MALLOC_LOCAL); len = (unsigned long*)zmalloc(sizeof(long) * numkeys, MALLOC_LOCAL); - objects = (robj**)zmalloc(sizeof(robj*) * numkeys, MALLOC_LOCAL); + objects = (robj_roptr*)zmalloc(sizeof(robj_roptr) * numkeys, MALLOC_LOCAL); for (j = 0; j < numkeys; j++) { o = lookupKeyRead(c->db,c->argv[j+3]); /* Handle non-existing keys as empty strings. */ - if (o == NULL) { - objects[j] = NULL; + if (o == nullptr) { + objects[j] = nullptr; src[j] = NULL; len[j] = 0; minlen = 0; @@ -753,7 +754,7 @@ void bitopCommand(client *c) { /* Store the computed value into the target key */ if (maxlen) { - o = createObject(OBJ_STRING,res); + robj *o = createObject(OBJ_STRING,res); setKey(c->db,targetkey,o); notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id); decrRefCount(o); @@ -767,13 +768,13 @@ void bitopCommand(client *c) { /* BITCOUNT key [start end] */ void bitcountCommand(client *c) { - robj *o; + robj_roptr o; long start, end, strlen; - unsigned char *p; + const unsigned char *p; char llbuf[LONG_STR_SIZE]; /* Lookup, check for type, and return 0 for non existing keys. */ - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr || checkType(c,o,OBJ_STRING)) return; p = getObjectReadOnlyString(o,&strlen,llbuf); @@ -816,9 +817,9 @@ void bitcountCommand(client *c) { /* BITPOS key bit [start [end]] */ void bitposCommand(client *c) { - robj *o; + robj_roptr o; long bit, start, end, strlen; - unsigned char *p; + const unsigned char *p; char llbuf[LONG_STR_SIZE]; int end_given = 0; @@ -834,7 +835,7 @@ void bitposCommand(client *c) { /* If the key does not exist, from our point of view it is an infinite * array of 0 bits. If the user is looking for the fist clear bit return 0, * If the user is looking for the first set bit, return -1. */ - if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) { + if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) { addReplyLongLong(c, bit ? -1 : 0); return; } @@ -912,7 +913,7 @@ struct bitfieldOp { }; void bitfieldCommand(client *c) { - robj *o; + robj_roptr o; size_t bitoffset; int j, numops = 0, changes = 0; struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */ @@ -994,12 +995,12 @@ void bitfieldCommand(client *c) { /* Lookup for read is ok if key doesn't exit, but errors * if it's not a string. */ o = lookupKeyRead(c->db,c->argv[1]); - if (o != NULL && checkType(c,o,OBJ_STRING)) return; + if (o != nullptr && checkType(c,o,OBJ_STRING)) return; } else { /* Lookup by making room up to the farest bit reached by * this operation. */ if ((o = lookupStringForBitCommand(c, - highest_write_offset)) == NULL) return; + highest_write_offset)) == nullptr) return; } addReplyArrayLen(c,numops); @@ -1084,10 +1085,10 @@ void bitfieldCommand(client *c) { /* GET */ unsigned char buf[9]; long strlen = 0; - unsigned char *src = NULL; + const unsigned char *src = NULL; char llbuf[LONG_STR_SIZE]; - if (o != NULL) + if (o != nullptr) src = getObjectReadOnlyString(o,&strlen,llbuf); /* For GET we use a trick: before executing the operation diff --git a/src/cluster.cpp b/src/cluster.cpp index b86d8b7cc..5aca6237d 100644 --- a/src/cluster.cpp +++ b/src/cluster.cpp @@ -4797,7 +4797,7 @@ NULL /* Generates a DUMP-format representation of the object 'o', adding it to the * io stream pointed by 'rio'. This function can't fail. */ -void createDumpPayload(rio *payload, robj *o, robj *key) { +void createDumpPayload(rio *payload, robj_roptr o, robj *key) { unsigned char buf[2]; uint64_t crc; @@ -4853,11 +4853,11 @@ int verifyDumpPayload(unsigned char *p, size_t len) { * DUMP is actually not used by Redis Cluster but it is the obvious * complement of RESTORE and can be useful for different applications. */ void dumpCommand(client *c) { - robj *o, *dumpobj; + robj_roptr o, dumpobj; rio payload; /* Check if the key is here. */ - if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) { + if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) { addReplyNull(c); return; } @@ -5086,7 +5086,7 @@ void migrateCommand(client *c) { char *password = NULL; long timeout; long dbid; - robj **ov = NULL; /* Objects to migrate. */ + robj_roptr *ov = NULL; /* Objects to migrate. */ robj **kv = NULL; /* Key names. */ robj **newargv = NULL; /* Used to rewrite the command as DEL ... keys ... */ rio cmd, payload; @@ -5141,12 +5141,12 @@ void migrateCommand(client *c) { * the caller there was nothing to migrate. We don't return an error in * this case, since often this is due to a normal condition like the key * expiring in the meantime. */ - ov = (robj**)zrealloc(ov,sizeof(robj*)*num_keys, MALLOC_LOCAL); + ov = (robj_roptr*)zrealloc(ov,sizeof(robj_roptr)*num_keys, MALLOC_LOCAL); kv = (robj**)zrealloc(kv,sizeof(robj*)*num_keys, MALLOC_LOCAL); int oi = 0; for (j = 0; j < num_keys; j++) { - if ((ov[oi] = lookupKeyRead(c->db,c->argv[first_key+j])) != NULL) { + if ((ov[oi] = lookupKeyRead(c->db,c->argv[first_key+j])) != nullptr) { kv[oi] = c->argv[first_key+j]; oi++; } @@ -5583,7 +5583,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in /* Migarting / Improrting slot? Count keys we don't have. */ if ((migrating_slot || importing_slot) && - lookupKeyRead(&server.db[0],thiskey) == NULL) + lookupKeyRead(&server.db[0],thiskey) == nullptr) { missing_keys++; } diff --git a/src/db.cpp b/src/db.cpp index dc51a999a..685d56bc8 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -52,7 +52,7 @@ void updateLFU(robj *val) { /* Low level key lookup API, not actually called directly from commands * implementations that should instead rely on lookupKeyRead(), * lookupKeyWrite() and lookupKeyReadWithFlags(). */ -robj *lookupKey(redisDb *db, robj *key, int flags) { +static robj *lookupKey(redisDb *db, robj *key, int flags) { dictEntry *de = dictFind(db->pdict,ptrFromObj(key)); if (de) { robj *val = (robj*)dictGetVal(de); @@ -98,7 +98,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) { * for read operations. Even if the key expiry is master-driven, we can * correctly report a key is expired on slaves even if the master is lagging * expiring our key via DELs in the replication link. */ -robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { +robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { robj *val; serverAssert(GlobalLocksAcquired()); @@ -146,7 +146,7 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { /* Like lookupKeyReadWithFlags(), but does not use any flag, which is the * common case. */ -robj *lookupKeyRead(redisDb *db, robj *key) { +robj_roptr lookupKeyRead(redisDb *db, robj *key) { return lookupKeyReadWithFlags(db,key,LOOKUP_NONE); } @@ -160,8 +160,8 @@ robj *lookupKeyWrite(redisDb *db, robj *key) { return lookupKey(db,key,LOOKUP_NONE); } -robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) { - robj *o = lookupKeyRead(c->db, key); +robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply) { + robj_roptr o = lookupKeyRead(c->db, key); if (!o) addReply(c,reply); return o; } @@ -175,6 +175,9 @@ robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) { int dbAddCore(redisDb *db, robj *key, robj *val) { sds copy = sdsdup(szFromObj(key)); int retval = dictAdd(db->pdict, copy, val); +#ifdef ENABLE_MVCC + val->mvcc_tstamp = key->mvcc_tstamp = getMvccTstamp(); +#endif if (retval == DICT_OK) { @@ -229,6 +232,9 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) { if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { val->lru = old->lru; } +#ifdef ENABLE_MVCC + val->mvcc_tstamp = getMvccTstamp(); +#endif dictSetVal(db->pdict, de, val); if (server.lazyfree_lazy_server_del) { @@ -652,7 +658,7 @@ int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor) { * * In the case of a Hash object the function returns both the field and value * of every element on the Hash. */ -void scanGenericCommand(client *c, robj *o, unsigned long cursor) { +void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) { int i, j; list *keys = listCreate(); listNode *node, *nextnode; @@ -663,11 +669,11 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { /* Object must be NULL (to iterate keys names), or the type of the object * must be Set, Sorted Set, or Hash. */ - serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH || + serverAssert(o == nullptr || o->type == OBJ_SET || o->type == OBJ_HASH || o->type == OBJ_ZSET); /* Set i to the first option argument. The previous one is the cursor. */ - i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */ + i = (o == nullptr) ? 2 : 3; /* Skip the key argument if needed. */ /* Step 1: Parse options. */ while (i < c->argc) { @@ -710,7 +716,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { /* Handle the case of a hash table. */ ht = NULL; - if (o == NULL) { + if (o == nullptr) { ht = c->db->pdict; } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) { ht = (dict*)ptrFromObj(o); @@ -735,7 +741,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { * add new elements, and the object containing the dictionary so that * it is possible to fetch more data in a type-dependent way. */ privdata[0] = keys; - privdata[1] = o; + privdata[1] = o.unsafe_robjcast(); do { cursor = dictScan(ht, cursor, scanCallback, NULL, privdata); } while (cursor && @@ -789,7 +795,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { } /* Filter element if it is an expired key. */ - if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1; + if (!filter && o == nullptr && expireIfNeeded(c->db, kobj)) filter = 1; /* Remove the element and its associted value if needed. */ if (filter) { @@ -833,7 +839,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { void scanCommand(client *c) { unsigned long cursor; if (parseScanCursorOrReply(c,c->argv[1],&cursor) == C_ERR) return; - scanGenericCommand(c,NULL,cursor); + scanGenericCommand(c,nullptr,cursor); } void dbsizeCommand(client *c) { @@ -845,11 +851,10 @@ void lastsaveCommand(client *c) { } void typeCommand(client *c) { - robj *o; const char *type; - o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH); - if (o == NULL) { + robj_roptr o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH); + if (o == nullptr) { type = "none"; } else { switch(o->type) { @@ -1121,7 +1126,7 @@ void setExpire(client *c, redisDb *db, robj *key, long long when) { /* Return the expire time of the specified key, or -1 if no expire * is associated with this key (i.e. the key is non volatile) */ -long long getExpire(redisDb *db, robj *key) { +long long getExpire(redisDb *db, robj_roptr key) { dictEntry *de; /* No expire? return ASAP */ diff --git a/src/debug.cpp b/src/debug.cpp index 0d0011f6c..edcc1c631 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -45,6 +45,7 @@ typedef ucontext_t sigcontext_t; #include #include "bio.h" #include +#include #endif /* HAVE_BACKTRACE */ #ifdef __CYGWIN__ @@ -61,7 +62,7 @@ typedef ucontext_t sigcontext_t; * "add" digests relative to unordered elements. * * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ -extern "C" void xorDigest(unsigned char *digest, const void *ptr, size_t len) { +void xorDigest(unsigned char *digest, const void *ptr, size_t len) { SHA1_CTX ctx; unsigned char hash[20]; const unsigned char *s = (const unsigned char*)ptr; @@ -95,9 +96,9 @@ void xorStringObjectDigest(unsigned char *digest, robj *o) { * Also note that mixdigest("foo") followed by mixdigest("bar") * will lead to a different digest compared to "fo", "obar". */ -void mixDigest(unsigned char *digest, void *ptr, size_t len) { +void mixDigest(unsigned char *digest, const void *ptr, size_t len) { SHA1_CTX ctx; - char *s = (char*)ptr; + const char *s = (const char*)ptr; xorDigest(digest,s,len); SHA1Init(&ctx); @@ -105,7 +106,7 @@ void mixDigest(unsigned char *digest, void *ptr, size_t len) { SHA1Final(digest,&ctx); } -void mixStringObjectDigest(unsigned char *digest, robj *o) { +void mixStringObjectDigest(unsigned char *digest, robj_roptr o) { o = getDecodedObject(o); mixDigest(digest,ptrFromObj(o),sdslen(szFromObj(o))); decrRefCount(o); @@ -119,7 +120,7 @@ void mixStringObjectDigest(unsigned char *digest, robj *o) { * Note that this function does not reset the initial 'digest' passed, it * will continue mixing this object digest to anything that was already * present. */ -void xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) { +void xorObjectDigest(redisDb *db, robj_roptr keyobj, unsigned char *digest, robj_roptr o) { uint32_t aux = htonl(o->type); mixDigest(digest,&aux,sizeof(aux)); long long expiretime = getExpire(db,keyobj); @@ -523,7 +524,7 @@ NULL for (int j = 2; j < c->argc; j++) { unsigned char digest[20]; memset(digest,0,20); /* Start with a clean result */ - robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH); + robj_roptr o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH); if (o) xorObjectDigest(c->db,c->argv[j],digest,o); sds d = sdsempty(); @@ -723,7 +724,7 @@ void _serverAssertPrintClientInfo(const client *c) { } } -void serverLogObjectDebugInfo(const robj *o) { +void serverLogObjectDebugInfo(robj_roptr o) { serverLog(LL_WARNING,"Object type: %d", o->type); serverLog(LL_WARNING,"Object encoding: %d", o->encoding); serverLog(LL_WARNING,"Object refcount: %d", o->refcount); @@ -747,13 +748,13 @@ void serverLogObjectDebugInfo(const robj *o) { } } -void _serverAssertPrintObject(const robj *o) { +void _serverAssertPrintObject(robj_roptr o) { bugReportStart(); serverLog(LL_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ==="); serverLogObjectDebugInfo(o); } -void _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) { +void _serverAssertWithInfo(const client *c, robj_roptr o, const char *estr, const char *file, int line) { if (c) _serverAssertPrintClientInfo(c); if (o) _serverAssertPrintObject(o); _serverAssert(estr,file,line); @@ -1138,6 +1139,61 @@ void closeDirectLogFiledes(int fd) { if (!log_to_stdout) close(fd); } +void safe_write(int fd, const void *pv, ssize_t cb) +{ + ssize_t offset = 0; + do + { + ssize_t cbWrite = write(fd, reinterpret_cast(pv)+offset, cb-offset); + if (cbWrite <= 0) + return; + offset += cbWrite; + } while (offset < cb); +} + +void backtrace_symbols_demangle_fd(void **trace, size_t csym, int fd) +{ + char **syms = backtrace_symbols(trace, csym); + char symbuf[1024]; + for (size_t itrace = 0; itrace < csym; ++itrace) + { + int status = 0; + + // First find the symbol (preceded by a '(') + char *pchSymStart = syms[itrace]; + while (*pchSymStart != '(' && *pchSymStart != '\0') + ++pchSymStart; + if (*pchSymStart != '\0') + ++pchSymStart; // skip the '(' + char *pchSymEnd = pchSymStart; + while (*pchSymEnd != '+' && *pchSymEnd != '\0') + ++pchSymEnd; + + if ((pchSymEnd - pchSymStart) < 1023) + { + memcpy(symbuf, pchSymStart, pchSymEnd - pchSymStart); + symbuf[pchSymEnd - pchSymStart] = '\0'; + char *sz = abi::__cxa_demangle(symbuf, nullptr, nullptr, &status); + if (status == 0) + { + safe_write(fd, syms[itrace], pchSymStart - syms[itrace]); + safe_write(fd, sz, strlen(sz)); + safe_write(fd, pchSymEnd, (syms[itrace] + strlen(syms[itrace])-pchSymEnd)); + } + else + { + safe_write(fd, syms[itrace], strlen(syms[itrace])); + } + free(sz); + } + else { + safe_write(fd, syms[itrace], strlen(syms[itrace])); + } + safe_write(fd, "\n", 1); + } + free(syms); +} + /* Logs the stack trace using the backtrace() call. This function is designed * to be called from signal handlers safely. */ void logStackTrace(ucontext_t *uc) { @@ -1154,12 +1210,12 @@ void logStackTrace(ucontext_t *uc) { const char *msg2 = "\nBacktrace:\n"; if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */}; trace[0] = getMcontextEip(uc); - backtrace_symbols_fd(trace, 1, fd); + backtrace_symbols_demangle_fd(trace, 1, fd); if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */}; } /* Write symbols to log file */ - backtrace_symbols_fd(trace+1, trace_size, fd); + backtrace_symbols_demangle_fd(trace+1, trace_size, fd); /* Cleanup */ closeDirectLogFiledes(fd); diff --git a/src/expire.cpp b/src/expire.cpp index 7f5ab9a17..0e87a05b6 100644 --- a/src/expire.cpp +++ b/src/expire.cpp @@ -474,7 +474,7 @@ void ttlGenericCommand(client *c, int output_ms) { long long expire, ttl = -1; /* If the key does not exist at all, return -2 */ - if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == NULL) { + if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == nullptr) { addReplyLongLong(c,-2); return; } @@ -520,7 +520,7 @@ void persistCommand(client *c) { void touchCommand(client *c) { int touched = 0; for (int j = 1; j < c->argc; j++) - if (lookupKeyRead(c->db,c->argv[j]) != NULL) touched++; + if (lookupKeyRead(c->db,c->argv[j]) != nullptr) touched++; addReplyLongLong(c,touched); } diff --git a/src/geo.cpp b/src/geo.cpp index 82e6e0101..a88949966 100644 --- a/src/geo.cpp +++ b/src/geo.cpp @@ -110,7 +110,7 @@ int extractLongLatOrReply(client *c, robj **argv, double *xy) { /* Input Argument Helper */ /* Decode lat/long from a zset member's score. * Returns C_OK on successful decoding, otherwise C_ERR is returned. */ -int longLatFromMember(robj *zobj, robj *member, double *xy) { +int longLatFromMember(robj_roptr zobj, robj *member, double *xy) { double score = 0; if (zsetScore(zobj, szFromObj(member), &score) == C_ERR) return C_ERR; @@ -222,7 +222,7 @@ int geoAppendIfWithinRadius(geoArray *ga, double lon, double lat, double radius, * using multiple queries to the sorted set, that we later need to sort * via qsort. Similarly we need to be able to reject points outside the search * radius area ASAP in order to allocate and process more points than needed. */ -int geoGetPointsInRange(robj *zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) { +int geoGetPointsInRange(robj_roptr zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) { /* minex 0 = include min in range; maxex 1 = exclude max in range */ /* That's: min <= val < max */ zrangespec range = { min, max, 0, 1 }; @@ -315,7 +315,7 @@ void scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bit /* Obtain all members between the min/max of this geohash bounding box. * Populate a geoArray of GeoPoints by calling geoGetPointsInRange(). * Return the number of points added to the array. */ -int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) { +int membersOfGeoHashBox(robj_roptr zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) { GeoHashFix52Bits min, max; scoresOfGeoHashBox(hash,&min,&max); @@ -323,7 +323,7 @@ int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon, } /* Search all eight neighbors + self geohash box */ -int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) { +int membersOfAllNeighbors(robj_roptr zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) { GeoHashBits neighbors[9]; unsigned int i, count = 0, last_processed = 0; int debugmsg = 0; @@ -465,8 +465,8 @@ void georadiusGeneric(client *c, int flags) { int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */ /* Look up the requested zset */ - robj *zobj = NULL; - if ((zobj = lookupKeyReadOrReply(c, key, shared.null[c->resp])) == NULL || + robj_roptr zobj; + if ((zobj = lookupKeyReadOrReply(c, key, shared.null[c->resp])) == nullptr || checkType(c, zobj, OBJ_ZSET)) { return; } @@ -701,7 +701,7 @@ void geohashCommand(client *c) { int j; /* Look up the requested zset */ - robj *zobj = lookupKeyRead(c->db, c->argv[1]); + robj_roptr zobj = lookupKeyRead(c->db, c->argv[1]); if (zobj && checkType(c, zobj, OBJ_ZSET)) return; /* Geohash elements one after the other, using a null bulk reply for @@ -754,7 +754,7 @@ void geoposCommand(client *c) { int j; /* Look up the requested zset */ - robj *zobj = lookupKeyRead(c->db, c->argv[1]); + robj_roptr zobj = lookupKeyRead(c->db, c->argv[1]); if (zobj && checkType(c, zobj, OBJ_ZSET)) return; /* Report elements one after the other, using a null bulk reply for @@ -796,9 +796,9 @@ void geodistCommand(client *c) { } /* Look up the requested zset */ - robj *zobj = NULL; + robj_roptr zobj = NULL; if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp])) - == NULL || checkType(c, zobj, OBJ_ZSET)) return; + == nullptr || checkType(c, zobj, OBJ_ZSET)) return; /* Get the scores. We need both otherwise NULL is returned. */ double score1, score2, xyxy[4]; diff --git a/src/hyperloglog.cpp b/src/hyperloglog.cpp index c576246cf..32c35d38e 100644 --- a/src/hyperloglog.cpp +++ b/src/hyperloglog.cpp @@ -1073,7 +1073,7 @@ int hllAdd(robj *o, unsigned char *ele, size_t elesize) { * * If the HyperLogLog is sparse and is found to be invalid, C_ERR * is returned, otherwise the function always succeeds. */ -int hllMerge(uint8_t *max, size_t cmax, robj *hll) { +int hllMerge(uint8_t *max, size_t cmax, robj_roptr hll) { struct hllhdr *hdr = (hllhdr*)ptrFromObj(hll); int i; @@ -1085,7 +1085,7 @@ int hllMerge(uint8_t *max, size_t cmax, robj *hll) { if (val > max[i]) max[i] = val; } } else { - uint8_t *p = (uint8_t*)ptrFromObj(hll), *end = p + sdslen(szFromObj(hll)); + const uint8_t *p = (const uint8_t*)ptrFromObj(hll), *end = p + sdslen(szFromObj(hll)); long runlen, regval; p += HLL_HDR_SIZE; @@ -1156,7 +1156,7 @@ robj *createHLLObject(void) { /* Check if the object is a String with a valid HLL representation. * Return C_OK if this is true, otherwise reply to the client * with an error and return C_ERR. */ -int isHLLObjectOrReply(client *c, robj *o) { +int isHLLObjectOrReply(client *c, robj_roptr o) { struct hllhdr *hdr; /* Key exists, check type */ @@ -1248,8 +1248,8 @@ void pfcountCommand(client *c) { registers = max + HLL_HDR_SIZE; for (j = 1; j < c->argc; j++) { /* Check type and size. */ - robj *o = lookupKeyRead(c->db,c->argv[j]); - if (o == NULL) continue; /* Assume empty HLL for non existing var.*/ + robj_roptr o = lookupKeyRead(c->db,c->argv[j]); + if (o == nullptr) continue; /* Assume empty HLL for non existing var.*/ if (isHLLObjectOrReply(c,o) != C_OK) return; /* Merge with this HLL with our 'max' HHL by setting max[i] @@ -1330,8 +1330,8 @@ void pfmergeCommand(client *c) { memset(max,0,sizeof(max)); for (j = 1; j < c->argc; j++) { /* Check type and size. */ - robj *o = lookupKeyRead(c->db,c->argv[j]); - if (o == NULL) continue; /* Assume empty HLL for non existing var. */ + robj_roptr o = lookupKeyRead(c->db,c->argv[j]); + if (o == nullptr) continue; /* Assume empty HLL for non existing var. */ if (isHLLObjectOrReply(c,o) != C_OK) return; /* If at least one involved HLL is dense, use the dense representation diff --git a/src/listpack.h b/src/listpack.h index af67b4b41..e69046e27 100644 --- a/src/listpack.h +++ b/src/listpack.h @@ -44,6 +44,10 @@ #define LP_AFTER 1 #define LP_REPLACE 2 +#ifdef __cplusplus +extern "C" { +#endif + unsigned char *lpNew(void); void lpFree(unsigned char *lp); unsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp); @@ -58,4 +62,8 @@ unsigned char *lpPrev(unsigned char *lp, unsigned char *p); uint32_t lpBytes(unsigned char *lp); unsigned char *lpSeek(unsigned char *lp, long index); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/module.cpp b/src/module.cpp index c3448e89f..d2b7cd64d 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -1525,7 +1525,7 @@ void *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { if (mode & REDISMODULE_WRITE) { value = lookupKeyWrite(ctx->client->db,keyname); } else { - value = lookupKeyRead(ctx->client->db,keyname); + value = lookupKeyRead(ctx->client->db,keyname).unsafe_robjcast(); if (value == NULL) { return NULL; } diff --git a/src/networking.cpp b/src/networking.cpp index 5ba82b588..88cc7afb2 100644 --- a/src/networking.cpp +++ b/src/networking.cpp @@ -122,7 +122,7 @@ void *dupClientReplyValue(void *o) { return buf; } -void freeClientReplyValue(void *o) { +void freeClientReplyValue(const void *o) { zfree(o); } @@ -405,7 +405,7 @@ void _addReplyProtoToList(client *c, const char *s, size_t len) { * Higher level functions to queue data on the client output buffer. * The following functions are the ones that commands implementations will call. * -------------------------------------------------------------------------- */ -void addReplyCore(client *c, robj *obj, bool fAsync) { +void addReplyCore(client *c, robj_roptr obj, bool fAsync) { if (prepareClientToWrite(c, fAsync) != C_OK) return; if (sdsEncodedObject(obj)) { @@ -425,11 +425,11 @@ void addReplyCore(client *c, robj *obj, bool fAsync) { } /* Add the object 'obj' string representation to the client output buffer. */ -void addReply(client *c, robj *obj) +void addReply(client *c, robj_roptr obj) { addReplyCore(c, obj, false); } -void addReplyAsync(client *c, robj *obj) +void addReplyAsync(client *c, robj_roptr obj) { addReplyCore(c, obj, true); } @@ -867,7 +867,7 @@ void addReplyNullArray(client *c) { } /* Create the length prefix of a bulk reply, example: $2234 */ -void addReplyBulkLenCore(client *c, robj *obj, bool fAsync) { +void addReplyBulkLenCore(client *c, robj_roptr obj, bool fAsync) { size_t len = stringObjectLen(obj); if (len < OBJ_SHARED_BULKHDR_LEN) @@ -882,18 +882,18 @@ void addReplyBulkLen(client *c, robj *obj) } /* Add a Redis Object as a bulk reply */ -void addReplyBulkCore(client *c, robj *obj, bool fAsync) { +void addReplyBulkCore(client *c, robj_roptr obj, bool fAsync) { addReplyBulkLenCore(c,obj,fAsync); addReplyCore(c,obj,fAsync); addReplyCore(c,shared.crlf,fAsync); } -void addReplyBulk(client *c, robj *obj) +void addReplyBulk(client *c, robj_roptr obj) { addReplyBulkCore(c, obj, false); } -void addReplyBulkAsync(client *c, robj *obj) +void addReplyBulkAsync(client *c, robj_roptr obj) { addReplyBulkCore(c, obj, true); } diff --git a/src/object.cpp b/src/object.cpp index 2799537e1..169a6e08d 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -44,6 +44,9 @@ robj *createObject(int type, void *ptr) { o->encoding = OBJ_ENCODING_RAW; o->m_ptr = ptr; o->refcount = 1; +#ifdef ENABLE_MVCC + o->mvcc_tstamp = OBJ_MVCC_INVALID; +#endif /* Set the LRU to the current lruclock (minutes resolution), or * alternatively the LFU counter. */ @@ -91,6 +94,9 @@ robj *createEmbeddedStringObject(const char *ptr, size_t len) { o->type = OBJ_STRING; o->encoding = OBJ_ENCODING_EMBSTR; o->refcount = 1; +#ifdef ENABLE_MVCC + o->mvcc_tstamp = OBJ_MVCC_INVALID; +#endif if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL; } else { @@ -280,13 +286,13 @@ robj *createModuleObject(moduleType *mt, void *value) { return createObject(OBJ_MODULE,mv); } -void freeStringObject(robj *o) { +void freeStringObject(robj_roptr o) { if (o->encoding == OBJ_ENCODING_RAW) { sdsfree(szFromObj(o)); } } -void freeListObject(robj *o) { +void freeListObject(robj_roptr o) { if (o->encoding == OBJ_ENCODING_QUICKLIST) { quicklistRelease((quicklist*)ptrFromObj(o)); } else { @@ -294,7 +300,7 @@ void freeListObject(robj *o) { } } -void freeSetObject(robj *o) { +void freeSetObject(robj_roptr o) { switch (o->encoding) { case OBJ_ENCODING_HT: dictRelease((dict*) ptrFromObj(o)); @@ -307,7 +313,7 @@ void freeSetObject(robj *o) { } } -void freeZsetObject(robj *o) { +void freeZsetObject(robj_roptr o) { zset *zs; switch (o->encoding) { case OBJ_ENCODING_SKIPLIST: @@ -324,7 +330,7 @@ void freeZsetObject(robj *o) { } } -void freeHashObject(robj *o) { +void freeHashObject(robj_roptr o) { switch (o->encoding) { case OBJ_ENCODING_HT: dictRelease((dict*) ptrFromObj(o)); @@ -338,21 +344,21 @@ void freeHashObject(robj *o) { } } -void freeModuleObject(robj *o) { +void freeModuleObject(robj_roptr o) { moduleValue *mv = (moduleValue*)ptrFromObj(o); mv->type->free(mv->value); zfree(mv); } -void freeStreamObject(robj *o) { +void freeStreamObject(robj_roptr o) { freeStream((stream*)ptrFromObj(o)); } -void incrRefCount(robj *o) { +void incrRefCount(robj_roptr o) { if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++; } -void decrRefCount(robj *o) { +void decrRefCount(robj_roptr o) { if (o->refcount == 1) { switch(o->type) { case OBJ_STRING: freeStringObject(o); break; @@ -364,7 +370,7 @@ void decrRefCount(robj *o) { case OBJ_STREAM: freeStreamObject(o); break; default: serverPanic("Unknown object type"); break; } - zfree(o); + zfree(o.unsafe_robjcast()); } else { if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0"); if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--; @@ -374,7 +380,7 @@ void decrRefCount(robj *o) { /* This variant of decrRefCount() gets its argument as void, and is useful * as free method in data structures that expect a 'void free_object(void*)' * prototype for the free method. */ -void decrRefCountVoid(void *o) { +void decrRefCountVoid(const void *o) { decrRefCount((robj*)o); } @@ -395,7 +401,7 @@ robj *resetRefCount(robj *obj) { return obj; } -int checkType(client *c, robj *o, int type) { +int checkType(client *c, robj_roptr o, int type) { if (o->type != type) { addReplyAsync(c,shared.wrongtypeerr); return 1; @@ -403,7 +409,7 @@ int checkType(client *c, robj *o, int type) { return 0; } -int isSdsRepresentableAsLongLong(sds s, long long *llval) { +int isSdsRepresentableAsLongLong(const char *s, long long *llval) { return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR; } @@ -524,6 +530,10 @@ robj *getDecodedObject(robj *o) { } } +robj_roptr getDecodedObject(robj_roptr o) { + return getDecodedObject(o.unsafe_robjcast()); +} + /* Compare two string objects via strcmp() or strcoll() depending on flags. * Note that the objects may be integer-encoded. In such a case we * use ll2string() to get a string representation of the numbers on the stack @@ -592,7 +602,7 @@ int equalStringObjects(robj *a, robj *b) { } } -size_t stringObjectLen(robj *o) { +size_t stringObjectLen(robj_roptr o) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { return sdslen(szFromObj(o)); diff --git a/src/pubsub.cpp b/src/pubsub.cpp index e79073e02..900c931d4 100644 --- a/src/pubsub.cpp +++ b/src/pubsub.cpp @@ -120,7 +120,7 @@ void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) { * Pubsub low level API *----------------------------------------------------------------------------*/ -void freePubsubPattern(void *p) { +void freePubsubPattern(const void *p) { pubsubPattern *pat = (pubsubPattern *)p; decrRefCount(pat->pattern); diff --git a/src/rdb-s3.cpp b/src/rdb-s3.cpp index f28bd07d5..e32275ef2 100644 --- a/src/rdb-s3.cpp +++ b/src/rdb-s3.cpp @@ -6,7 +6,7 @@ extern "C" { #include /* Save the DB on disk. Return C_ERR on error, C_OK on success. */ -extern "C" int rdbSaveS3(char *s3bucket, rdbSaveInfo *rsi) +int rdbSaveS3(char *s3bucket, rdbSaveInfo *rsi) { int status = EXIT_FAILURE; int fd[2]; diff --git a/src/rdb.cpp b/src/rdb.cpp index ab03f45a9..12bbc8333 100644 --- a/src/rdb.cpp +++ b/src/rdb.cpp @@ -455,7 +455,7 @@ ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) { } /* Like rdbSaveRawString() gets a Redis object instead. */ -ssize_t rdbSaveStringObject(rio *rdb, robj *obj) { +ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj) { /* Avoid to decode the object, then encode it again, if the * object is already integer encoded. */ if (obj->encoding == OBJ_ENCODING_INT) { @@ -623,7 +623,7 @@ int rdbLoadBinaryFloatValue(rio *rdb, float *val) { } /* Save the object type of object "o". */ -int rdbSaveObjectType(rio *rdb, robj *o) { +int rdbSaveObjectType(rio *rdb, robj_roptr o) { switch (o->type) { case OBJ_STRING: return rdbSaveType(rdb,RDB_TYPE_STRING); @@ -752,7 +752,7 @@ size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) { /* Save a Redis object. * Returns -1 on error, number of bytes written on success. */ -ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) { +ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) { ssize_t n = 0, nwritten = 0; if (o->type == OBJ_STRING) { diff --git a/src/rdb.h b/src/rdb.h index f43b2f4f7..18cd3f3d4 100644 --- a/src/rdb.h +++ b/src/rdb.h @@ -133,7 +133,7 @@ int rdbSaveMillisecondTime(rio *rdb, long long t); long long rdbLoadMillisecondTime(rio *rdb, int rdbver); uint64_t rdbLoadLen(rio *rdb, int *isencoded); int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr); -int rdbSaveObjectType(rio *rdb, robj *o); +int rdbSaveObjectType(rio *rdb, robj_roptr o); int rdbLoadObjectType(rio *rdb); int rdbLoad(rdbSaveInfo *rsi); int rdbSaveBackground(rdbSaveInfo *rsi); @@ -144,13 +144,13 @@ int rdbSaveFile(char *filename, rdbSaveInfo *rsi); int rdbSaveFd(int fd, rdbSaveInfo *rsi); int rdbSaveS3(char *path, rdbSaveInfo *rsi); int rdbLoadS3(char *path, rdbSaveInfo *rsi); -ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key); +ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key); size_t rdbSavedObjectLen(robj *o); robj *rdbLoadObject(int type, rio *rdb, robj *key); void backgroundSaveDoneHandler(int exitcode, int bysignal); int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime); robj *rdbLoadStringObject(rio *rdb); -ssize_t rdbSaveStringObject(rio *rdb, robj *obj); +ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj); ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len); void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr); int rdbSaveBinaryDoubleValue(rio *rdb, double val); diff --git a/src/replication.cpp b/src/replication.cpp index 4e431cf73..27e05c803 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -889,29 +889,28 @@ void syncCommand(client *c) { void processReplconfUuid(client *c, robj *arg) { - try - { - if (arg->type != OBJ_STRING) - throw "Invalid UUID"; - - const char *remoteUUID = (const char*)ptrFromObj(arg); - if (strlen(remoteUUID) != 36) - throw "Invalid UUID"; - - if (uuid_parse(remoteUUID, c->uuid) != 0) - throw "Invalid UUID"; - - char szServerUUID[36 + 2]; // 1 for the '+', another for '\0' - szServerUUID[0] = '+'; - uuid_unparse(server.uuid, szServerUUID+1); - addReplyProto(c, szServerUUID, 37); - addReplyProto(c, "\r\n", 2); - } - catch (const char *szErr) - { - addReplyError(c, szErr); - return; - } + const char *remoteUUID = nullptr; + + if (arg->type != OBJ_STRING) + goto LError; + + remoteUUID = (const char*)ptrFromObj(arg); + if (strlen(remoteUUID) != 36) + goto LError; + + if (uuid_parse(remoteUUID, c->uuid) != 0) + goto LError; + + char szServerUUID[36 + 2]; // 1 for the '+', another for '\0' + szServerUUID[0] = '+'; + uuid_unparse(server.uuid, szServerUUID+1); + addReplyProto(c, szServerUUID, 37); + addReplyProto(c, "\r\n", 2); + return; + +LError: + addReplyError(c, "Invalid UUID"); + return; } /* REPLCONF