TEST(sds, sds) { // struct sdshdr *sh; sds x = sdsnew("foo"), y; TEST_ASSERT_TRUE( sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) sdsfree(x); x = sdsnewlen("foo",2); TEST_ASSERT_TRUE( sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) x = sdscat(x,"bar"); TEST_ASSERT_TRUE( sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); x = sdscpy(x,"a"); TEST_ASSERT_TRUE( sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); TEST_ASSERT_TRUE( sdslen(x) == 33 && memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) sdsfree(x); x = sdscatprintf(sdsempty(),"%d",123); TEST_ASSERT_TRUE( sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) sdsfree(x); x = sdsnew("xxciaoyyy"); sdstrim(x,"xy"); TEST_ASSERT_TRUE( sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) y = sdsdup(x); sdsrange(y,1,1); TEST_ASSERT_TRUE( sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,1,-1); TEST_ASSERT_TRUE( sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,-2,-1); TEST_ASSERT_TRUE( sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,2,1); TEST_ASSERT_TRUE( sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,1,100); TEST_ASSERT_TRUE( sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,100,100); TEST_ASSERT_TRUE( sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); sdsfree(x); x = sdsnew("foo"); y = sdsnew("foa"); TEST_ASSERT_TRUE( sdscmp(x,y) > 0) sdsfree(y); sdsfree(x); x = sdsnew("bar"); y = sdsnew("bar"); TEST_ASSERT_TRUE( sdscmp(x,y) == 0) sdsfree(y); sdsfree(x); x = sdsnew("aar"); y = sdsnew("bar"); TEST_ASSERT_TRUE( sdscmp(x,y) < 0) sdsfree(y); sdsfree(x); // x = sdsnewlen("\a\n\0foo\r",7); // y = sdscatrepr(sdsempty(),x,sdslen(x)); // TEST_ASSERT_TRUE( // memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) #if 0 { int oldfree; sdsfree(x); x = sdsnew("0"); sh = (void*) (x-(sizeof(struct sdshdr))); TEST_ASSERT_TRUE( sh->len == 1 && sh->free == 0); x = sdsMakeRoomFor(x,1); sh = (void*) (x-(sizeof(struct sdshdr))); TEST_ASSERT_TRUE( sh->len == 1 && sh->free > 0); oldfree = sh->free; x[1] = '1'; sdsIncrLen(x,1); TEST_ASSERT_TRUE( x[0] == '0' && x[1] == '1'); TEST_ASSERT_TRUE( sh->len == 2); TEST_ASSERT_TRUE( sh->free == oldfree-1); } #endif }
static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) { client c = privdata; void *reply = NULL; UNUSED(el); UNUSED(fd); UNUSED(mask); /* Calculate latency only for the first read event. This means that the * server already sent the reply and we need to parse it. Parsing overhead * is not part of the latency, so calculate it only once, here. */ if (c->latency < 0) c->latency = ustime()-(c->start); if (redisBufferRead(c->context) != REDIS_OK) { fprintf(stderr,"Error: %s\n",c->context->errstr); exit(1); } else { while(c->pending) { if (redisGetReply(c->context,&reply) != REDIS_OK) { fprintf(stderr,"Error: %s\n",c->context->errstr); exit(1); } if (reply != NULL) { if (reply == (void*)REDIS_REPLY_ERROR) { fprintf(stderr,"Unexpected error reply, exiting...\n"); exit(1); } if (config.showerrors) { static time_t lasterr_time = 0; time_t now = time(NULL); redisReply *r = reply; if (r->type == REDIS_REPLY_ERROR && lasterr_time != now) { lasterr_time = now; printf("Error from server: %s\n", r->str); } } freeReplyObject(reply); /* This is an OK for prefix commands such as auth and select.*/ if (c->prefix_pending > 0) { c->prefix_pending--; c->pending--; /* Discard prefix commands on first response.*/ if (c->prefixlen > 0) { size_t j; sdsrange(c->obuf, c->prefixlen, -1); /* We also need to fix the pointers to the strings * we need to randomize. */ for (j = 0; j < c->randlen; j++) c->randptr[j] -= c->prefixlen; c->prefixlen = 0; } continue; } if (config.requests_finished < config.requests) config.latency[config.requests_finished++] = c->latency; c->pending--; if (c->pending == 0) { clientDone(c); break; } } else { break; } } } }
void processInputBuffer(redisClient *c) { again: /* Before to process the input buffer, make sure the client is not * waitig for a blocking operation such as BLPOP. Note that the first * iteration the client is never blocked, otherwise the processInputBuffer * would not be called at all, but after the execution of the first commands * in the input buffer the client may be blocked, and the "goto again" * will try to reiterate. The following line will make it return asap. */ if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return; if (c->bulklen == -1) { /* Read the first line of the query */ char *p = strchr(c->querybuf,'\n'); size_t querylen; if (p) { sds query, *argv; int argc, j; query = c->querybuf; c->querybuf = sdsempty(); querylen = 1+(p-(query)); if (sdslen(query) > querylen) { /* leave data after the first line of the query in the buffer */ c->querybuf = sdscatlen(c->querybuf,query+querylen,sdslen(query)-querylen); } *p = '\0'; /* remove "\n" */ if (*(p-1) == '\r') *(p-1) = '\0'; /* and "\r" if any */ sdsupdatelen(query); /* Now we can split the query in arguments */ argv = sdssplitlen(query,sdslen(query)," ",1,&argc); sdsfree(query); if (c->argv) zfree(c->argv); c->argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (sdslen(argv[j])) { c->argv[c->argc] = createObject(REDIS_STRING,argv[j]); c->argc++; } else { sdsfree(argv[j]); } } zfree(argv); if (c->argc) { /* Execute the command. If the client is still valid * after processCommand() return and there is something * on the query buffer try to process the next command. */ if (processCommand(c) && sdslen(c->querybuf)) goto again; } else { /* Nothing to process, argc == 0. Just process the query * buffer if it's not empty or return to the caller */ if (sdslen(c->querybuf)) goto again; } return; } else if (sdslen(c->querybuf) >= REDIS_REQUEST_MAX_SIZE) { redisLog(REDIS_VERBOSE, "Client protocol error"); freeClient(c); return; } } else { /* Bulk read handling. Note that if we are at this point the client already sent a command terminated with a newline, we are reading the bulk data that is actually the last argument of the command. */ int qbl = sdslen(c->querybuf); if (c->bulklen <= qbl) { /* Copy everything but the final CRLF as final argument */ c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2); c->argc++; c->querybuf = sdsrange(c->querybuf,c->bulklen,-1); /* Process the command. If the client is still valid after * the processing and there is more data in the buffer * try to parse it. */ if (processCommand(c) && sdslen(c->querybuf)) goto again; return; } } }
/* Use this function to handle a read event on the descriptor. It will try * and read some bytes from the socket and feed them to the reply parser. * * After this function is called, you may use redisContextReadReply to * see if there is a reply available. */ int redisBufferRead(redisContext *c) { char buf[1024*16]; int nread; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; #ifndef HIREDIS_WIN nread = read(c->fd,buf,sizeof(buf)); if (nread == -1) { if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) { /* Try again later */ #else nread = recv(c->fd,buf,sizeof(buf),0); if (nread == -1) { errno = WSAGetLastError(); if ((errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK) && !(c->flags & REDIS_BLOCK)) { /* Try again later */ #endif } else { __redisSetError(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } } else if (nread == 0) { __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); return REDIS_ERR; } else { if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } } return REDIS_OK; } /* Write the output buffer to the socket. * * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was * succesfully written to the socket. When the buffer is empty after the * write operation, "done" is set to 1 (if given). * * Returns REDIS_ERR if an error occured trying to write and sets * c->errstr to hold the appropriate error string. */ int redisBufferWrite(redisContext *c, int *done) { int nwritten; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; if (sdslen(c->obuf) > 0) { #ifndef HIREDIS_WIN nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); if (nwritten == -1) { if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) { /* Try again later */ #else nwritten = send(c->fd,c->obuf,sdslen(c->obuf),0); if (nwritten == -1) { errno = WSAGetLastError(); if ((errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK) && !(c->flags & REDIS_BLOCK)) { /* Try again later */ #endif } else { __redisSetError(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } } else if (nwritten > 0) { if (nwritten == (signed)sdslen(c->obuf)) { sdsfree(c->obuf); c->obuf = sdsempty(); } else { c->obuf = sdsrange(c->obuf,nwritten,-1); } } } if (done != NULL) *done = (sdslen(c->obuf) == 0); return REDIS_OK; } /* Internal helper function to try and get a reply from the reader, * or set an error in the context otherwise. */ int redisGetReplyFromReader(redisContext *c, void **reply) { if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } return REDIS_OK; } int redisGetReply(redisContext *c, void **reply) { int wdone = 0; void *aux = NULL; /* Try to read pending replies */ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ if (aux == NULL && c->flags & REDIS_BLOCK) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) return REDIS_ERR; } while (!wdone); /* Read until there is a reply */ do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } /* Set reply object */ if (reply != NULL) *reply = aux; return REDIS_OK; } /* Helper function for the redisAppendCommand* family of functions. * * Write a formatted command to the output buffer. When this family * is used, you need to call redisGetReply yourself to retrieve * the reply (or replies in pub/sub). */ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) { sds newbuf; newbuf = sdscatlen(c->obuf,cmd,len); if (newbuf == NULL) { __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); return REDIS_ERR; } c->obuf = newbuf; return REDIS_OK; }
int main(void) { { sds x = sdsnew("foo"), y; test_cond("Create a string and obtain the length", sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) sdsfree(x); x = sdsnewlen("foo",2); test_cond("Create a string with specified length", sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) x = sdscat(x,"bar"); test_cond("Strings concatenation", sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); x = sdscpy(x,"a"); test_cond("sdscpy() against an originally longer string", sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); test_cond("sdscpy() against an originally shorter string", sdslen(x) == 33 && memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) sdsfree(x); x = sdscatprintf(sdsempty(),"%d",123); test_cond("sdscatprintf() seems working in the base case", sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) sdsfree(x); x = sdstrim(sdsnew("xxciaoyyy"),"xy"); test_cond("sdstrim() correctly trims characters", sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) y = sdsrange(sdsdup(x),1,1); test_cond("sdsrange(...,1,1)", sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) sdsfree(y); y = sdsrange(sdsdup(x),1,-1); test_cond("sdsrange(...,1,-1)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsrange(sdsdup(x),-2,-1); test_cond("sdsrange(...,-2,-1)", sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) sdsfree(y); y = sdsrange(sdsdup(x),2,1); test_cond("sdsrange(...,2,1)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); y = sdsrange(sdsdup(x),1,100); test_cond("sdsrange(...,1,100)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsrange(sdsdup(x),100,100); test_cond("sdsrange(...,100,100)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); sdsfree(x); x = sdsnew("foo"); y = sdsnew("foa"); test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) sdsfree(y); sdsfree(x); x = sdsnew("bar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) sdsfree(y); sdsfree(x); x = sdsnew("aar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) } test_report() }
int main(void) { { sdshdr *sh; sds x = sdsnew("foo"), y; test_cond("Create a string and obtain the length", sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) sdsfree(x); x = sdsnewlen("foo",2); test_cond("Create a string with specified length", sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) x = sdscat(x,"bar"); test_cond("Strings concatenation", sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); x = sdscpy(x,"a"); test_cond("sdscpy() against an originally longer string", sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); test_cond("sdscpy() against an originally shorter string", sdslen(x) == 33 && memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) sdsfree(x); x = sdscatprintf(sdsempty(),"%d",123); test_cond("sdscatprintf() seems working in the base case", sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) sdsfree(x); x = sdsnew("xxciaoyyy"); sdstrim(x,"xy"); test_cond("sdstrim() correctly trims characters", sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) y = sdsdup(x); sdsrange(y,1,1); test_cond("sdsrange(...,1,1)", sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,1,-1); test_cond("sdsrange(...,1,-1)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,-2,-1); test_cond("sdsrange(...,-2,-1)", sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,2,1); test_cond("sdsrange(...,2,1)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,1,100); test_cond("sdsrange(...,1,100)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) sdsfree(y); y = sdsdup(x); sdsrange(y,100,100); test_cond("sdsrange(...,100,100)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0) sdsfree(y); sdsfree(x); x = sdsnew("foo"); y = sdsnew("foa"); test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) sdsfree(y); sdsfree(x); x = sdsnew("bar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) sdsfree(y); sdsfree(x); x = sdsnew("aar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) sdsfree(y); sdsfree(x); x = sdsnewlen("\a\n\0foo\r",7); y = sdscatrepr(sdsempty(),x,sdslen(x)); test_cond("sdscatrepr(...data...)", memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) sdsfree(y); { size_t oldfree; sdsfree(x); x = sdsnew("0"); sh = sds_start(x); test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); x = sdsMakeRoomFor(x,1); sh = sds_start(x); test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); oldfree = sh->free; x[1] = '1'; sdsIncrLen(x,1); test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); test_cond("sdsIncrLen() -- len", sh->len == 2); test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); sdsfree(x); } x = sdsnew("0FoO1bar\n"); sdstolower(x); test_cond("sdstolower(...)", memcmp(x,"0foo1bar\n\0",10) == 0) sdsfree(x); x = sdsnew("0FoO1bar\n"); sdstoupper(x); test_cond("sdstoupper(...)", memcmp(x,"0FOO1BAR\n\0",10) == 0) sdsfree(x); } test_report() return 0; }
/* Write the append only file buffer on disk. * * Since we are required to write the AOF before replying to the client, * and the only way the client socket can get a write is entering when the * the event loop, we accumulate all the AOF writes in a memory * buffer and write it on disk using this function just before entering * the event loop again. * * About the 'force' argument: * * When the fsync policy is set to 'everysec' we may delay the flush if there * is still an fsync() going on in the background thread, since for instance * on Linux write(2) will be blocked by the background fsync anyway. * When this happens we remember that there is some aof buffer to be * flushed ASAP, and will try to do that in the serverCron() function. * * However if force is set to 1 we'll write regardless of the background * fsync. */ #define AOF_WRITE_LOG_ERROR_RATE 30 /* Seconds between errors logging. */ void flushAppendOnlyFile(int force) { ssize_t nwritten; int sync_in_progress = 0; if (sdslen(server.aof_buf) == 0) return; if (server.aof_fsync == AOF_FSYNC_EVERYSEC) sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0; if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) { /* With this append fsync policy we do background fsyncing. * If the fsync is still in progress we can try to delay * the write for a couple of seconds. */ if (sync_in_progress) { if (server.aof_flush_postponed_start == 0) { /* No previous write postponinig, remember that we are * postponing the flush and return. */ server.aof_flush_postponed_start = server.unixtime; return; } else if (server.unixtime - server.aof_flush_postponed_start < 2) { /* We were already waiting for fsync to finish, but for less * than two seconds this is still ok. Postpone again. */ return; } /* Otherwise fall trough, and go write since we can't wait * over two seconds. */ server.aof_delayed_fsync++; redisLog(REDIS_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis."); } } /* If you are following this code path, then we are going to write so * set reset the postponed flush sentinel to zero. */ server.aof_flush_postponed_start = 0; /* We want to perform a single write. This should be guaranteed atomic * at least if the filesystem we are writing is a real physical one. * While this will save us against the server being killed I don't think * there is much to do about the whole server stopping for power problems * or alike */ nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf)); if (nwritten != (signed)sdslen(server.aof_buf)) { static time_t last_write_error_log = 0; int can_log = 0; /* Limit logging rate to 1 line per AOF_WRITE_LOG_ERROR_RATE seconds. */ if ((server.unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) { can_log = 1; last_write_error_log = server.unixtime; } /* Lof the AOF write error and record the error code. */ if (nwritten == -1) { if (can_log) { redisLog(REDIS_WARNING,"Error writing to the AOF file: %s", strerror(errno)); server.aof_last_write_errno = errno; } } else { if (can_log) { redisLog(REDIS_WARNING,"Short write while writing to " "the AOF file: (nwritten=%lld, " "expected=%lld)", (long long)nwritten, (long long)sdslen(server.aof_buf)); } if (ftruncate(server.aof_fd, server.aof_current_size) == -1) { if (can_log) { redisLog(REDIS_WARNING, "Could not remove short write " "from the append-only file. Redis may refuse " "to load the AOF the next time it starts. " "ftruncate: %s", strerror(errno)); } } else { /* If the ftrunacate() succeeded we can set nwritten to * -1 since there is no longer partial data into the AOF. */ nwritten = -1; } server.aof_last_write_errno = ENOSPC; } /* Handle the AOF write error. */ if (server.aof_fsync == AOF_FSYNC_ALWAYS) { /* We can't recover when the fsync policy is ALWAYS since the * reply for the client is already in the output buffers, and we * have the contract with the user that on acknowledged write data * is synched on disk. */ redisLog(REDIS_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting..."); exit(1); } else { /* Recover from failed write leaving data into the buffer. However * set an error to stop accepting writes as long as the error * condition is not cleared. */ server.aof_last_write_status = REDIS_ERR; /* Trim the sds buffer if there was a partial write, and there * was no way to undo it with ftruncate(2). */ if (nwritten > 0) { server.aof_current_size += nwritten; sdsrange(server.aof_buf,nwritten,-1); } return; /* We'll try again on the next call... */ } } else { /* Successful write(2). If AOF was in error state, restore the * OK state and log the event. */ if (server.aof_last_write_status == REDIS_ERR) { redisLog(REDIS_WARNING, "AOF write error looks solved, Redis can write again."); server.aof_last_write_status = REDIS_OK; } } server.aof_current_size += nwritten; /* Re-use AOF buffer when it is small enough. The maximum comes from the * arena size of 4k minus some overhead (but is otherwise arbitrary). */ if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) { sdsclear(server.aof_buf); } else { sdsfree(server.aof_buf); server.aof_buf = sdsempty(); } /* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are * children doing I/O in the background. */ if (server.aof_no_fsync_on_rewrite && (server.aof_child_pid != -1 || server.rdb_child_pid != -1)) return; /* Perform the fsync if needed. */ if (server.aof_fsync == AOF_FSYNC_ALWAYS) { /* aof_fsync is defined as fdatasync() for Linux in order to avoid * flushing metadata. */ aof_fsync(server.aof_fd); /* Let's try to get this data on the disk */ server.aof_last_fsync = server.unixtime; } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC && server.unixtime > server.aof_last_fsync)) { if (!sync_in_progress) aof_background_fsync(server.aof_fd); server.aof_last_fsync = server.unixtime; } }
/* Use this function to handle a read event on the descriptor. It will try * and read some bytes from the socket and feed them to the reply parser. * * After this function is called, you may use redisContextReadReply to * see if there is a reply available. */ int redisBufferRead(redisContext *c) { char buf[1024*16]; int nread; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; #ifdef WIN32 nread = recv(c->fd,buf,sizeof(buf),0); #else nread = read(c->fd,buf,sizeof(buf)); #endif if (nread == -1) { #ifdef WIN32 errno = WSAGetLastError(); if ((errno == WSAEWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == WSAEINTR)) { #else if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { #endif /* Try again later */ } else { __redisSetError(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } } else if (nread == 0) { __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); return REDIS_ERR; } else { if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } } return REDIS_OK; } /* Write the output buffer to the socket. * * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was * succesfully written to the socket. When the buffer is empty after the * write operation, "done" is set to 1 (if given). * * Returns REDIS_ERR if an error occured trying to write and sets * c->errstr to hold the appropriate error string. */ int redisBufferWrite(redisContext *c, int *done) { int nwritten; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; if (sdslen(c->obuf) > 0) { #ifdef WIN32 nwritten = send(c->fd,c->obuf,sdslen(c->obuf),0); #else nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); #endif if (nwritten == -1) { #ifdef WIN32 errno = WSAGetLastError(); if ((errno == WSAEWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == WSAEINTR)) { #else if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { #endif /* Try again later */ } else { __redisSetError(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } } else if (nwritten > 0) { if (nwritten == (signed)sdslen(c->obuf)) { sdsfree(c->obuf); c->obuf = sdsempty(); } else { c->obuf = sdsrange(c->obuf,nwritten,-1); } } } if (done != NULL) *done = (sdslen(c->obuf) == 0); return REDIS_OK; } /* Internal helper function to try and get a reply from the reader, * or set an error in the context otherwise. */ int redisGetReplyFromReader(redisContext *c, void **reply) { if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } return REDIS_OK; } int redisGetReply(redisContext *c, void **reply) { int wdone = 0; void *aux = NULL; /* Try to read pending replies */ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ if (aux == NULL && c->flags & REDIS_BLOCK) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) return REDIS_ERR; } while (!wdone); /* Read until there is a reply */ do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } /* Set reply object */ if (reply != NULL) *reply = aux; return REDIS_OK; }
void onClientReadable( aeEventLoop *el , int connfd , void *privdata , int mask ) { appnetServer *serv = servG; ssize_t nread; unsigned int readlen,rcvbuflen,datalen; int worker_id = servG->connlist[connfd].worker_id; int thid = servG->connlist[connfd].reactor_id; char buffer[TMP_BUFFER_LENGTH]; while (1) { nread = 0; memset( &buffer , 0 , sizeof( buffer ) ); nread = read( connfd , &buffer , sizeof( buffer ) ); if (nread == -1 && errno == EAGAIN) { return; /* No more data ready. */ } if (nread == 0) { onCloseByClient( el , privdata , serv , &serv->connlist[connfd] ); return; /* Close event */ } else if (nread > 0) { /* Append client recv_buffer */ servG->connlist[connfd].recv_buffer = sdscatlen( servG->connlist[connfd].recv_buffer , &buffer , nread ); int ret = parseRequestMessage( connfd , servG->connlist[connfd].recv_buffer , sdslen( servG->connlist[connfd].recv_buffer ) ); if (ret == BREAK_RECV) { int complete_length = servG->reactor_threads[thid].hh->complete_length; if (complete_length > 0) { sdsrange( servG->connlist[connfd].recv_buffer , complete_length , -1 ); } else { sdsclear( servG->connlist[connfd].recv_buffer ); } break; } else if (ret == CONTINUE_RECV) { continue; } else if (ret == CLOSE_CONNECT) { freeClient( &servG->connlist[connfd] ); return; } return; } else { printf( "Recv Errno=%d,Err=%s \n" , errno , strerror( errno ) ); } } }
static int process_multibulk_buffer(sub_client *c) { char *newline = NULL; int pos = 0, ok; long long ll; if (c->multi_bulk_len == 0) { /* Multi bulk length cannot be read without a \r\n */ newline = strchr(c->read_buf, '\r'); if (newline == NULL) { if (sdslen(c->read_buf) > MAX_INLINE_READ) { srv_log(LOG_ERROR, "protocol error: too big mbulk string"); set_protocol_err(c, 0); } return SUBCLI_ERR; } /* Buffer should also contain \n */ if (newline-(c->read_buf) > ((signed)sdslen(c->read_buf)-2)) { return SUBCLI_ERR; } ok = string2ll(c->read_buf+1, newline-(c->read_buf+1), &ll); if (!ok || ll > SUB_READ_BUF_LEN) { srv_log(LOG_ERROR, "protocol error: invalid multibulk length"); set_protocol_err(c, pos); return SUBCLI_ERR; } pos = (newline - c->read_buf) + 2; if (ll <= 0) { sdsrange(c->read_buf, pos, -1); return SUBCLI_OK; } c->multi_bulk_len = ll; c->argv = malloc(sizeof(sds) * c->multi_bulk_len); } while (c->multi_bulk_len) { /* Read bulk length if unknown */ if (c->bulk_len == -1) { newline = strchr(c->read_buf + pos, '\r'); if (newline == NULL) { if (sdslen(c->read_buf) > MAX_INLINE_READ) { srv_log(LOG_ERROR, "protocol error: too big bulk count string"); set_protocol_err(c, 0); return SUBCLI_ERR; } break; } /* Buffer should also contain \n */ if (newline-(c->read_buf) > ((signed)sdslen(c->read_buf)-2)) { break; } if (c->read_buf[pos] != '$') { srv_log(LOG_ERROR, "Protocol error: expected '$', got '%c'", c->read_buf[pos]); set_protocol_err(c, pos); return SUBCLI_ERR; } ok = string2ll(c->read_buf+pos+1, newline-(c->read_buf+pos+1), &ll); if (!ok || ll < 0 || ll > MAX_BULK_LEN) { srv_log(LOG_ERROR, "Protocol error: invalid bulk length"); set_protocol_err(c, pos); return SUBCLI_ERR; } pos += newline - (c->read_buf + pos) + 2; c->bulk_len = ll; } /* Read bulk argument */ if (sdslen(c->read_buf) - pos < (unsigned)(c->bulk_len + 2)) { /* Not enough data (+2 == trailing \r\n) */ break; } else { /*c->argv[c->argc++] = create_string_obj(c->read_buf+pos, c->bulk_len);*/ c->argv[c->argc++] = sdsnewlen(c->read_buf+pos, c->bulk_len); pos += c->bulk_len + 2; } c->bulk_len = -1; c->multi_bulk_len--; } /* Trim to pos */ if (pos) sdsrange(c->read_buf, pos, -1); /* We're done when c->multibulk == 0 */ if (c->multi_bulk_len == 0) return SUBCLI_OK; /* Still not read to process the command */ return SUBCLI_ERR; }
// 这其实是一个回调函数,会作为参数传入另外一个函数中 static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) { client c = privdata; void *reply = NULL; REDIS_NOTUSED(el); REDIS_NOTUSED(fd); REDIS_NOTUSED(mask); /* Calculate latency only for the first read event. This means that the * server already sent the reply and we need to parse it. Parsing overhead * is not part of the latency, so calculate it only once, here. */ // 计算延时,然后比较延时,取得第一个read的event事件 if (c->latency < 0) c->latency = ustime()-(c->start); if (redisBufferRead(c->context) != REDIS_OK) { // 首先判断是否能读 fprintf(stderr,"Error: %s\n",c->context->errstr); exit(1); } else { while(c->pending) { if (redisGetReply(c->context,&reply) != REDIS_OK) { fprintf(stderr,"Error: %s\n",c->context->errstr); exit(1); } if (reply != NULL) { if (reply == (void*)REDIS_REPLY_ERROR) { // 如果获取reply回复出错,也会直接退出 fprintf(stderr,"Unexpected error reply, exiting...\n"); exit(1); } freeReplyObject(reply); /* This is an OK for prefix commands such as auth and select.*/ if (c->prefix_pending > 0) { c->prefix_pending--; // 执行到这里,请求已经执行成功,等待的请求数-1 c->pending--; /* Discard prefix commands on first response.*/ if (c->prefixlen > 0) { size_t j; sdsrange(c->obuf, c->prefixlen, -1); /* We also need to fix the pointers to the strings * we need to randomize. */ for (j = 0; j < c->randlen; j++) c->randptr[j] -= c->prefixlen; c->prefixlen = 0; } continue; } if (config.requests_finished < config.requests) config.latency[config.requests_finished++] = c->latency; c->pending--; // 全部处理完毕,调用客户端done完成后的方法 if (c->pending == 0) { clientDone(c); break; } } else { break; } } } }
static void set_protocol_err(sub_client *c, int pos) { srv_log(LOG_DEBUG, "Protocol error from sub client"); sdsrange(c->read_buf, pos, -1); }
int processMultibulkBuffer(client *c) { char *newline = NULL; int pos = 0, ok; long long ll; if (c->multibulklen == 0) { /* The client should have been reset */ serverAssertWithInfo(c,NULL,c->argc == 0); /* Multi bulk length cannot be read without a \r\n */ newline = strchr(c->querybuf,'\r'); if (newline == NULL) { if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) { addReplyError(c,"Protocol error: too big mbulk count string"); setProtocolError(c,0); } return C_ERR; } /* Buffer should also contain \n */ if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2)) return C_ERR; /* We know for sure there is a whole line since newline != NULL, * so go ahead and find out the multi bulk length. */ serverAssertWithInfo(c,NULL,c->querybuf[0] == '*'); ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll); if (!ok || ll > 1024*1024) { addReplyError(c,"Protocol error: invalid multibulk length"); setProtocolError(c,pos); return C_ERR; } pos = (newline-c->querybuf)+2; if (ll <= 0) { sdsrange(c->querybuf,pos,-1); return C_OK; } c->multibulklen = ll; /* Setup argv array on client structure */ if (c->argv) free(c->argv); c->argv = malloc(sizeof(sds*)*c->multibulklen); } serverAssertWithInfo(c,NULL,c->multibulklen > 0); while(c->multibulklen) { /* Read bulk length if unknown */ if (c->bulklen == -1) { newline = strchr(c->querybuf+pos,'\r'); if (newline == NULL) { if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) { addReplyError(c, "Protocol error: too big bulk count string"); setProtocolError(c,0); return C_ERR; } break; } /* Buffer should also contain \n */ if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2)) break; if (c->querybuf[pos] != '$') { addReplyErrorFormat(c, "Protocol error: expected '$', got '%c'", c->querybuf[pos]); setProtocolError(c,pos); return C_ERR; } ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll); if (!ok || ll < 0 || ll > 512*1024*1024) { addReplyError(c,"Protocol error: invalid bulk length"); setProtocolError(c,pos); return C_ERR; } pos += newline-(c->querybuf+pos)+2; if (ll >= PROTO_MBULK_BIG_ARG) { size_t qblen; /* If we are going to read a large object from network * try to make it likely that it will start at c->querybuf * boundary so that we can optimize object creation * avoiding a large copy of data. */ sdsrange(c->querybuf,pos,-1); pos = 0; qblen = sdslen(c->querybuf); /* Hint the sds library about the amount of bytes this string is * going to contain. */ if (qblen < (size_t)ll+2) c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen); } c->bulklen = ll; } /* Read bulk argument */ if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) { /* Not enough data (+2 == trailing \r\n) */ break; } else { /* Optimization: if the buffer contains JUST our bulk element * instead of creating a new object by *copying* the sds we * just use the current sds string. */ if (pos == 0 && c->bulklen >= PROTO_MBULK_BIG_ARG && (signed) sdslen(c->querybuf) == c->bulklen+2) { c->argv[c->argc++] = c->querybuf; sdsIncrLen(c->querybuf,-2); /* remove CRLF */ c->querybuf = sdsempty(); /* Assume that if we saw a fat argument we'll see another one * likely... */ c->querybuf = sdsMakeRoomFor(c->querybuf,c->bulklen+2); pos = 0; } else { c->argv[c->argc++] = sdsnewlen(c->querybuf+pos,c->bulklen); pos += c->bulklen+2; } c->bulklen = -1; c->multibulklen--; } } /* Trim to pos */ if (pos) sdsrange(c->querybuf,pos,-1); /* We're done when c->multibulk == 0 */ if (c->multibulklen == 0) return C_OK; /* Still not read to process the command */ return C_ERR; }
static int conf_pre_validate(rmt_conf *cf) { int ret; FILE *fh; char line[256]; dict *organization = NULL; sds str = NULL; sds org_name = NULL; sds *key_value = NULL; int key_value_count = 0; sds key = NULL; conf_value *cv = NULL, **cv_sub; if (cf == NULL) { return RMT_ERROR; } fh = cf->fh; if (fh == NULL) { return RMT_ERROR; } while (!feof(fh)) { if (fgets(line,256,fh) == NULL) { if(feof(fh)) break; log_error("ERROR: Read a line from conf file %s failed: %s", cf->fname, strerror(errno)); goto error; } if(str != NULL){ sdsfree(str); str = NULL; } if(key_value!= NULL){ sdsfreesplitres(key_value, key_value_count); key_value = NULL; key_value_count = 0; } ASSERT(str == NULL); str = sdsnew(line); if(str == NULL){ log_error("ERROR: Out of memory"); goto error; } sdstrim(str, " \n"); if(strchr(str, '#') != NULL){ if(str[0] == '#'){ log_debug(LOG_VVERB, "This line is comment"); sdsfree(str); str = NULL; continue; } sds *content_comment = NULL; int content_comment_count = 0; content_comment = sdssplitlenonce(str, sdslen(str), "#", 1, &content_comment_count); if(content_comment == NULL || content_comment_count != 2){ log_error("ERROR: Split content and comment error."); goto error; } sdsfree(str); str = content_comment[0]; content_comment[0] = NULL; sdsfreesplitres(content_comment, content_comment_count); } log_debug(LOG_VVERB, "%s", str); if(sdslen(str) == 0){ log_debug(LOG_VVERB, "This line is space"); sdsfree(str); str = NULL; continue; }else if(*str == '['){ if(sdslen(str) <= 2 || *(str+sdslen(str)-1) != ']') { log_error("ERROR: Organization name %s in conf file %s error", str, cf->fname); goto error; } if(key != NULL || cv != NULL){ ret = conf_key_value_insert(organization, key, cv); if(ret != RMT_OK){ log_error("ERROR: Key value insert into organization failed"); goto error; } organization = NULL; key = NULL; cv = NULL; } ASSERT(org_name == NULL); sdsrange(str,1,-2); org_name = sdstrim(str, " "); str = NULL; if(sdslen(org_name) == 0){ log_error("ERROR: Organization name %s in conf file %s error", str, cf->fname); goto error; } organization = dictCreate(&KeyValueDictType, NULL); if(organization == NULL){ log_error("ERROR: Create dict organization %s failed", org_name); goto error; } ret = dictAdd(cf->organizations, org_name, organization); if(ret != DICT_OK){ log_error("ERROR: Organization %s in conf file is duplicate", org_name); goto error; } org_name = NULL; }else if(*str == '-'){ if(cv == NULL || cv->type != CONF_VALUE_ARRAY){ log_error("ERROR: Array %s in conf file %s has wrong conf value", str, cf->fname); goto error; } sdsrange(str,1,-1); sdstrim(str, " "); cv_sub = array_push(cv->value); *cv_sub = conf_value_create(CONF_VALUE_STRING); if(*cv_sub == NULL){ log_error("ERROR: Conf value create failed"); goto error; } (*cv_sub)->value = str; str = NULL; }else{ key_value = sdssplitlenonce(str,sdslen(str),":",1,&key_value_count); log_debug(LOG_VVERB, "key_value_count: %d", key_value_count); if(key_value == NULL || key_value_count == 0){ log_error("ERROR: line %s split by : failed", str); goto error; }else if(key_value_count == 1){ log_error("ERROR: Line %s in conf file %s has no :", str, cf->fname); goto error; }else if(key_value_count == 2){ if(key != NULL || cv != NULL){ ret = conf_key_value_insert(organization, key, cv); if(ret != RMT_OK){ log_error("ERROR: Key value insert into organization failed"); goto error; } key = NULL; cv = NULL; } sdstrim(key_value[0]," "); sdstrim(key_value[1]," "); if(sdslen(key_value[1]) == 0){ ASSERT(cv == NULL); cv = conf_value_create(CONF_VALUE_ARRAY); if(cv == NULL){ log_error("ERROR: Conf value create failed"); goto error; } ASSERT(key == NULL); key = key_value[0]; key_value[0] = NULL; continue; } ASSERT(cv == NULL); cv = conf_value_create(CONF_VALUE_STRING); if(cv == NULL){ log_error("ERROR: Conf value create failed"); goto error; } cv->value = key_value[1]; key_value[1] = NULL; if(organization == NULL){ log_error("ERROR: line %s in conf file %s has no organization", str, cf->fname); goto error; } ret = dictAdd(organization,key_value[0],cv); if(ret != DICT_OK){ log_error("ERROR: Key %s in organization of conf file is duplicate", key_value[0]); goto error; } key_value[0] = NULL; cv = NULL; }else{ NOT_REACHED(); } } } if(key != NULL || cv != NULL){ ret = conf_key_value_insert(organization, key, cv); if(ret != RMT_OK){ log_error("ERROR: Key value insert into organization failed"); goto error; } key = NULL; cv = NULL; } if(str != NULL){ sdsfree(str); str = NULL; } if(key_value!= NULL){ sdsfreesplitres(key_value, key_value_count); key_value = NULL; key_value_count = 0; } return RMT_OK; error: if(str != NULL){ sdsfree(str); } if(key != NULL){ sdsfree(key); } if(org_name != NULL){ sdsfree(org_name); } if(key_value != NULL){ sdsfreesplitres(key_value, key_value_count); } if(cv != NULL){ conf_value_destroy(cv); } return RMT_ERROR; }
static int processMultiBulk (client_t * c) { char *newline = NULL; int pos = 0, ok; long long ll; if (c->multibulklen == 0) { newline = strchr (c->querybuf, '\r'); if (newline == NULL) { if (sdslen (c->querybuf) > REDIS_INLINE_MAX_SIZE) { addReplyStr (c, "-ERR Protocol error: too big mbulk count string\r\n"); c->flags |= REDIS_CLOSE_AFTER_REPLY; } return -1; } if (newline - c->querybuf > sdslen (c->querybuf) - 2) return -1; ok = string2ll (c->querybuf + 1, newline - (c->querybuf + 1), &ll); if (!ok || ll > 1024 * 1024) { addReplyStr (c, "-ERR Protocol error: invalid multibulk length\r\n"); c->flags |= REDIS_CLOSE_AFTER_REPLY; return -1; } c->multibulklen = ll; c->argv = malloc (sizeof (sds) * c->multibulklen); c->argvlen = malloc (sizeof (size_t) * c->multibulklen); pos = (newline - c->querybuf) + 2; if (ll <= 0) { sdsrange (c->querybuf, pos, -1); return 0; } } while (c->multibulklen) { if (c->bulklen == -1) { newline = strchr (c->querybuf + pos, '\r'); if (newline == NULL) { if (sdslen (c->querybuf) > REDIS_INLINE_MAX_SIZE) { addReplyStr (c, "-ERR Protocol error: too big bulk count string\r\n"); c->flags |= REDIS_CLOSE_AFTER_REPLY; } break; } if (newline - (c->querybuf) > (sdslen (c->querybuf) - 2)) break; if (c->querybuf[pos] != '$') { addReplyStr (c, "-ERR Protocol error: expected '$', got '"); addReplyStrLen (c, c->querybuf + pos, 1); addReplyStr (c, "'\r\n"); c->flags |= REDIS_CLOSE_AFTER_REPLY; return -1; } ok = string2ll (c->querybuf + pos + 1, newline - (c->querybuf + pos + 1), &ll); if (!ok || ll < 0 || ll > 512 * 1024 * 1024) { addReplyStr (c, "-ERR Protocol error: invalid bulk length\r\n"); c->flags |= REDIS_CLOSE_AFTER_REPLY; return -1; } pos += newline - (c->querybuf + pos) + 2; c->bulklen = ll; } if (sdslen (c->querybuf) - pos < (unsigned) (c->bulklen + 2)) break; c->argvlen[c->argc] = c->bulklen; c->argv[c->argc++] = sdsnewlen (c->querybuf + pos, c->bulklen); pos += c->bulklen + 2; c->bulklen = -1; c->multibulklen--; } if (pos) sdsrange (c->querybuf, pos, -1); if (c->multibulklen) return -1; if (c->argc == 1 && sdslen (c->argv[0]) == 4 && !strcasecmp (c->argv[0], "quit")) { addReplyStrLen (c, "+OK\r\n", 5); c->flags |= REDIS_CLOSE_AFTER_REPLY; return -1; } arc_append_commandcv (c->rqst, c->argc, (const char **) c->argv, c->argvlen); c->total_append_command++; return 0; }
static void * clientThread (void *arg) { client_t *c = (client_t *) arg; fd_set rfds, wfds; int ret; c->querybuf = sdsMakeRoomFor (sdsempty (), DEFAULT_QUERY_BUF_SIZE); c->replybuf = sdsMakeRoomFor (sdsempty (), DEFAULT_QUERY_BUF_SIZE); c->argc = 0; c->argv = NULL; c->argvlen = NULL; c->reqtype = 0; c->multibulklen = 0; c->bulklen = -1; c->rqst = arc_create_request (); c->flags = 0; c->total_append_command = 0; FD_ZERO (&rfds); FD_ZERO (&wfds); while (1) { struct timeval timeout; FD_CLR (c->fd, &rfds); FD_CLR (c->fd, &wfds); if (!(c->flags & REDIS_CLOSE_AFTER_REPLY)) FD_SET (c->fd, &rfds); if (sdslen (c->replybuf) > 0) FD_SET (c->fd, &wfds); timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select (c->fd + 1, &rfds, &wfds, NULL, &timeout); if (ret == -1) { perror ("select"); freeClient (c); } if (server.shutdown_signal) { c->flags |= REDIS_CLOSE_AFTER_REPLY; } /* readable */ if (FD_ISSET (c->fd, &rfds)) { int pos = sdslen (c->querybuf); int avail = sdsavail (c->querybuf); ssize_t nread; if (avail == 0) { c->querybuf = sdsMakeRoomFor (c->querybuf, sdslen (c->querybuf)); avail = sdsavail (c->querybuf); } nread = read (c->fd, c->querybuf + pos, avail); if (nread > 0) { sdsIncrLen (c->querybuf, nread); processInputBuffer (c); if (c->total_append_command) { int arc_errno, arc_be_errno, be_errno; arc_ref_t *arc_ref; arc_ref = acquire_arc_ref (); ret = arc_do_request (arc_ref->arc, c->rqst, server.query_timeout_millis, &be_errno); if (ret == -1) { arc_errno = errno; arc_be_errno = be_errno; } else { ret = processReply (c, &be_errno); if (ret == -1) { arc_errno = errno; arc_be_errno = be_errno; } } arc_free_request (c->rqst); release_arc_ref (arc_ref); c->rqst = arc_create_request (); if (ret == -1) { if (arc_errno == ARC_ERR_TIMEOUT || (arc_errno == ARC_ERR_BACKEND && arc_be_errno == ARC_ERR_TIMEOUT)) { addReplyStr (c, "-ERR Redis Timeout\r\n"); } else { addReplyStr (c, "-ERR Internal Error\r\n"); } c->flags |= REDIS_CLOSE_AFTER_REPLY; } } } else { if (nread == -1 && errno == EAGAIN) { /* Skip */ } else { freeClient (c); } } } /* writable */ if (FD_ISSET (c->fd, &wfds)) { int pos = 0; int avail = sdslen (c->replybuf); ssize_t nwritten; nwritten = write (c->fd, c->replybuf + pos, avail); if (nwritten > 0) { avail -= nwritten; pos += nwritten; sdsrange (c->replybuf, pos, -1); } else { if (nwritten == -1 && errno == EAGAIN) { /* Skip */ } else { freeClient (c); } } } if (sdslen (c->replybuf) == 0 && (c->flags & REDIS_CLOSE_AFTER_REPLY)) { freeClient (c); } } return NULL; }