//解阻塞一个正在阻塞中的client void unblockClientWaitingData(client *c) { dictEntry *de; dictIterator *di; list *l; serverAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0); //创建一个字典的迭代器,指向的是造成client阻塞的键所组成的字典 di = dictGetIterator(c->bpop.keys); /* The client may wait for multiple keys, so unblock it for every key. */ //因为client可能被多个key所阻塞,所以要遍历所有的键 while((de = dictNext(di)) != NULL) { robj *key = dictGetKey(de); //获得key对象 /* Remove this client from the list of clients waiting for this key. */ //根据key找到对应的列表类型值,值保存着被阻塞的client,从中找c->db->blocking_keys中寻找 l = dictFetchValue(c->db->blocking_keys,key); serverAssertWithInfo(c,key,l != NULL); // 将阻塞的client从列表中移除 listDelNode(l,listSearchKey(l,c)); /* If the list is empty we need to remove it to avoid wasting memory */ //如果当前列表为空了,则从c->db->blocking_keys中将key删除 if (listLength(l) == 0) dictDelete(c->db->blocking_keys,key); } dictReleaseIterator(di); //释放迭代器 /* Cleanup the client structure */ //清空bpop.keys的所有节点 dictEmpty(c->bpop.keys,NULL); //如果保存有新添加的元素,则应该释放 if (c->bpop.target) { decrRefCount(c->bpop.target); c->bpop.target = NULL; } }
/* Unblock a client that's waiting in a blocking operation such as BLPOP. * You should never call this function directly, but unblockClient() instead. */ void unblockClientWaitingData(client *c) { dictEntry *de; dictIterator *di; list *l; serverAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0); di = dictGetIterator(c->bpop.keys); /* The client may wait for multiple keys, so unblock it for every key. */ while((de = dictNext(di)) != NULL) { robj *key = dictGetKey(de); /* Remove this client from the list of clients waiting for this key. */ l = dictFetchValue(c->db->blocking_keys,key); serverAssertWithInfo(c,key,l != NULL); listDelNode(l,listSearchKey(l,c)); /* If the list is empty we need to remove it to avoid wasting memory */ if (listLength(l) == 0) dictDelete(c->db->blocking_keys,key); } dictReleaseIterator(di); /* Cleanup the client structure */ dictEmpty(c->bpop.keys,NULL); if (c->bpop.target) { decrRefCount(c->bpop.target); c->bpop.target = NULL; } }
/* Create a quicklist from a single ziplist */ void listTypeConvert(robj *subject, int enc) { serverAssertWithInfo(NULL,subject,subject->type==OBJ_LIST); serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST); if (enc == OBJ_ENCODING_QUICKLIST) { size_t zlen = server.list_max_ziplist_size; int depth = server.list_compress_depth; subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr); subject->encoding = OBJ_ENCODING_QUICKLIST; } else { serverPanic("Unsupported list conversion"); } }
// 取消客户端对所有的键的监视,清理 EXEC dirty 标识状态由调用者决定 void unwatchAllKeys(client *c) { listIter li; listNode *ln; // 如果客户端没有监视key则直接返回 if (listLength(c->watched_keys) == 0) return; listRewind(c->watched_keys,&li); // 遍历客户端监视的key while((ln = listNext(&li))) { list *clients; watchedKey *wk; /* Lookup the watched key -> clients list and remove the client * from the list */ wk = listNodeValue(ln); // 从数据库中的watched_keys字典中查找出监视key的client clients = dictFetchValue(wk->db->watched_keys, wk->key); serverAssertWithInfo(c,NULL,clients != NULL); // 从client的链表中删除当前client节点 listDelNode(clients,listSearchKey(clients,c)); /* Kill the entry at all if this was the only client */ // 如果client链表为空,标识给key没有被监视 if (listLength(clients) == 0) // 从数据库的watched_keys中删除该key dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ // 从客户端的watched_keys中删除该节点 listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
/* Unwatch all the keys watched by this client. To clean the EXEC dirty * flag is up to the caller. * * 清空WATCH的键, 而清空EXEC脏标识的任务由调用者完成 */ void unwatchAllKeys(client *c) { listIter li; listNode *ln; /* 释放监控的键 */ if (listLength(c->watched_keys) == 0) return; listRewind(c->watched_keys,&li); while((ln = listNext(&li))) { list *clients; watchedKey *wk; /* Lookup the watched key -> clients list and remove the client * from the list */ wk = listNodeValue(ln); clients = dictFetchValue(wk->db->watched_keys, wk->key); serverAssertWithInfo(c,NULL,clients != NULL); listDelNode(clients,listSearchKey(clients,c)); /* Kill the entry at all if this was the only client */ if (listLength(clients) == 0) dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
//从对象中将字符串值转换为long double并存储在target中 int getLongDoubleFromObject(robj *o, long double *target) { long double value; char *eptr; if (o == NULL) { //对象不存在 value = 0; } else { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); //如果是字符串编码的两种类型 if (sdsEncodedObject(o)) { errno = 0; value = strtold(o->ptr, &eptr); //将字符串转换为long double类型 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || errno == ERANGE || isnan(value)) //转换失败返回-1 return C_ERR; } else if (o->encoding == OBJ_ENCODING_INT) { //整数编码 value = (long)o->ptr; //保存整数值 } else { serverPanic("Unknown string encoding"); } } *target = value; //将值存到传入参数中,返回0成功 return C_OK; }
/* Set a client in blocking mode for the specified key, with the specified * timeout */ void blockForKeys(client *c, robj **keys, int numkeys, mstime_t timeout, robj *target) { dictEntry *de; list *l; int j; c->bpop.timeout = timeout; c->bpop.target = target; if (target != NULL) incrRefCount(target); for (j = 0; j < numkeys; j++) { /* If the key already exists in the dict ignore it. */ if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue; incrRefCount(keys[j]); /* And in the other "side", to map keys -> clients */ de = dictFind(c->db->blocking_keys,keys[j]); if (de == NULL) { int retval; /* For every key we take a list of clients blocked for it */ l = listCreate(); retval = dictAdd(c->db->blocking_keys,keys[j],l); incrRefCount(keys[j]); serverAssertWithInfo(c,keys[j],retval == DICT_OK); } else { l = dictGetVal(de); } listAddNodeTail(l,c); } blockClient(c,BLOCKED_LIST); }
void brpoplpushCommand(client *c) { mstime_t timeout; if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS) != C_OK) return; robj *key = lookupKeyWrite(c->db, c->argv[1]); if (key == NULL) { if (c->flags & CLIENT_MULTI) { /* Blocking against an empty list in a multi state * returns immediately. */ addReply(c, shared.nullbulk); } else { /* The list is empty and the client blocks. */ blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]); } } else { if (key->type != OBJ_LIST) { addReply(c, shared.wrongtypeerr); } else { /* The list exists and has elements, so * the regular rpoplpushCommand is executed. */ serverAssertWithInfo(c,key,listTypeLength(key) > 0); rpoplpushCommand(c); } } }
int getDoubleFromObject(robj *o, double *target) { double value; char *eptr; if (o == NULL) { value = 0; } else { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { errno = 0; value = strtod(o->ptr, &eptr); if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || (errno == ERANGE && (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) || errno == EINVAL || isnan(value)) return C_ERR; } else if (o->encoding == OBJ_ENCODING_INT) { value = (long)o->ptr; } else { serverPanic("Unknown string encoding"); } } *target = value; return C_OK; }
/* Convert the set to specified encoding. The resulting dict (when converting * to a hash table) is presized to hold the number of elements in the original * set. */ void setTypeConvert(robj *setobj, int enc) { setTypeIterator *si; serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET && setobj->encoding == OBJ_ENCODING_INTSET); if (enc == OBJ_ENCODING_HT) { int64_t intele; dict *d = dictCreate(&setDictType,NULL); sds element; /* Presize the dict to avoid rehashing */ dictExpand(d,intsetLen(setobj->ptr)); /* To add the elements we extract integers and create redis objects */ si = setTypeInitIterator(setobj); while (setTypeNext(si,&element,&intele) != -1) { element = sdsfromlonglong(intele); serverAssert(dictAdd(d,element,NULL) == DICT_OK); } setTypeReleaseIterator(si); setobj->encoding = OBJ_ENCODING_HT; zfree(setobj->ptr); setobj->ptr = d; } else { serverPanic("Unsupported set conversion"); } }
int compareStringObjectsWithFlags(robj *a, robj *b, int flags) { serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING); char bufa[128], bufb[128], *astr, *bstr; size_t alen, blen, minlen; if (a == b) return 0; if (sdsEncodedObject(a)) { astr = a->ptr; alen = sdslen(astr); } else { alen = ll2string(bufa,sizeof(bufa),(long) a->ptr); astr = bufa; } if (sdsEncodedObject(b)) { bstr = b->ptr; blen = sdslen(bstr); } else { blen = ll2string(bufb,sizeof(bufb),(long) b->ptr); bstr = bufb; } if (flags & REDIS_COMPARE_COLL) { return strcoll(astr,bstr); } else { int cmp; minlen = (alen < blen) ? alen : blen; cmp = memcmp(astr,bstr,minlen); if (cmp == 0) return alen-blen; return cmp; } }
/* Add the key to the DB. It's up to the caller to increment the reference * counter of the value if needed. * * The program is aborted if the key already exists. */ void dbAdd(redisDb *db, robj *key, robj *val) { sds copy = sdsdup(key->ptr); int retval = dictAdd(db->dict, copy, val); serverAssertWithInfo(NULL,key,retval == C_OK); if (val->type == OBJ_LIST) signalListAsReady(db, key); if (server.cluster_enabled) slotToKeyAdd(key); }
size_t stringObjectLen(robj *o) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { return sdslen(o->ptr); } else { return sdigits10((long)o->ptr); } }
/* Compare the given object with the entry at the current position. */ int listTypeEqual(listTypeEntry *entry, robj *o) { if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { serverAssertWithInfo(NULL,o,sdsEncodedObject(o)); return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr)); } else { serverPanic("Unknown list encoding"); } }
/* Return the amount of memory used by the sds string at object->ptr * for a string object. */ size_t getStringObjectSdsUsedMemory(robj *o) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); switch(o->encoding) { case OBJ_ENCODING_RAW: return sdsZmallocSize(o->ptr); case OBJ_ENCODING_EMBSTR: return dmalloc_size(o)-sizeof(robj); default: return 0; /* Just integer encoding for now. */ } }
void setExpire(redisDb *db, robj *key, long long when) { dictEntry *kde, *de; /* Reuse the sds from the main dict in the expire dict */ kde = dictFind(db->dict,key->ptr); serverAssertWithInfo(NULL,key,kde != NULL); de = dictReplaceRaw(db->expires,dictGetKey(kde)); dictSetSignedIntegerVal(de,when); }
//转换ZIPLIST编码类型为quicklist类型,enc指定OBJ_ENCODING_QUICKLIST void listTypeConvert(robj *subject, int enc) { //确保subject的类型为列表类型,且编码为ziplist类型 serverAssertWithInfo(NULL,subject,subject->type==OBJ_LIST); serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST); //对制定enc编码为quicklist类型操作 if (enc == OBJ_ENCODING_QUICKLIST) { //以下两行都是由配置文件制定的参数 size_t zlen = server.list_max_ziplist_size; //最大ziplist大小 int depth = server.list_compress_depth; //压缩深度 //创建一个quicklist并将ptr指向的entry追加在quicklist末尾 subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr); //设置新的编码类型为OBJ_ENCODING_QUICKLIST subject->encoding = OBJ_ENCODING_QUICKLIST; } else { serverPanic("Unsupported list conversion"); } }
int isObjectRepresentableAsLongLong(robj *o, long long *llval) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (o->encoding == OBJ_ENCODING_INT) { if (llval) *llval = (long) o->ptr; return C_OK; } else { return string2ll(o->ptr,sdslen(o->ptr),llval) ? C_OK : C_ERR; } }
//返回字符串对象的字符串长度 size_t stringObjectLen(robj *o) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); //如果是字符串编码的两种类型 if (sdsEncodedObject(o)) { return sdslen(o->ptr); } else { //如果是整数编码类型 return sdigits10((long)o->ptr); //计算出整数值的位数返回 } }
//判断对象的ptr指向的值能否转换为long long类型,如果可以保存在llval中 int isObjectRepresentableAsLongLong(robj *o, long long *llval) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (o->encoding == OBJ_ENCODING_INT) { //如果本身就是整数 if (llval) *llval = (long) o->ptr; return C_OK; //成功返回0 } else { //字符串转换为longlong类型,成功返回0,失败返回-1 return string2ll(o->ptr,sdslen(o->ptr),llval) ? C_OK : C_ERR; } }
void debugCommand(client *c) { if (!strcasecmp(c->argv[1]->ptr,"segfault")) { *((char*)-1) = 'x'; } else if (!strcasecmp(c->argv[1]->ptr,"oom")) { void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */ zfree(ptr); addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"assert")) { if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]); serverAssertWithInfo(c,c->argv[0],1 == 2); } else if (!strcasecmp(c->argv[1]->ptr,"flushall")) { flushServerData(); addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) { flushServerData(); if (loadAppendOnlyFile(server.aof_filename) != C_OK) { addReply(c,shared.err); return; } serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF"); addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) { double dtime = strtod(c->argv[2]->ptr,NULL); long long utime = dtime*1000000; struct timespec tv; tv.tv_sec = utime / 1000000; tv.tv_nsec = (utime % 1000000) * 1000; nanosleep(&tv, NULL); addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) { sds errstr = sdsnewlen("-",1); errstr = sdscatsds(errstr,c->argv[2]->ptr); errstr = sdsmapchars(errstr,"\n\r"," ",2); /* no newlines in errors. */ errstr = sdscatlen(errstr,"\r\n",2); addReplySds(c,errstr); } else if (!strcasecmp(c->argv[1]->ptr,"structsize") && c->argc == 2) { sds sizes = sdsempty(); sizes = sdscatprintf(sizes,"bits:%d ",(sizeof(void*) == 8)?64:32); sizes = sdscatprintf(sizes,"job:%d ", (int)sizeof(job)); sizes = sdscatprintf(sizes,"queue:%d ", (int)sizeof(queue)); sizes = sdscatprintf(sizes,"robj:%d ",(int)sizeof(robj)); sizes = sdscatprintf(sizes,"dictentry:%d ",(int)sizeof(dictEntry)); sizes = sdscatprintf(sizes,"sdshdr5:%d ",(int)sizeof(struct sdshdr5)); sizes = sdscatprintf(sizes,"sdshdr8:%d ",(int)sizeof(struct sdshdr8)); sizes = sdscatprintf(sizes,"sdshdr16:%d ",(int)sizeof(struct sdshdr16)); sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32)); sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64)); addReplyBulkSds(c,sizes); } else { addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'", (char*)c->argv[1]->ptr); } }
//比较列表类型的entry结构与对象的entry节点的值是否等,相等返回1 int listTypeEqual(listTypeEntry *entry, robj *o) { //对列表对象编码为quicklist类型操作 if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { //确保objptr的编码类型是简单动态字符串类型的RAW或EMBSTR serverAssertWithInfo(NULL,o,sdsEncodedObject(o)); //比较listTypeEntry结构中的entry值和给定的对象的值 return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr)); } else { serverPanic("Unknown list encoding"); } }
/* 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) { dictEntry *de; /* No expire? return ASAP */ if (dictSize(db->expires) == 0 || (de = dictFind(db->expires,key->ptr)) == NULL) return -1; /* The entry was found in the expire dict, this means it should also * be present in the main dict (safety check). */ serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL); return dictGetSignedIntegerVal(de); }
/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or * 0 if the client was not subscribed to the specified channel. */ int smempubsubUnsubscribeChannel(client *c, robj *channel, int notify) { dictEntry *de; list *clients; listNode *ln; int retval = 0; /* Remove the channel from the client -> channels hash table */ incrRefCount(channel); /* channel may be just a pointer to the same object we have in the hash tables. Protect it... */ if (dictDelete(c->smempubsub_channels,channel) == DICT_OK) { retval = 1; /* Remove the client from the channel -> clients list hash table */ de = dictFind(server.smempubsub_channels,channel); serverAssertWithInfo(c,NULL,de != NULL); clients = dictGetVal(de); ln = listSearchKey(clients,c); serverAssertWithInfo(c,NULL,ln != NULL); listDelNode(clients,ln); if (listLength(clients) == 0) { /* Free the list and associated hash entry at all if this was * the latest client, so that it will be possible to abuse * Redis PUBSUB creating millions of channels. */ dictDelete(server.smempubsub_channels,channel); } } /* Notify the client */ if (notify) { addReply(c,shared.mbulkhdr[3]); addReply(c,shared.unsubscribebulk); addReplyBulk(c,channel); addReplyLongLong(c,dictSize(c->smempubsub_channels)); } decrRefCount(channel); /* it is finally safe to release it */ return retval; }
/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT * and PEXPIREAT. Because the commad second argument may be relative or absolute * the "basetime" argument is used to signal what the base time is (either 0 * for *AT variants of the command, or the current time for relative expires). * * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for * the argv[2] parameter. The basetime is always specified in milliseconds. */ void expireGenericCommand(client *c, long long basetime, int unit) { robj *key = c->argv[1], *param = c->argv[2]; long long when; /* unix time in milliseconds when the key will expire. */ if (getLongLongFromObjectOrReply(c, param, &when, NULL) != C_OK) return; if (unit == UNIT_SECONDS) when *= 1000; when += basetime; /* No key, return zero. */ if (lookupKeyWrite(c->db,key) == NULL) { addReply(c,shared.czero); return; } /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past * should never be executed as a DEL when load the AOF or in the context * of a slave instance. * * Instead we take the other branch of the IF statement setting an expire * (possibly in the past) and wait for an explicit DEL from the master. */ if (when <= mstime() && !server.loading && !server.masterhost) { robj *aux; serverAssertWithInfo(c,key,dbDelete(c->db,key)); server.dirty++; /* Replicate/AOF this as an explicit DEL. */ aux = createStringObject("DEL",3); rewriteClientCommandVector(c,2,aux,key); decrRefCount(aux); signalModifiedKey(c->db,key); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id); addReply(c, shared.cone); return; } else { setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id); server.dirty++; return; } }
int getLongLongFromObject(robj *o, long long *target) { long long value; if (o == NULL) { value = 0; } else { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR; } else if (o->encoding == OBJ_ENCODING_INT) { value = (long)o->ptr; } else { serverPanic("Unknown string encoding"); } } if (target) *target = value; return C_OK; }
/* Set a client in blocking mode for the specified key (list or stream), with * the specified timeout. The 'type' argument is BLOCKED_LIST or BLOCKED_STREAM * depending on the kind of operation we are waiting for an empty key in * order to awake the client. The client is blocked for all the 'numkeys' * keys as in the 'keys' argument. When we block for stream keys, we also * provide an array of streamID structures: clients will be unblocked only * when items with an ID greater or equal to the specified one is appended * to the stream. */ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids) { dictEntry *de; list *l; int j; c->bpop.timeout = timeout; c->bpop.target = target; if (target != NULL) incrRefCount(target); for (j = 0; j < numkeys; j++) { /* The value associated with the key name in the bpop.keys dictionary * is NULL for lists, or the stream ID for streams. */ void *key_data = NULL; if (btype == BLOCKED_STREAM) { key_data = zmalloc(sizeof(streamID)); memcpy(key_data,ids+j,sizeof(streamID)); } /* If the key already exists in the dictionary ignore it. */ if (dictAdd(c->bpop.keys,keys[j],key_data) != DICT_OK) { zfree(key_data); continue; } incrRefCount(keys[j]); /* And in the other "side", to map keys -> clients */ de = dictFind(c->db->blocking_keys,keys[j]); if (de == NULL) { int retval; /* For every key we take a list of clients blocked for it */ l = listCreate(); retval = dictAdd(c->db->blocking_keys,keys[j],l); incrRefCount(keys[j]); serverAssertWithInfo(c,keys[j],retval == DICT_OK); } else { l = dictGetVal(de); } listAddNodeTail(l,c); } blockClient(c,btype); }
// keys是一个key的数组,个数为numkeys个 // timeout保存超时时间 // target保存解除阻塞时的key对象,用于BRPOPLPUSH函数 // 根据给定的key将client阻塞 void blockForKeys(client *c, robj **keys, int numkeys, mstime_t timeout, robj *target) { dictEntry *de; list *l; int j; //设置超时时间和target c->bpop.timeout = timeout; c->bpop.target = target; //增加target的引用计数 if (target != NULL) incrRefCount(target); //将当前client的numkeys个key设置为阻塞 for (j = 0; j < numkeys; j++) { /* If the key already exists in the dict ignore it. */ //bpop.keys记录所有造成client阻塞的键 //将要阻塞的键放入bpop.keys字典中 if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue; //当前的key引用计数加1 incrRefCount(keys[j]); /* And in the other "side", to map keys -> clients */ //db->blocking_keys是一个字典,字典的键为bpop.keys中的一个键,值是一个列表,保存着所有被该键阻塞的client //当前造成client被阻塞的键有没有当前的key de = dictFind(c->db->blocking_keys,keys[j]); if (de == NULL) { //没有当前的key,添加进去 int retval; /* For every key we take a list of clients blocked for it */ //创建一个列表 l = listCreate(); //将造成阻塞的键和列表添加到db->blocking_keys字典中 retval = dictAdd(c->db->blocking_keys,keys[j],l); incrRefCount(keys[j]); serverAssertWithInfo(c,keys[j],retval == DICT_OK); } else { //如果已经有了,则当前key的值保存起来,值是一个列表 l = dictGetVal(de); } listAddNodeTail(l,c); //将当前client加入到阻塞的client的列表 } blockClient(c,BLOCKED_LIST); //阻塞client }
//从对象中将字符串值转换为long long并存储在target中 int getLongLongFromObject(robj *o, long long *target) { long long value; if (o == NULL) { //对象不存在 value = 0; } else { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); //如果是字符串编码的两种类型 if (sdsEncodedObject(o)) { //转换失败发送-1,成功保存值到value中 if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR; } else if (o->encoding == OBJ_ENCODING_INT) { //整型编码 value = (long)o->ptr; //保存整数值 } else { serverPanic("Unknown string encoding"); } } if (target) *target = value; //将值存到传入参数中,返回0成功 return C_OK; }
int getLongLongFromObject(robj *o, long long *target) { long long value; char *eptr; if (o == NULL) { value = 0; } else { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { errno = 0; value = strtoll(o->ptr, &eptr, 10); if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || errno == ERANGE) return VR_ERROR; } else if (o->encoding == OBJ_ENCODING_INT) { value = (long)o->ptr; } else { serverPanic("Unknown string encoding"); } } if (target) *target = value; return VR_OK; }