Beispiel #1
0
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. */
    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 {
        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.requests_finished < config.requests)
                config.latency[config.requests_finished++] = c->latency;
            clientDone(c);
        }
    }
}
Beispiel #2
0
/* This function should be called when the socket is readable.
 * It processes all replies that can be read and executes their callbacks.
 */
void redisAsyncHandleRead(redisAsyncContext *ac) {
    redisContext *c = &(ac->c);

    // 如果连接已经断开,尝试连接
    if (!(c->flags & REDIS_CONNECTED)) {
        /* Abort connect was not successful. */
        if (__redisAsyncHandleConnect(ac) != REDIS_OK)
            return;
        /* Try again later when the context is still not connected. */
        if (!(c->flags & REDIS_CONNECTED))
            return;
    }

    // 读取数据
    if (redisBufferRead(c) == REDIS_ERR) {
        // 读取错误,断开连接并做一些清理工作
        __redisAsyncDisconnect(ac);
    } else {
        // 读取数据成功,再次注册读事件,
        /* Always re-schedule reads */
        _EL_ADD_READ(ac);

        // 并处理回调函数
        redisProcessCallbacks(ac);
    }
}
Beispiel #3
0
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;
}
Beispiel #4
0
/* This function should be called when the socket is readable.
 * It processes all replies that can be read and executes their callbacks.
 */
void redisAsyncHandleRead(redisAsyncContext *ac) {
    redisContext *c = &(ac->c);

    if (!(c->flags & REDIS_CONNECTED)) {
        /* Abort connect was not successful. */
        if (__redisAsyncHandleConnect(ac) != REDIS_OK)
            return;
        /* Try again later when the context is still not connected. */
        if (!(c->flags & REDIS_CONNECTED))
            return;
    }

    if (redisBufferRead(c) == REDIS_ERR) {
        __redisAsyncDisconnect(ac);
    } else {
        /* Always re-schedule reads */
#ifdef _WIN32
        // There appears to be a bug in the Linux version of _EL_ADD_READ which will not reschedule
        // the read if already reading. This is a problem if there is a large number of async GET 
        // operations. If the receive buffer is exhausted with the data returned, the read would
        // not be rescheduled, and the async operations would cease. This forces the read to recur.
        _EL_FORCE_ADD_READ(ac);
#else
        _EL_ADD_READ(ac);
#endif
        redisProcessCallbacks(ac);
    }
}
Beispiel #5
0
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. */
    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);
                }

                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;
            }
        }
    }
}
Beispiel #6
0
/* This function should be called when the socket is readable.
 * It processes all replies that can be read and executes their callbacks.
 */
void redisAsyncHandleRead(redisAsyncContext *ac) {
    redisContext *c = &(ac->c);

    if (redisBufferRead(c) == REDIS_ERR) {
        __redisAsyncDisconnect(ac);
    } else {
        /* Always re-schedule reads */
        if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
        redisProcessCallbacks(ac);
    }
}
Beispiel #7
0
/* This function should be called when the socket is readable.
 * It processes all replies that can be read and executes their callbacks.
 */
void redisAsyncHandleRead(redisAsyncContext *ac) {
    redisContext *c = &(ac->c);

    if (!(c->flags & REDIS_CONNECTED)) {
        /* Abort connect was not successful. */
        if (__redisAsyncHandleConnect(ac) != REDIS_OK)
            return;
        /* Try again later when the context is still not connected. */
        if (!(c->flags & REDIS_CONNECTED))
            return;
    }

    if (redisBufferRead(c) == REDIS_ERR) {
        __redisAsyncDisconnect(ac);
    } else {
        /* Always re-schedule reads */
        _EL_ADD_READ(ac);
        redisProcessCallbacks(ac);
    }
}
Beispiel #8
0
static int __get_reply(redisParentContext *pc, VALUE *reply) {
    redisContext *c = pc->context;
    int wdone = 0;
    void *aux = NULL;

    /* Try to read pending replies */
    if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) {
        /* Protocol error */
        return -1;
    }

    if (aux == NULL) {
        /* Write until the write buffer is drained */
        while (!wdone) {
            errno = 0;

            if (redisBufferWrite(c, &wdone) == REDIS_ERR) {
                /* Socket error */
                return -1;
            }

            if (errno == EAGAIN) {
                int writable = 0;

                if (__wait_writable(c->fd, pc->timeout, &writable) < 0) {
                    rb_sys_fail(0);
                }

                if (!writable) {
                    errno = EAGAIN;
                    rb_sys_fail(0);
                }
            }
        }

        /* Read until there is a full reply */
        while (aux == NULL) {
            errno = 0;

            if (redisBufferRead(c) == REDIS_ERR) {
                /* Socket error */
                return -1;
            }

            if (errno == EAGAIN) {
                int readable = 0;

                if (__wait_readable(c->fd, pc->timeout, &readable) < 0) {
                    rb_sys_fail(0);
                }

                if (!readable) {
                    errno = EAGAIN;
                    rb_sys_fail(0);
                }

                /* Retry */
                continue;
            }

            if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) {
                /* Protocol error */
                return -1;
            }
        }
    }

    /* Set reply object */
    if (reply != NULL) {
        *reply = (VALUE)aux;
    }

    return 0;
}
Beispiel #9
0
/* 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;
}
Beispiel #10
0
 static void test_nonblocking_connection() {
     redisContext *c;
     int wdone = 0;

     test("Calls command callback when command is issued: ");
     c = __connect_nonblock();
     redisSetCommandCallback(c,__test_callback,(void*)1);
     redisCommand(c,"PING");
     test_cond(__test_callback_flags == 1);
     redisFree(c);

     test("Calls disconnect callback on redisDisconnect: ");
     c = __connect_nonblock();
     redisSetDisconnectCallback(c,__test_callback,(void*)2);
     redisDisconnect(c);
     test_cond(__test_callback_flags == 2);
     redisFree(c);

     test("Calls disconnect callback and free callback on redisFree: ");
     c = __connect_nonblock();
     redisSetDisconnectCallback(c,__test_callback,(void*)2);
     redisSetFreeCallback(c,__test_callback,(void*)4);
     redisFree(c);
     test_cond(__test_callback_flags == ((2 << 8) | 4));

     test("redisBufferWrite against empty write buffer: ");
     c = __connect_nonblock();
     test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
     redisFree(c);

     test("redisBufferWrite against not yet connected fd: ");
     c = __connect_nonblock();
     redisCommand(c,"PING");
     test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
               strncmp(c->error,"write:",6) == 0);
     redisFree(c);

     test("redisBufferWrite against closed fd: ");
     c = __connect_nonblock();
     redisCommand(c,"PING");
     redisDisconnect(c);
     test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
               strncmp(c->error,"write:",6) == 0);
     redisFree(c);

     test("Process callbacks in the right sequence: ");
     c = __connect_nonblock();
     redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
     redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
     redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");

     /* Write output buffer */
     wdone = 0;
     while(!wdone) {
         usleep(500);
         redisBufferWrite(c,&wdone);
     }

     /* Read until at least one callback is executed (the 3 replies will
      * arrive in a single packet, causing all callbacks to be executed in
      * a single pass). */
     while(__test_callback_flags == 0) {
         assert(redisBufferRead(c) == REDIS_OK);
         redisProcessCallbacks(c);
     }
     test_cond(__test_callback_flags == 0x010203);
     redisFree(c);

     test("redisDisconnect executes pending callbacks with NULL reply: ");
     c = __connect_nonblock();
     redisSetDisconnectCallback(c,__test_callback,(void*)1);
     redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
     redisDisconnect(c);
     test_cond(__test_callback_flags == 0x0201);
     redisFree(c);
 }
Beispiel #11
0
/* 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;
}