return c;
}
-/* Set the event loop to listen for write events on the client's socket.
- * Typically gets called every time a reply is built. */
-int _installWriteEvent(redisClient *c) {
+/* This function is called every time we are going to transmit new data
+ * to the client. The behavior is the following:
+ *
+ * If the client should receive new data (normal clients will) the function
+ * returns REDIS_OK, and make sure to install the write handler in our event
+ * loop so that when the socket is writable new data gets written.
+ *
+ * If the client should not receive new data, because it is a fake client
+ * or a slave, or because the setup of the write handler failed, the function
+ * returns REDIS_ERR.
+ *
+ * Typically gets called every time a reply is built, before adding more
+ * data to the clients output buffers. If the function returns REDIS_ERR no
+ * data should be appended to the output buffers. */
+int prepareClientToWrite(redisClient *c) {
if (c->flags & REDIS_LUA_CLIENT) return REDIS_OK;
- if (c->fd <= 0) return REDIS_ERR;
+ if (c->fd <= 0) return REDIS_ERR; /* Fake client */
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||
c->replstate == REDIS_REPL_ONLINE) &&
* -------------------------------------------------------------------------- */
void addReply(redisClient *c, robj *obj) {
- if (_installWriteEvent(c) != REDIS_OK) return;
+ if (prepareClientToWrite(c) != REDIS_OK) return;
/* This is an important place where we can avoid copy-on-write
* when there is a saving child running, avoiding touching the
if (obj->encoding == REDIS_ENCODING_RAW) {
if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)
_addReplyObjectToList(c,obj);
- } else {
- /* FIXME: convert the long into string and use _addReplyToBuffer()
- * instead of calling getDecodedObject. As this place in the
- * code is too performance critical. */
+ } else if (obj->encoding == REDIS_ENCODING_INT) {
+ /* Optimization: if there is room in the static buffer for 32 bytes
+ * (more than the max chars a 64 bit integer can take as string) we
+ * avoid decoding the object and go for the lower level approach. */
+ if (listLength(c->reply) == 0 && (sizeof(c->buf) - c->bufpos) >= 32) {
+ char buf[32];
+ int len;
+
+ len = ll2string(buf,sizeof(buf),(long)obj->ptr);
+ if (_addReplyToBuffer(c,buf,len) == REDIS_OK)
+ return;
+ /* else... continue with the normal code path, but should never
+ * happen actually since we verified there is room. */
+ }
obj = getDecodedObject(obj);
if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)
_addReplyObjectToList(c,obj);
decrRefCount(obj);
+ } else {
+ redisPanic("Wrong obj->encoding in addReply()");
}
}
void addReplySds(redisClient *c, sds s) {
- if (_installWriteEvent(c) != REDIS_OK) {
+ if (prepareClientToWrite(c) != REDIS_OK) {
/* The caller expects the sds to be free'd. */
sdsfree(s);
return;
}
void addReplyString(redisClient *c, char *s, size_t len) {
- if (_installWriteEvent(c) != REDIS_OK) return;
+ if (prepareClientToWrite(c) != REDIS_OK) return;
if (_addReplyToBuffer(c,s,len) != REDIS_OK)
_addReplyStringToList(c,s,len);
}
-void _addReplyError(redisClient *c, char *s, size_t len) {
+void addReplyErrorLength(redisClient *c, char *s, size_t len) {
addReplyString(c,"-ERR ",5);
addReplyString(c,s,len);
addReplyString(c,"\r\n",2);
}
void addReplyError(redisClient *c, char *err) {
- _addReplyError(c,err,strlen(err));
+ addReplyErrorLength(c,err,strlen(err));
}
void addReplyErrorFormat(redisClient *c, const char *fmt, ...) {
for (j = 0; j < l; j++) {
if (s[j] == '\r' || s[j] == '\n') s[j] = ' ';
}
- _addReplyError(c,s,sdslen(s));
+ addReplyErrorLength(c,s,sdslen(s));
sdsfree(s);
}
-void _addReplyStatus(redisClient *c, char *s, size_t len) {
+void addReplyStatusLength(redisClient *c, char *s, size_t len) {
addReplyString(c,"+",1);
addReplyString(c,s,len);
addReplyString(c,"\r\n",2);
}
void addReplyStatus(redisClient *c, char *status) {
- _addReplyStatus(c,status,strlen(status));
+ addReplyStatusLength(c,status,strlen(status));
}
void addReplyStatusFormat(redisClient *c, const char *fmt, ...) {
va_start(ap,fmt);
sds s = sdscatvprintf(sdsempty(),fmt,ap);
va_end(ap);
- _addReplyStatus(c,s,sdslen(s));
+ addReplyStatusLength(c,s,sdslen(s));
sdsfree(s);
}
/* Note that we install the write event here even if the object is not
* ready to be sent, since we are sure that before returning to the
* event loop setDeferredMultiBulkLength() will be called. */
- if (_installWriteEvent(c) != REDIS_OK) return NULL;
+ if (prepareClientToWrite(c) != REDIS_OK) return NULL;
listAddNodeTail(c->reply,createObject(REDIS_STRING,NULL));
return listLast(c->reply);
}
/* Add a long long as integer reply or bulk len / multi bulk count.
* Basically this is used to output <prefix><long long><crlf>. */
-void _addReplyLongLong(redisClient *c, long long ll, char prefix) {
+void addReplyLongLongWithPrefix(redisClient *c, long long ll, char prefix) {
char buf[128];
int len;
buf[0] = prefix;
else if (ll == 1)
addReply(c,shared.cone);
else
- _addReplyLongLong(c,ll,':');
+ addReplyLongLongWithPrefix(c,ll,':');
}
void addReplyMultiBulkLen(redisClient *c, long length) {
- _addReplyLongLong(c,length,'*');
+ addReplyLongLongWithPrefix(c,length,'*');
}
/* Create the length prefix of a bulk reply, example: $2234 */
len++;
}
}
- _addReplyLongLong(c,len,'$');
+ addReplyLongLongWithPrefix(c,len,'$');
}
/* Add a Redis Object as a bulk reply */
/* Add a C buffer as bulk reply */
void addReplyBulkCBuffer(redisClient *c, void *p, size_t len) {
- _addReplyLongLong(c,len,'$');
+ addReplyLongLongWithPrefix(c,len,'$');
addReplyString(c,p,len);
addReply(c,shared.crlf);
}
}
/* Asynchronously close a client if soft or hard limit is reached on the
- * output buffer size. If the client will be closed 1 is returend, otherwise 0
- * is returned.
+ * output buffer size. The caller can check if the client will be closed
+ * checking if the client REDIS_CLOSE_ASAP flag is set.
*
* Note: we need to close the client asynchronously because this function is
* called from contexts where the client can't be freed safely, i.e. from the
* lower level functions pushing data inside the client output buffers. */
-int asyncCloseClientOnOutputBufferLimitReached(redisClient *c) {
+void asyncCloseClientOnOutputBufferLimitReached(redisClient *c) {
+ if (c->flags & REDIS_CLOSE_ASAP) return;
if (checkClientOutputBufferLimits(c)) {
sds client = getClientInfoString(c);
freeClientAsync(c);
- redisLog(REDIS_NOTICE,"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.");
+ redisLog(REDIS_NOTICE,"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.", client);
sdsfree(client);
- return 1;
- } else {
- return 0;
}
}