Prevent mixed up client replies, and deadlocks

This commit is contained in:
John Sully 2019-02-22 01:24:16 -05:00
parent 2788cf57b0
commit 30e8a859c0
17 changed files with 232 additions and 150 deletions

View File

@ -655,6 +655,8 @@ struct client *createFakeClient(void) {
c->puser = NULL; c->puser = NULL;
listSetFreeMethod(c->reply,freeClientReplyValue); listSetFreeMethod(c->reply,freeClientReplyValue);
listSetDupMethod(c->reply,dupClientReplyValue); listSetDupMethod(c->reply,dupClientReplyValue);
fastlock_init(&c->lock);
fastlock_lock(&c->lock);
initClientMultiState(c); initClientMultiState(c);
return c; return c;
} }
@ -672,6 +674,8 @@ void freeFakeClient(struct client *c) {
listRelease(c->reply); listRelease(c->reply);
listRelease(c->watched_keys); listRelease(c->watched_keys);
freeClientMultiState(c); freeClientMultiState(c);
fastlock_unlock(&c->lock);
fastlock_free(&c->lock);
zfree(c); zfree(c);
} }

View File

@ -224,6 +224,7 @@ void disconnectAllBlockedClients(void) {
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *c = listNodeValue(ln); client *c = listNodeValue(ln);
fastlock_lock(&c->lock);
if (c->flags & CLIENT_BLOCKED) { if (c->flags & CLIENT_BLOCKED) {
addReplySdsAsync(c,sdsnew( addReplySdsAsync(c,sdsnew(
"-UNBLOCKED force unblock from blocking operation, " "-UNBLOCKED force unblock from blocking operation, "
@ -231,6 +232,7 @@ void disconnectAllBlockedClients(void) {
unblockClient(c); unblockClient(c);
c->flags |= CLIENT_CLOSE_AFTER_REPLY; c->flags |= CLIENT_CLOSE_AFTER_REPLY;
} }
fastlock_unlock(&c->lock);
} }
} }
@ -309,6 +311,7 @@ void handleClientsBlockedOnKeys(void) {
* freed by the next unblockClient() * freed by the next unblockClient()
* call. */ * call. */
if (dstkey) incrRefCount(dstkey); if (dstkey) incrRefCount(dstkey);
fastlock_lock(&receiver->lock);
unblockClient(receiver); unblockClient(receiver);
if (serveClientBlockedOnList(receiver, if (serveClientBlockedOnList(receiver,
@ -321,6 +324,7 @@ void handleClientsBlockedOnKeys(void) {
} }
if (dstkey) decrRefCount(dstkey); if (dstkey) decrRefCount(dstkey);
fastlock_unlock(&receiver->lock);
decrRefCount(value); decrRefCount(value);
} else { } else {
break; break;
@ -360,6 +364,7 @@ void handleClientsBlockedOnKeys(void) {
continue; continue;
} }
fastlock_lock(&receiver->lock);
int where = (receiver->lastcmd && int where = (receiver->lastcmd &&
receiver->lastcmd->proc == bzpopminCommand) receiver->lastcmd->proc == bzpopminCommand)
? ZSET_MIN : ZSET_MAX; ? ZSET_MIN : ZSET_MAX;
@ -377,6 +382,7 @@ void handleClientsBlockedOnKeys(void) {
incrRefCount(rl->key); incrRefCount(rl->key);
propagate(cmd,receiver->db->id, propagate(cmd,receiver->db->id,
argv,2,PROPAGATE_AOF|PROPAGATE_REPL); argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
fastlock_unlock(&receiver->lock);
decrRefCount(argv[0]); decrRefCount(argv[0]);
decrRefCount(argv[1]); decrRefCount(argv[1]);
} }
@ -419,10 +425,12 @@ void handleClientsBlockedOnKeys(void) {
/* If the group was not found, send an error /* If the group was not found, send an error
* to the consumer. */ * to the consumer. */
if (!group) { if (!group) {
fastlock_lock(&receiver->lock);
addReplyErrorAsync(receiver, addReplyErrorAsync(receiver,
"-NOGROUP the consumer group this client " "-NOGROUP the consumer group this client "
"was blocked on no longer exists"); "was blocked on no longer exists");
unblockClient(receiver); unblockClient(receiver);
fastlock_unlock(&receiver->lock);
continue; continue;
} else { } else {
*gt = group->last_id; *gt = group->last_id;
@ -444,6 +452,8 @@ void handleClientsBlockedOnKeys(void) {
noack = receiver->bpop.xread_group_noack; noack = receiver->bpop.xread_group_noack;
} }
fastlock_lock(&receiver->lock);
/* Emit the two elements sub-array consisting of /* Emit the two elements sub-array consisting of
* the name of the stream and the data we * the name of the stream and the data we
* extracted from it. Wrapped in a single-item * extracted from it. Wrapped in a single-item
@ -469,6 +479,7 @@ void handleClientsBlockedOnKeys(void) {
* valid, so we must do the setup above before * valid, so we must do the setup above before
* this call. */ * this call. */
unblockClient(receiver); unblockClient(receiver);
fastlock_unlock(&receiver->lock);
} }
} }
} }

View File

@ -1,6 +1,10 @@
#ifndef __CLUSTER_H #ifndef __CLUSTER_H
#define __CLUSTER_H #define __CLUSTER_H
#ifdef __cplusplus
extern "C" {
#endif
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* Redis cluster data structures, defines, exported API. * Redis cluster data structures, defines, exported API.
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
@ -287,4 +291,8 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
int clusterRedirectBlockedClientIfNeeded(client *c); int clusterRedirectBlockedClientIfNeeded(client *c);
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code); void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
#ifdef __cplusplus
}
#endif
#endif /* __CLUSTER_H */ #endif /* __CLUSTER_H */

View File

@ -1110,6 +1110,7 @@ long long getExpire(redisDb *db, robj *key) {
* will be consistent even if we allow write operations against expiring * will be consistent even if we allow write operations against expiring
* keys. */ * keys. */
void propagateExpire(redisDb *db, robj *key, int lazy) { void propagateExpire(redisDb *db, robj *key, int lazy) {
serverAssert(aeThreadOwnsLock());
robj *argv[2]; robj *argv[2];
argv[0] = lazy ? shared.unlink : shared.del; argv[0] = lazy ? shared.unlink : shared.del;

View File

@ -350,6 +350,7 @@ unsigned long LFUDecrAndReturn(robj *o) {
* used memory: the eviction should use mostly data size. This function * used memory: the eviction should use mostly data size. This function
* returns the sum of AOF and slaves buffer. */ * returns the sum of AOF and slaves buffer. */
size_t freeMemoryGetNotCountedMemory(void) { size_t freeMemoryGetNotCountedMemory(void) {
serverAssert(aeThreadOwnsLock());
size_t overhead = 0; size_t overhead = 0;
int slaves = listLength(server.slaves); int slaves = listLength(server.slaves);
@ -444,6 +445,7 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
* Otehrwise if we are over the memory limit, but not enough memory * Otehrwise if we are over the memory limit, but not enough memory
* was freed to return back under the limit, the function returns C_ERR. */ * was freed to return back under the limit, the function returns C_ERR. */
int freeMemoryIfNeeded(void) { int freeMemoryIfNeeded(void) {
serverAssert(aeThreadOwnsLock());
/* By default replicas should ignore maxmemory /* By default replicas should ignore maxmemory
* and just be masters exact copies. */ * and just be masters exact copies. */
if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK; if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK;

View File

@ -3696,7 +3696,7 @@ void moduleHandleBlockedClients(void) {
/* Put the client in the list of clients that need to write /* Put the client in the list of clients that need to write
* if there are pending replies here. This is needed since * if there are pending replies here. This is needed since
* during a non blocking command the client may receive output. */ * during a non blocking command the client may receive output. */
if (clientHasPendingReplies(c, FALSE) && if (clientHasPendingReplies(c) &&
!(c->flags & CLIENT_PENDING_WRITE)) !(c->flags & CLIENT_PENDING_WRITE))
{ {
c->flags |= CLIENT_PENDING_WRITE; c->flags |= CLIENT_PENDING_WRITE;

View File

@ -45,15 +45,25 @@ class AeLocker
bool m_fArmed = false; bool m_fArmed = false;
public: public:
AeLocker(bool fArm = false) AeLocker()
{ {
if (fArm)
arm();
} }
void arm() void arm(client *c) // if a client is passed, then the client is already locked
{ {
if (!m_fArmed) if (c != nullptr)
{
serverAssert(!m_fArmed);
serverAssert(c->lock.fOwnLock());
while (!aeTryAcquireLock())
{
c->lock.unlock();
// give a chance for the global lock to progress if they were waiting on the client
c->lock.lock();
}
m_fArmed = true;
}
else if (!m_fArmed)
{ {
m_fArmed = true; m_fArmed = true;
aeAcquireLock(); aeAcquireLock();
@ -204,9 +214,6 @@ client *createClient(int fd, int iel) {
c->bufAsync = NULL; c->bufAsync = NULL;
c->buflenAsync = 0; c->buflenAsync = 0;
c->bufposAsync = 0; c->bufposAsync = 0;
c->listbufferDoneAsync = listCreate();
listSetFreeMethod(c->listbufferDoneAsync,freeClientReplyValue);
listSetDupMethod(c->listbufferDoneAsync,dupClientReplyValue);
listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid); listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);
listSetMatchMethod(c->pubsub_patterns,listMatchObjects); listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
@ -276,6 +283,7 @@ void clientInstallAsyncWriteHandler(client *c) {
int prepareClientToWrite(client *c, bool fAsync) { int prepareClientToWrite(client *c, bool fAsync) {
fAsync = fAsync && !FCorrectThread(c); // Not async if we're on the right thread fAsync = fAsync && !FCorrectThread(c); // Not async if we're on the right thread
serverAssert(!fAsync || aeThreadOwnsLock()); serverAssert(!fAsync || aeThreadOwnsLock());
serverAssert(c->lock.fOwnLock());
/* If it's the Lua client we always return ok without installing any /* If it's the Lua client we always return ok without installing any
* handler since there is no socket at all. */ * handler since there is no socket at all. */
@ -293,7 +301,7 @@ int prepareClientToWrite(client *c, bool fAsync) {
/* Schedule the client to write the output buffers to the socket, unless /* Schedule the client to write the output buffers to the socket, unless
* it should already be setup to do so (it has already pending data). */ * it should already be setup to do so (it has already pending data). */
if (!fAsync && !clientHasPendingReplies(c, FALSE)) clientInstallWriteHandler(c); if (!fAsync && !clientHasPendingReplies(c)) clientInstallWriteHandler(c);
if (fAsync && !(c->fPendingAsyncWrite)) clientInstallAsyncWriteHandler(c); if (fAsync && !(c->fPendingAsyncWrite)) clientInstallAsyncWriteHandler(c);
/* Authorize the caller to queue in the output buffer of this client. */ /* Authorize the caller to queue in the output buffer of this client. */
@ -1014,8 +1022,8 @@ void copyClientOutputBuffer(client *dst, client *src) {
/* Return true if the specified client has pending reply buffers to write to /* Return true if the specified client has pending reply buffers to write to
* the socket. */ * the socket. */
int clientHasPendingReplies(client *c, int fIncludeAsync) { int clientHasPendingReplies(client *c) {
return c->bufpos || listLength(c->reply) || (fIncludeAsync && listLength(c->listbufferDoneAsync)); return c->bufpos || listLength(c->reply);
} }
#define MAX_ACCEPTS_PER_CALL 1000 #define MAX_ACCEPTS_PER_CALL 1000
@ -1149,6 +1157,7 @@ static void freeClientArgv(client *c) {
* when we resync with our own master and want to force all our slaves to * when we resync with our own master and want to force all our slaves to
* resync with us as well. */ * resync with us as well. */
void disconnectSlaves(void) { void disconnectSlaves(void) {
serverAssert(aeThreadOwnsLock());
std::vector<client*> vecfreeImmediate; std::vector<client*> vecfreeImmediate;
listNode *ln; listNode *ln;
listIter li; listIter li;
@ -1327,7 +1336,6 @@ void freeClient(client *c) {
/* Release other dynamically allocated client structure fields, /* Release other dynamically allocated client structure fields,
* and finally release the client structure itself. */ * and finally release the client structure itself. */
zfree(c->bufAsync); zfree(c->bufAsync);
listRelease(c->listbufferDoneAsync);
if (c->name) decrRefCount(c->name); if (c->name) decrRefCount(c->name);
zfree(c->argv); zfree(c->argv);
freeClientMultiState(c); freeClientMultiState(c);
@ -1342,10 +1350,10 @@ void freeClient(client *c) {
* should be valid for the continuation of the flow of the program. */ * should be valid for the continuation of the flow of the program. */
void freeClientAsync(client *c) { void freeClientAsync(client *c) {
if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return; if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;
aeAcquireLock(); AeLocker lock;
lock.arm(nullptr);
c->flags |= CLIENT_CLOSE_ASAP; c->flags |= CLIENT_CLOSE_ASAP;
listAddNodeTail(server.clients_to_close,c); listAddNodeTail(server.clients_to_close,c);
aeReleaseLock();
} }
void freeClientsInAsyncFreeQueue(int iel) { void freeClientsInAsyncFreeQueue(int iel) {
@ -1381,48 +1389,10 @@ int writeToClient(int fd, client *c, int handler_installed) {
clientReplyBlock *o; clientReplyBlock *o;
AssertCorrectThread(c); AssertCorrectThread(c);
// Decide up front if we are sending the done buffer. This prevents us from completing std::unique_lock<decltype(c->lock)> lock(c->lock);
// a transmission on another thread while transmitting the thread local buffer, resulting in us
// overlapping messages
AeLocker locker(true);
std::lock_guard<decltype(c->lock)> lock(c->lock); // To prevent deadlocks this must be after we acquire the global lock
int fSendAsyncBuffer = listLength(c->listbufferDoneAsync) && (c->sentlen == 0 || c->sentlenAsync > 0);
if (!fSendAsyncBuffer)
locker.disarm();
while(fSendAsyncBuffer || clientHasPendingReplies(c, FALSE)) { while(clientHasPendingReplies(c)) {
if (fSendAsyncBuffer) { if (c->bufpos > 0) {
o = (clientReplyBlock*)listNodeValue(listFirst(c->listbufferDoneAsync));
if (o->used == 0) {
listDelNode(c->listbufferDoneAsync,listFirst(c->listbufferDoneAsync));
if (listLength(c->listbufferDoneAsync) == 0)
{
fSendAsyncBuffer = 0;
locker.disarm();
}
continue;
}
nwritten = write(fd, o->buf() + c->sentlen, o->used - c->sentlen);
if (nwritten <= 0)
break;
c->sentlenAsync += nwritten;
totwritten += nwritten;
/* If we fully sent the object on head go to the next one */
if (c->sentlenAsync == o->used) {
listDelNode(c->listbufferDoneAsync,listFirst(c->listbufferDoneAsync));
c->sentlenAsync = 0;
/* If there are no longer objects in the list, we expect
* the count of reply bytes to be exactly zero. */
if (listLength(c->listbufferDoneAsync) == 0)
{
fSendAsyncBuffer = 0;
locker.disarm();
continue;
}
}
} else if (c->bufpos > 0) {
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen); nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
if (nwritten <= 0) break; if (nwritten <= 0) break;
@ -1486,9 +1456,17 @@ int writeToClient(int fd, client *c, int handler_installed) {
} else { } else {
serverLog(LL_VERBOSE, serverLog(LL_VERBOSE,
"Error writing to client: %s", strerror(errno)); "Error writing to client: %s", strerror(errno));
aeAcquireLock(); if (aeTryAcquireLock())
freeClient(c); {
aeReleaseLock(); freeClient(c);
aeReleaseLock();
}
else
{
lock.unlock();
freeClientAsync(c);
}
return C_ERR; return C_ERR;
} }
} }
@ -1499,15 +1477,22 @@ int writeToClient(int fd, client *c, int handler_installed) {
* We just rely on data / pings received for timeout detection. */ * We just rely on data / pings received for timeout detection. */
if (!(c->flags & CLIENT_MASTER)) c->lastinteraction = server.unixtime; if (!(c->flags & CLIENT_MASTER)) c->lastinteraction = server.unixtime;
} }
if (!clientHasPendingReplies(c, TRUE)) { if (!clientHasPendingReplies(c)) {
c->sentlen = 0; c->sentlen = 0;
if (handler_installed) aeDeleteFileEvent(server.rgthreadvar[c->iel].el,c->fd,AE_WRITABLE); if (handler_installed) aeDeleteFileEvent(server.rgthreadvar[c->iel].el,c->fd,AE_WRITABLE);
/* Close connection after entire reply has been sent. */ /* Close connection after entire reply has been sent. */
if (c->flags & CLIENT_CLOSE_AFTER_REPLY) { if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {
aeAcquireLock(); if (aeTryAcquireLock())
freeClient(c); {
aeReleaseLock(); freeClient(c);
aeReleaseLock();
}
else
{
lock.unlock();
freeClientAsync(c);
}
return C_ERR; return C_ERR;
} }
} }
@ -1542,7 +1527,9 @@ void ProcessPendingAsyncWrites()
reply->size = zmalloc_usable(reply) - sizeof(clientReplyBlock); reply->size = zmalloc_usable(reply) - sizeof(clientReplyBlock);
reply->used = c->bufposAsync; reply->used = c->bufposAsync;
memcpy(reply->buf(), c->bufAsync, c->bufposAsync); memcpy(reply->buf(), c->bufAsync, c->bufposAsync);
listAddNodeTail(c->listbufferDoneAsync, reply); listAddNodeTail(c->reply, reply);
c->reply_bytes += reply->size;
c->bufposAsync = 0; c->bufposAsync = 0;
c->buflenAsync = 0; c->buflenAsync = 0;
zfree(c->bufAsync); zfree(c->bufAsync);
@ -1566,6 +1553,7 @@ void ProcessPendingAsyncWrites()
(c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack)))) (c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack))))
continue; continue;
asyncCloseClientOnOutputBufferLimitReached(c);
if (aeCreateRemoteFileEvent(server.rgthreadvar[c->iel].el, c->fd, ae_flags, sendReplyToClient, c, FALSE) == AE_ERR) if (aeCreateRemoteFileEvent(server.rgthreadvar[c->iel].el, c->fd, ae_flags, sendReplyToClient, c, FALSE) == AE_ERR)
continue; // We can retry later in the cron continue; // We can retry later in the cron
} }
@ -1601,7 +1589,7 @@ int handleClientsWithPendingWrites(int iel) {
/* If after the synchronous writes above we still have data to /* If after the synchronous writes above we still have data to
* output to the client, we need to install the writable handler. */ * output to the client, we need to install the writable handler. */
if (clientHasPendingReplies(c, TRUE)) { if (clientHasPendingReplies(c)) {
int ae_flags = AE_WRITABLE|AE_WRITE_THREADSAFE; int ae_flags = AE_WRITABLE|AE_WRITE_THREADSAFE;
/* For the fsync=always policy, we want that a given FD is never /* For the fsync=always policy, we want that a given FD is never
* served for reading and writing in the same event loop iteration, * served for reading and writing in the same event loop iteration,
@ -1619,7 +1607,8 @@ int handleClientsWithPendingWrites(int iel) {
} }
} }
AeLocker locker(true); AeLocker locker;
locker.arm(nullptr);
ProcessPendingAsyncWrites(); ProcessPendingAsyncWrites();
return processed; return processed;
@ -1675,7 +1664,7 @@ void unprotectClient(client *c) {
if (c->flags & CLIENT_PROTECTED) { if (c->flags & CLIENT_PROTECTED) {
c->flags &= ~CLIENT_PROTECTED; c->flags &= ~CLIENT_PROTECTED;
aeCreateFileEvent(server.rgthreadvar[c->iel].el,c->fd,AE_READABLE|AE_READ_THREADSAFE,readQueryFromClient,c); aeCreateFileEvent(server.rgthreadvar[c->iel].el,c->fd,AE_READABLE|AE_READ_THREADSAFE,readQueryFromClient,c);
if (clientHasPendingReplies(c, TRUE)) clientInstallWriteHandler(c); if (clientHasPendingReplies(c)) clientInstallWriteHandler(c);
} }
} }
@ -1967,7 +1956,8 @@ void processInputBuffer(client *c) {
} else { } else {
serverPanic("Unknown request type"); serverPanic("Unknown request type");
} }
AeLocker locker(true); AeLocker locker;
locker.arm(c);
server.current_client = c; server.current_client = c;
/* Multibulk processing could see a <= 0 length. */ /* Multibulk processing could see a <= 0 length. */
@ -2033,9 +2023,12 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
UNUSED(mask); UNUSED(mask);
serverAssert(mask & AE_READ_THREADSAFE); serverAssert(mask & AE_READ_THREADSAFE);
serverAssert(c->iel == ielFromEventLoop(el)); serverAssert(c->iel == ielFromEventLoop(el));
AeLocker locker;
AeLocker aelock;
AssertCorrectThread(c); AssertCorrectThread(c);
std::lock_guard<decltype(c->lock)> lock(c->lock); std::unique_lock<decltype(c->lock)> lock(c->lock, std::defer_lock);
if (!lock.try_lock())
return; // Process something else while we wait
readlen = PROTO_IOBUF_LEN; readlen = PROTO_IOBUF_LEN;
/* If this is a multi bulk request, and we are processing a bulk reply /* If this is a multi bulk request, and we are processing a bulk reply
@ -2065,16 +2058,14 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
return; return;
} else { } else {
serverLog(LL_VERBOSE, "Reading from client: %s",strerror(errno)); serverLog(LL_VERBOSE, "Reading from client: %s",strerror(errno));
aeAcquireLock(); aelock.arm(c);
freeClient(c); freeClient(c);
aeReleaseLock();
return; return;
} }
} else if (nread == 0) { } else if (nread == 0) {
serverLog(LL_VERBOSE, "Client closed connection"); serverLog(LL_VERBOSE, "Client closed connection");
aeAcquireLock(); aelock.arm(c);
freeClient(c); freeClient(c);
aeReleaseLock();
return; return;
} else if (c->flags & CLIENT_MASTER) { } else if (c->flags & CLIENT_MASTER) {
/* Append the query buffer to the pending (not applied) buffer /* Append the query buffer to the pending (not applied) buffer
@ -2095,9 +2086,8 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
serverLog(LL_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes); serverLog(LL_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);
sdsfree(ci); sdsfree(ci);
sdsfree(bytes); sdsfree(bytes);
aeAcquireLock(); aelock.arm(c);
freeClient(c); freeClient(c);
aeReleaseLock();
return; return;
} }
@ -2108,9 +2098,8 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
* corresponding part of the replication stream, will be propagated to * corresponding part of the replication stream, will be propagated to
* the sub-slaves and to the replication backlog. */ * the sub-slaves and to the replication backlog. */
processInputBufferAndReplicate(c); processInputBufferAndReplicate(c);
aeAcquireLock(); aelock.arm(c);
ProcessPendingAsyncWrites(); ProcessPendingAsyncWrites();
aeReleaseLock();
} }
void getClientsMaxBuffers(unsigned long *longest_output_list, void getClientsMaxBuffers(unsigned long *longest_output_list,
@ -2619,7 +2608,7 @@ void rewriteClientCommandArgument(client *c, int i, robj *newval) {
* enforcing the client output length limits. */ * enforcing the client output length limits. */
unsigned long getClientOutputBufferMemoryUsage(client *c) { unsigned long getClientOutputBufferMemoryUsage(client *c) {
unsigned long list_item_size = sizeof(listNode) + sizeof(clientReplyBlock); unsigned long list_item_size = sizeof(listNode) + sizeof(clientReplyBlock);
return c->reply_bytes + (list_item_size*listLength(c->reply)); return c->reply_bytes + (list_item_size*listLength(c->reply)) + c->buflenAsync;
} }
/* Get the class of a client, used in order to enforce limits to different /* Get the class of a client, used in order to enforce limits to different
@ -2727,6 +2716,7 @@ void asyncCloseClientOnOutputBufferLimitReached(client *c) {
* This is also called by SHUTDOWN for a best-effort attempt to send * This is also called by SHUTDOWN for a best-effort attempt to send
* slaves the latest writes. */ * slaves the latest writes. */
void flushSlavesOutputBuffers(void) { void flushSlavesOutputBuffers(void) {
serverAssert(aeThreadOwnsLock());
listIter li; listIter li;
listNode *ln; listNode *ln;
@ -2747,7 +2737,7 @@ void flushSlavesOutputBuffers(void) {
events = aeGetFileEvents(server.rgthreadvar[slave->iel].el,slave->fd); events = aeGetFileEvents(server.rgthreadvar[slave->iel].el,slave->fd);
if (events & AE_WRITABLE && if (events & AE_WRITABLE &&
slave->replstate == SLAVE_STATE_ONLINE && slave->replstate == SLAVE_STATE_ONLINE &&
clientHasPendingReplies(slave, TRUE)) clientHasPendingReplies(slave))
{ {
writeToClient(slave->fd,slave,0); writeToClient(slave->fd,slave,0);
} }
@ -2820,8 +2810,7 @@ int processEventsWhileBlocked(int iel) {
int iterations = 4; /* See the function top-comment. */ int iterations = 4; /* See the function top-comment. */
int count = 0; int count = 0;
// BUGBUG - This function isn't fair - why should clients on this thread get to run, but not clients elsewhere? aeReleaseLock();
// We mix up replies when releasing the lock here so more work is needed to fix this
while (iterations--) { while (iterations--) {
int events = 0; int events = 0;
events += aeProcessEvents(server.rgthreadvar[iel].el, AE_FILE_EVENTS|AE_DONT_WAIT); events += aeProcessEvents(server.rgthreadvar[iel].el, AE_FILE_EVENTS|AE_DONT_WAIT);
@ -2829,5 +2818,6 @@ int processEventsWhileBlocked(int iel) {
if (!events) break; if (!events) break;
count += events; count += events;
} }
aeAcquireLock();
return count; return count;
} }

View File

@ -940,6 +940,7 @@ void freeMemoryOverheadData(struct redisMemOverhead *mh) {
* information used for the MEMORY OVERHEAD and INFO command. The returned * information used for the MEMORY OVERHEAD and INFO command. The returned
* structure pointer should be freed calling freeMemoryOverheadData(). */ * structure pointer should be freed calling freeMemoryOverheadData(). */
struct redisMemOverhead *getMemoryOverheadData(void) { struct redisMemOverhead *getMemoryOverheadData(void) {
serverAssert(aeThreadOwnsLock());
int j; int j;
size_t mem_total = 0; size_t mem_total = 0;
size_t mem = 0; size_t mem = 0;
@ -1077,6 +1078,7 @@ void inputCatSds(void *result, const char *str) {
/* This implements MEMORY DOCTOR. An human readable analysis of the Redis /* This implements MEMORY DOCTOR. An human readable analysis of the Redis
* memory condition. */ * memory condition. */
sds getMemoryDoctorReport(void) { sds getMemoryDoctorReport(void) {
serverAssert(aeThreadOwnsLock());
int empty = 0; /* Instance is empty or almost empty. */ int empty = 0; /* Instance is empty or almost empty. */
int big_peak = 0; /* Memory peak is much larger than used mem. */ int big_peak = 0; /* Memory peak is much larger than used mem. */
int high_frag = 0; /* High fragmentation. */ int high_frag = 0; /* High fragmentation. */

View File

@ -293,7 +293,9 @@ int pubsubPublishMessage(robj *channel, robj *message) {
listRewind(list,&li); listRewind(list,&li);
while ((ln = listNext(&li)) != NULL) { while ((ln = listNext(&li)) != NULL) {
client *c = ln->value; client *c = ln->value;
fastlock_lock(&c->lock);
addReplyPubsubMessage(c,channel,message); addReplyPubsubMessage(c,channel,message);
fastlock_unlock(&c->lock);
receivers++; receivers++;
} }
} }
@ -309,8 +311,10 @@ int pubsubPublishMessage(robj *channel, robj *message) {
(char*)ptrFromObj(channel), (char*)ptrFromObj(channel),
sdslen(ptrFromObj(channel)),0)) sdslen(ptrFromObj(channel)),0))
{ {
fastlock_lock(&pat->pclient->lock);
addReplyPubsubPatMessage(pat->pclient, addReplyPubsubPatMessage(pat->pclient,
pat->pattern,channel,message); pat->pattern,channel,message);
fastlock_unlock(&pat->pclient->lock);
receivers++; receivers++;
} }
} }

View File

@ -30,9 +30,17 @@
#ifndef REDIS_RANDOM_H #ifndef REDIS_RANDOM_H
#define REDIS_RANDOM_H #define REDIS_RANDOM_H
#ifdef __cplusplus
extern "C" {
#endif
int32_t redisLrand48(); int32_t redisLrand48();
void redisSrand48(int32_t seedval); void redisSrand48(int32_t seedval);
#ifdef __cplusplus
}
#endif
#define REDIS_LRAND48_MAX INT32_MAX #define REDIS_LRAND48_MAX INT32_MAX
#endif #endif

View File

@ -2140,6 +2140,7 @@ void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
* This function covers the case of RDB -> Salves socket transfers for * This function covers the case of RDB -> Salves socket transfers for
* diskless replication. */ * diskless replication. */
void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) { void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
serverAssert(aeThreadOwnsLock());
uint64_t *ok_slaves; uint64_t *ok_slaves;
if (!bysignal && exitcode == 0) { if (!bysignal && exitcode == 0) {
@ -2259,6 +2260,7 @@ void killRDBChild(void) {
/* Spawn an RDB child that writes the RDB to the sockets of the slaves /* Spawn an RDB child that writes the RDB to the sockets of the slaves
* that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */ * that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) { int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
serverAssert(aeThreadOwnsLock());
int *fds; int *fds;
uint64_t *clientids; uint64_t *clientids;
int numfds; int numfds;

View File

@ -37,6 +37,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <mutex>
void replicationDiscardCachedMaster(void); void replicationDiscardCachedMaster(void);
void replicationResurrectCachedMaster(int newfd); void replicationResurrectCachedMaster(int newfd);
@ -115,6 +116,7 @@ void resizeReplicationBacklog(long long newsize) {
} }
void freeReplicationBacklog(void) { void freeReplicationBacklog(void) {
serverAssert(aeThreadOwnsLock());
serverAssert(listLength(server.slaves) == 0); serverAssert(listLength(server.slaves) == 0);
zfree(server.repl_backlog); zfree(server.repl_backlog);
server.repl_backlog = NULL; server.repl_backlog = NULL;
@ -200,6 +202,12 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
/* We can't have slaves attached and no backlog. */ /* We can't have slaves attached and no backlog. */
serverAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL)); serverAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL));
/* Get the lock on all slaves */
listRewind(slaves,&li);
while((ln = listNext(&li))) {
((client*)ln->value)->lock.lock();
}
/* Send SELECT command to every slave if needed. */ /* Send SELECT command to every slave if needed. */
if (server.slaveseldb != dictid) { if (server.slaveseldb != dictid) {
robj *selectcmd; robj *selectcmd;
@ -280,6 +288,12 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
for (j = 0; j < argc; j++) for (j = 0; j < argc; j++)
addReplyBulkAsync(slave,argv[j]); addReplyBulkAsync(slave,argv[j]);
} }
/* Release the lock on all slaves */
listRewind(slaves,&li);
while((ln = listNext(&li))) {
((client*)ln->value)->lock.unlock();
}
} }
/* This function is used in order to proxy what we receive from our master /* This function is used in order to proxy what we receive from our master
@ -303,6 +317,7 @@ void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t bufle
listRewind(slaves,&li); listRewind(slaves,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *slave = (client*)ln->value; client *slave = (client*)ln->value;
std::lock_guard<decltype(slave->lock)> ulock(slave->lock);
/* Don't feed slaves that are still waiting for BGSAVE to start */ /* Don't feed slaves that are still waiting for BGSAVE to start */
if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue; if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
@ -348,6 +363,7 @@ void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv,
listRewind(monitors,&li); listRewind(monitors,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *monitor = (client*)ln->value; client *monitor = (client*)ln->value;
std::lock_guard<decltype(monitor->lock)> lock(monitor->lock);
addReplyAsync(monitor,cmdobj); addReplyAsync(monitor,cmdobj);
} }
decrRefCount(cmdobj); decrRefCount(cmdobj);
@ -459,6 +475,7 @@ int replicationSetupSlaveForFullResync(client *slave, long long offset) {
* On success return C_OK, otherwise C_ERR is returned and we proceed * On success return C_OK, otherwise C_ERR is returned and we proceed
* with the usual full resync. */ * with the usual full resync. */
int masterTryPartialResynchronization(client *c) { int masterTryPartialResynchronization(client *c) {
serverAssert(aeThreadOwnsLock());
long long psync_offset, psync_len; long long psync_offset, psync_len;
char *master_replid = (char*)ptrFromObj(c->argv[1]); char *master_replid = (char*)ptrFromObj(c->argv[1]);
char buf[128]; char buf[128];
@ -575,6 +592,7 @@ need_full_resync:
* *
* Returns C_OK on success or C_ERR otherwise. */ * Returns C_OK on success or C_ERR otherwise. */
int startBgsaveForReplication(int mincapa) { int startBgsaveForReplication(int mincapa) {
serverAssert(aeThreadOwnsLock());
int retval; int retval;
int socket_target = server.repl_diskless_sync && (mincapa & SLAVE_CAPA_EOF); int socket_target = server.repl_diskless_sync && (mincapa & SLAVE_CAPA_EOF);
listIter li; listIter li;
@ -653,7 +671,7 @@ void syncCommand(client *c) {
* the client about already issued commands. We need a fresh reply * the client about already issued commands. We need a fresh reply
* buffer registering the differences between the BGSAVE and the current * buffer registering the differences between the BGSAVE and the current
* dataset, so that we can copy to other slaves if needed. */ * dataset, so that we can copy to other slaves if needed. */
if (clientHasPendingReplies(c, TRUE)) { if (clientHasPendingReplies(c)) {
addReplyError(c,"SYNC and PSYNC are invalid with pending output"); addReplyError(c,"SYNC and PSYNC are invalid with pending output");
return; return;
} }
@ -1637,6 +1655,7 @@ int slaveTryPartialResynchronization(aeEventLoop *el, int fd, int read_reply) {
/* This handler fires when the non blocking connect was able to /* This handler fires when the non blocking connect was able to
* establish a connection with the master. */ * establish a connection with the master. */
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) { void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
serverAssert(aeThreadOwnsLock());
char tmpfile[256], *err = NULL; char tmpfile[256], *err = NULL;
int dfd = -1, maxtries = 5; int dfd = -1, maxtries = 5;
int sockerr = 0, psync_result; int sockerr = 0, psync_result;
@ -2211,7 +2230,6 @@ void replicationCacheMaster(client *c) {
if (c->flags & CLIENT_MULTI) discardTransaction(c); if (c->flags & CLIENT_MULTI) discardTransaction(c);
listEmpty(c->reply); listEmpty(c->reply);
c->sentlen = 0; c->sentlen = 0;
listEmpty(c->listbufferDoneAsync);
c->sentlenAsync = 0; c->sentlenAsync = 0;
c->reply_bytes = 0; c->reply_bytes = 0;
c->bufpos = 0; c->bufpos = 0;
@ -2299,7 +2317,7 @@ void replicationResurrectCachedMaster(int newfd) {
/* We may also need to install the write handler as well if there is /* We may also need to install the write handler as well if there is
* pending data in the write buffers. */ * pending data in the write buffers. */
if (clientHasPendingReplies(server.master, TRUE)) { if (clientHasPendingReplies(server.master)) {
if (aeCreateFileEvent(server.rgthreadvar[server.master->iel].el, newfd, AE_WRITABLE|AE_WRITE_THREADSAFE, if (aeCreateFileEvent(server.rgthreadvar[server.master->iel].el, newfd, AE_WRITABLE|AE_WRITE_THREADSAFE,
sendReplyToClient, server.master)) { sendReplyToClient, server.master)) {
serverLog(LL_WARNING,"Error resurrecting the cached master, impossible to add the writable handler: %s", strerror(errno)); serverLog(LL_WARNING,"Error resurrecting the cached master, impossible to add the writable handler: %s", strerror(errno));
@ -2527,6 +2545,7 @@ void processClientsWaitingReplicas(void) {
listRewind(server.clients_waiting_acks,&li); listRewind(server.clients_waiting_acks,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *c = (client*)ln->value; client *c = (client*)ln->value;
fastlock_lock(&c->lock);
/* Every time we find a client that is satisfied for a given /* Every time we find a client that is satisfied for a given
* offset and number of replicas, we remember it so the next client * offset and number of replicas, we remember it so the next client
@ -2547,6 +2566,7 @@ void processClientsWaitingReplicas(void) {
addReplyLongLongAsync(c,numreplicas); addReplyLongLongAsync(c,numreplicas);
} }
} }
fastlock_unlock(&c->lock);
} }
} }
@ -2574,7 +2594,11 @@ long long replicationGetSlaveOffset(void) {
/* Replication cron function, called 1 time per second. */ /* Replication cron function, called 1 time per second. */
void replicationCron(void) { void replicationCron(void) {
serverAssert(aeThreadOwnsLock());
static long long replication_cron_loops = 0; static long long replication_cron_loops = 0;
std::unique_lock<decltype(server.master->lock)> ulock;
if (server.master != nullptr)
ulock = decltype(ulock)(server.master->lock);
/* Non blocking connection timeout? */ /* Non blocking connection timeout? */
if (server.masterhost && if (server.masterhost &&

View File

@ -32,11 +32,14 @@
#include "rand.h" #include "rand.h"
#include "cluster.h" #include "cluster.h"
extern "C" {
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include <lualib.h> #include <lualib.h>
}
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
#include <mutex>
char *redisProtocolToLuaType_Int(lua_State *lua, char *reply); char *redisProtocolToLuaType_Int(lua_State *lua, char *reply);
char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply); char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);
@ -89,7 +92,7 @@ struct ldbState {
void sha1hex(char *digest, char *script, size_t len) { void sha1hex(char *digest, char *script, size_t len) {
SHA1_CTX ctx; SHA1_CTX ctx;
unsigned char hash[20]; unsigned char hash[20];
char *cset = "0123456789abcdef"; const char *cset = "0123456789abcdef";
int j; int j;
SHA1Init(&ctx); SHA1Init(&ctx);
@ -223,7 +226,7 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
* with a single "err" field set to the error string. Note that this * with a single "err" field set to the error string. Note that this
* table is never a valid reply by proper commands, since the returned * table is never a valid reply by proper commands, since the returned
* tables are otherwise always indexed by integers, never by strings. */ * tables are otherwise always indexed by integers, never by strings. */
void luaPushError(lua_State *lua, char *error) { void luaPushError(lua_State *lua, const char *error) {
lua_Debug dbg; lua_Debug dbg;
/* If debugging is active and in step mode, log errors resulting from /* If debugging is active and in step mode, log errors resulting from
@ -365,6 +368,8 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
#define LUA_CMD_OBJCACHE_MAX_LEN 64 #define LUA_CMD_OBJCACHE_MAX_LEN 64
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int luaRedisGenericCommand(lua_State *lua, int raise_error) {
int j, argc = lua_gettop(lua); int j, argc = lua_gettop(lua);
int acl_retval = 0;
int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;
struct redisCommand *cmd; struct redisCommand *cmd;
client *c = server.lua_client; client *c = server.lua_client;
sds reply; sds reply;
@ -394,7 +399,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
* To make this function reentrant is futile and makes it slower, but * To make this function reentrant is futile and makes it slower, but
* we should at least detect such a misuse, and abort. */ * we should at least detect such a misuse, and abort. */
if (inuse) { if (inuse) {
char *recursion_warning = const char *recursion_warning =
"luaRedisGenericCommand() recursive call detected. " "luaRedisGenericCommand() recursive call detected. "
"Are you doing funny stuff with Lua debug hooks?"; "Are you doing funny stuff with Lua debug hooks?";
serverLog(LL_WARNING,"%s",recursion_warning); serverLog(LL_WARNING,"%s",recursion_warning);
@ -402,6 +407,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
return 1; return 1;
} }
inuse++; inuse++;
std::unique_lock<decltype(c->lock)> ulock(c->lock);
/* Require at least one argument */ /* Require at least one argument */
if (argc == 0) { if (argc == 0) {
@ -413,7 +419,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
/* Build the arguments vector */ /* Build the arguments vector */
if (argv_size < argc) { if (argv_size < argc) {
argv = zrealloc(argv,sizeof(robj*)*argc, MALLOC_LOCAL); argv = (robj**)zrealloc(argv,sizeof(robj*)*argc, MALLOC_LOCAL);
argv_size = argc; argv_size = argc;
} }
@ -438,7 +444,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] && if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] &&
cached_objects_len[j] >= obj_len) cached_objects_len[j] >= obj_len)
{ {
sds s = ptrFromObj(cached_objects[j]); sds s = (sds)ptrFromObj(cached_objects[j]);
argv[j] = cached_objects[j]; argv[j] = cached_objects[j];
cached_objects[j] = NULL; cached_objects[j] = NULL;
memcpy(s,obj_s,obj_len+1); memcpy(s,obj_s,obj_len+1);
@ -478,14 +484,14 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
break; break;
} else { } else {
cmdlog = sdscatlen(cmdlog," ",1); cmdlog = sdscatlen(cmdlog," ",1);
cmdlog = sdscatsds(cmdlog,ptrFromObj(c->argv[j])); cmdlog = sdscatsds(cmdlog,(sds)ptrFromObj(c->argv[j]));
} }
} }
ldbLog(cmdlog); ldbLog(cmdlog);
} }
/* Command lookup */ /* Command lookup */
cmd = lookupCommand(ptrFromObj(argv[0])); cmd = lookupCommand((sds)ptrFromObj(argv[0]));
if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
(argc < -cmd->arity))) (argc < -cmd->arity)))
{ {
@ -505,7 +511,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
} }
/* Check the ACLs. */ /* Check the ACLs. */
int acl_retval = ACLCheckCommandPerm(c); acl_retval = ACLCheckCommandPerm(c);
if (acl_retval != ACL_OK) { if (acl_retval != ACL_OK) {
if (acl_retval == ACL_DENIED_CMD) if (acl_retval == ACL_DENIED_CMD)
luaPushError(lua, "The user executing the script can't run this " luaPushError(lua, "The user executing the script can't run this "
@ -530,11 +536,11 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
!server.loading && !server.loading &&
!(server.lua_caller->flags & CLIENT_MASTER)) !(server.lua_caller->flags & CLIENT_MASTER))
{ {
luaPushError(lua, ptrFromObj(shared.roslaveerr)); luaPushError(lua, (char*)ptrFromObj(shared.roslaveerr));
goto cleanup; goto cleanup;
} else if (deny_write_type != DISK_ERROR_TYPE_NONE) { } else if (deny_write_type != DISK_ERROR_TYPE_NONE) {
if (deny_write_type == DISK_ERROR_TYPE_RDB) { if (deny_write_type == DISK_ERROR_TYPE_RDB) {
luaPushError(lua, ptrFromObj(shared.bgsaveerr)); luaPushError(lua, (char*)ptrFromObj(shared.bgsaveerr));
} else { } else {
sds aof_write_err = sdscatfmt(sdsempty(), sds aof_write_err = sdscatfmt(sdsempty(),
"-MISCONF Errors writing to the AOF file: %s\r\n", "-MISCONF Errors writing to the AOF file: %s\r\n",
@ -557,7 +563,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
(cmd->flags & CMD_DENYOOM)) (cmd->flags & CMD_DENYOOM))
{ {
if (getMaxmemoryState(NULL,NULL,NULL,NULL) != C_OK) { if (getMaxmemoryState(NULL,NULL,NULL,NULL) != C_OK) {
luaPushError(lua, ptrFromObj(shared.oomerr)); luaPushError(lua, (char*)ptrFromObj(shared.oomerr));
goto cleanup; goto cleanup;
} }
} }
@ -598,7 +604,6 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
} }
/* Run the command */ /* Run the command */
int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;
if (server.lua_replicate_commands) { if (server.lua_replicate_commands) {
/* Set flags according to redis.set_repl() settings. */ /* Set flags according to redis.set_repl() settings. */
if (server.lua_repl & PROPAGATE_AOF) if (server.lua_repl & PROPAGATE_AOF)
@ -622,9 +627,9 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
reply = sdsnewlen(c->buf,c->bufpos); reply = sdsnewlen(c->buf,c->bufpos);
c->bufpos = 0; c->bufpos = 0;
while(listLength(c->reply)) { while(listLength(c->reply)) {
clientReplyBlock *o = listNodeValue(listFirst(c->reply)); clientReplyBlock *o = (clientReplyBlock*)listNodeValue(listFirst(c->reply));
reply = sdscatlen(reply,o->buf,o->used); reply = sdscatlen(reply,o->buf(),o->used);
listDelNode(c->reply,listFirst(c->reply)); listDelNode(c->reply,listFirst(c->reply));
} }
} }
@ -658,9 +663,9 @@ cleanup:
o->refcount == 1 && o->refcount == 1 &&
(o->encoding == OBJ_ENCODING_RAW || (o->encoding == OBJ_ENCODING_RAW ||
o->encoding == OBJ_ENCODING_EMBSTR) && o->encoding == OBJ_ENCODING_EMBSTR) &&
sdslen(ptrFromObj(o)) <= LUA_CMD_OBJCACHE_MAX_LEN) sdslen((sds)ptrFromObj(o)) <= LUA_CMD_OBJCACHE_MAX_LEN)
{ {
sds s = ptrFromObj(o); sds s = (sds)ptrFromObj(o);
if (cached_objects[j]) decrRefCount(cached_objects[j]); if (cached_objects[j]) decrRefCount(cached_objects[j]);
cached_objects[j] = o; cached_objects[j] = o;
cached_objects_len[j] = sdsalloc(s); cached_objects_len[j] = sdsalloc(s);
@ -724,7 +729,7 @@ int luaRedisSha1hexCommand(lua_State *lua) {
* return redis.error_reply("ERR Some Error") * return redis.error_reply("ERR Some Error")
* return redis.status_reply("ERR Some Error") * return redis.status_reply("ERR Some Error")
*/ */
int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) { int luaRedisReturnSingleFieldTable(lua_State *lua, const char *field) {
if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) { if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) {
luaPushError(lua, "wrong number or type of arguments"); luaPushError(lua, "wrong number or type of arguments");
return 1; return 1;
@ -870,10 +875,12 @@ void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
lua_call(lua, 1, 0); lua_call(lua, 1, 0);
} }
extern "C" {
LUALIB_API int (luaopen_cjson) (lua_State *L); LUALIB_API int (luaopen_cjson) (lua_State *L);
LUALIB_API int (luaopen_struct) (lua_State *L); LUALIB_API int (luaopen_struct) (lua_State *L);
LUALIB_API int (luaopen_cmsgpack) (lua_State *L); LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
LUALIB_API int (luaopen_bit) (lua_State *L); LUALIB_API int (luaopen_bit) (lua_State *L);
}
void luaLoadLibraries(lua_State *lua) { void luaLoadLibraries(lua_State *lua) {
luaLoadLib(lua, "", luaopen_base); luaLoadLib(lua, "", luaopen_base);
@ -907,7 +914,7 @@ void luaRemoveUnsupportedFunctions(lua_State *lua) {
* It should be the last to be called in the scripting engine initialization * It should be the last to be called in the scripting engine initialization
* sequence, because it may interact with creation of globals. */ * sequence, because it may interact with creation of globals. */
void scriptingEnableGlobalsProtection(lua_State *lua) { void scriptingEnableGlobalsProtection(lua_State *lua) {
char *s[32]; const char *s[32];
sds code = sdsempty(); sds code = sdsempty();
int j = 0; int j = 0;
@ -1075,7 +1082,7 @@ void scriptingInit(int setup) {
/* Add a helper function that we use to sort the multi bulk output of non /* Add a helper function that we use to sort the multi bulk output of non
* deterministic commands, when containing 'false' elements. */ * deterministic commands, when containing 'false' elements. */
{ {
char *compare_func = "function __redis__compare_helper(a,b)\n" const char *compare_func = "function __redis__compare_helper(a,b)\n"
" if a == false then a = '' end\n" " if a == false then a = '' end\n"
" if b == false then b = '' end\n" " if b == false then b = '' end\n"
" return a<b\n" " return a<b\n"
@ -1089,7 +1096,7 @@ void scriptingInit(int setup) {
* information about the caller, that's what makes sense from the point * information about the caller, that's what makes sense from the point
* of view of the user debugging a script. */ * of view of the user debugging a script. */
{ {
char *errh_func = "local dbg = debug\n" const char *errh_func = "local dbg = debug\n"
"function __redis__err__handler(err)\n" "function __redis__err__handler(err)\n"
" local i = dbg.getinfo(2,'nSl')\n" " local i = dbg.getinfo(2,'nSl')\n"
" if i and i.what == 'C' then\n" " if i and i.what == 'C' then\n"
@ -1137,12 +1144,12 @@ void scriptingReset(void) {
/* Set an array of Redis String Objects as a Lua array (table) stored into a /* Set an array of Redis String Objects as a Lua array (table) stored into a
* global variable. */ * global variable. */
void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) { void luaSetGlobalArray(lua_State *lua, const char *var, robj **elev, int elec) {
int j; int j;
lua_newtable(lua); lua_newtable(lua);
for (j = 0; j < elec; j++) { for (j = 0; j < elec; j++) {
lua_pushlstring(lua,(char*)ptrFromObj(elev[j]),sdslen(ptrFromObj(elev[j]))); lua_pushlstring(lua,(char*)ptrFromObj(elev[j]),sdslen((sds)ptrFromObj(elev[j])));
lua_rawseti(lua,-2,j+1); lua_rawseti(lua,-2,j+1);
} }
lua_setglobal(lua,var); lua_setglobal(lua,var);
@ -1218,19 +1225,19 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) {
funcname[0] = 'f'; funcname[0] = 'f';
funcname[1] = '_'; funcname[1] = '_';
sha1hex(funcname+2,ptrFromObj(body),sdslen(ptrFromObj(body))); sha1hex(funcname+2,(char*)ptrFromObj(body),sdslen((sds)ptrFromObj(body)));
sds sha = sdsnewlen(funcname+2,40); sds sha = sdsnewlen(funcname+2,40);
if ((de = dictFind(server.lua_scripts,sha)) != NULL) { if ((de = dictFind(server.lua_scripts,sha)) != NULL) {
sdsfree(sha); sdsfree(sha);
return dictGetKey(de); return (sds)dictGetKey(de);
} }
sds funcdef = sdsempty(); sds funcdef = sdsempty();
funcdef = sdscat(funcdef,"function "); funcdef = sdscat(funcdef,"function ");
funcdef = sdscatlen(funcdef,funcname,42); funcdef = sdscatlen(funcdef,funcname,42);
funcdef = sdscatlen(funcdef,"() ",3); funcdef = sdscatlen(funcdef,"() ",3);
funcdef = sdscatlen(funcdef,ptrFromObj(body),sdslen(ptrFromObj(body))); funcdef = sdscatlen(funcdef,ptrFromObj(body),sdslen((sds)ptrFromObj(body)));
funcdef = sdscatlen(funcdef,"\nend",4); funcdef = sdscatlen(funcdef,"\nend",4);
if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) { if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) {
@ -1334,11 +1341,11 @@ void evalGenericCommand(client *c, int evalsha) {
funcname[1] = '_'; funcname[1] = '_';
if (!evalsha) { if (!evalsha) {
/* Hash the code if this is an EVAL call */ /* Hash the code if this is an EVAL call */
sha1hex(funcname+2,ptrFromObj(c->argv[1]),sdslen(ptrFromObj(c->argv[1]))); sha1hex(funcname+2,(char*)ptrFromObj(c->argv[1]),sdslen((sds)ptrFromObj(c->argv[1])));
} else { } else {
/* We already have the SHA if it is a EVALSHA */ /* We already have the SHA if it is a EVALSHA */
int j; int j;
char *sha = ptrFromObj(c->argv[1]); char *sha = (char*)ptrFromObj(c->argv[1]);
/* Convert to lowercase. We don't use tolower since the function /* Convert to lowercase. We don't use tolower since the function
* managed to always show up in the profiler output consuming * managed to always show up in the profiler output consuming
@ -1470,13 +1477,13 @@ void evalGenericCommand(client *c, int evalsha) {
* flush our cache of scripts that can be replicated as EVALSHA, while * flush our cache of scripts that can be replicated as EVALSHA, while
* for AOF we need to do so every time we rewrite the AOF file. */ * for AOF we need to do so every time we rewrite the AOF file. */
if (evalsha && !server.lua_replicate_commands) { if (evalsha && !server.lua_replicate_commands) {
if (!replicationScriptCacheExists(ptrFromObj(c->argv[1]))) { if (!replicationScriptCacheExists((sds)ptrFromObj(c->argv[1]))) {
/* This script is not in our script cache, replicate it as /* This script is not in our script cache, replicate it as
* EVAL, then add it into the script cache, as from now on * EVAL, then add it into the script cache, as from now on
* slaves and AOF know about it. */ * slaves and AOF know about it. */
robj *script = dictFetchValue(server.lua_scripts,ptrFromObj(c->argv[1])); robj *script = (robj*)dictFetchValue(server.lua_scripts,ptrFromObj(c->argv[1]));
replicationScriptCacheAdd(ptrFromObj(c->argv[1])); replicationScriptCacheAdd((sds)ptrFromObj(c->argv[1]));
serverAssertWithInfo(c,NULL,script != NULL); serverAssertWithInfo(c,NULL,script != NULL);
/* If the script did not produce any changes in the dataset we want /* If the script did not produce any changes in the dataset we want
@ -1506,7 +1513,7 @@ void evalCommand(client *c) {
} }
void evalShaCommand(client *c) { void evalShaCommand(client *c) {
if (sdslen(ptrFromObj(c->argv[1])) != 40) { if (sdslen((sds)ptrFromObj(c->argv[1])) != 40) {
/* We know that a match is not possible if the provided SHA is /* We know that a match is not possible if the provided SHA is
* not the right length. So we return an error ASAP, this way * not the right length. So we return an error ASAP, this way
* evalGenericCommand() can be implemented without string length * evalGenericCommand() can be implemented without string length
@ -1523,7 +1530,7 @@ void evalShaCommand(client *c) {
} }
void scriptCommand(client *c) { void scriptCommand(client *c) {
if (c->argc == 2 && !strcasecmp(ptrFromObj(c->argv[1]),"help")) { if (c->argc == 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"help")) {
const char *help[] = { const char *help[] = {
"DEBUG (yes|sync|no) -- Set the debug mode for subsequent scripts executed.", "DEBUG (yes|sync|no) -- Set the debug mode for subsequent scripts executed.",
"EXISTS <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.", "EXISTS <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.",
@ -1533,12 +1540,12 @@ void scriptCommand(client *c) {
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
} else if (c->argc == 2 && !strcasecmp(ptrFromObj(c->argv[1]),"flush")) { } else if (c->argc == 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"flush")) {
scriptingReset(); scriptingReset();
addReply(c,shared.ok); addReply(c,shared.ok);
replicationScriptCacheFlush(); replicationScriptCacheFlush();
server.dirty++; /* Propagating this command is a good idea. */ server.dirty++; /* Propagating this command is a good idea. */
} else if (c->argc >= 2 && !strcasecmp(ptrFromObj(c->argv[1]),"exists")) { } else if (c->argc >= 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"exists")) {
int j; int j;
addReplyArrayLen(c, c->argc-2); addReplyArrayLen(c, c->argc-2);
@ -1548,12 +1555,12 @@ NULL
else else
addReply(c,shared.czero); addReply(c,shared.czero);
} }
} else if (c->argc == 3 && !strcasecmp(ptrFromObj(c->argv[1]),"load")) { } else if (c->argc == 3 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"load")) {
sds sha = luaCreateFunction(c,server.lua,c->argv[2]); sds sha = luaCreateFunction(c,server.lua,c->argv[2]);
if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */ if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */
addReplyBulkCBuffer(c,sha,40); addReplyBulkCBuffer(c,sha,40);
forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF); forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);
} else if (c->argc == 2 && !strcasecmp(ptrFromObj(c->argv[1]),"kill")) { } else if (c->argc == 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"kill")) {
if (server.lua_caller == NULL) { if (server.lua_caller == NULL) {
addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n")); addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n"));
} else if (server.lua_caller->flags & CLIENT_MASTER) { } else if (server.lua_caller->flags & CLIENT_MASTER) {
@ -1564,18 +1571,18 @@ NULL
server.lua_kill = 1; server.lua_kill = 1;
addReply(c,shared.ok); addReply(c,shared.ok);
} }
} else if (c->argc == 3 && !strcasecmp(ptrFromObj(c->argv[1]),"debug")) { } else if (c->argc == 3 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"debug")) {
if (clientHasPendingReplies(c, TRUE)) { if (clientHasPendingReplies(c)) {
addReplyError(c,"SCRIPT DEBUG must be called outside a pipeline"); addReplyError(c,"SCRIPT DEBUG must be called outside a pipeline");
return; return;
} }
if (!strcasecmp(ptrFromObj(c->argv[2]),"no")) { if (!strcasecmp((const char*)ptrFromObj(c->argv[2]),"no")) {
ldbDisable(c); ldbDisable(c);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(ptrFromObj(c->argv[2]),"yes")) { } else if (!strcasecmp((const char*)ptrFromObj(c->argv[2]),"yes")) {
ldbEnable(c); ldbEnable(c);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(ptrFromObj(c->argv[2]),"sync")) { } else if (!strcasecmp((const char*)ptrFromObj(c->argv[2]),"sync")) {
ldbEnable(c); ldbEnable(c);
addReply(c,shared.ok); addReply(c,shared.ok);
c->flags |= CLIENT_LUA_DEBUG_SYNC; c->flags |= CLIENT_LUA_DEBUG_SYNC;
@ -1666,8 +1673,8 @@ void ldbSendLogs(void) {
while(listLength(ldb.logs)) { while(listLength(ldb.logs)) {
listNode *ln = listFirst(ldb.logs); listNode *ln = listFirst(ldb.logs);
proto = sdscatlen(proto,"+",1); proto = sdscatlen(proto,"+",1);
sdsmapchars(ln->value,"\r\n"," ",2); sdsmapchars((sds)ln->value,"\r\n"," ",2);
proto = sdscatsds(proto,ln->value); proto = sdscatsds(proto,(sds)ln->value);
proto = sdscatlen(proto,"\r\n",2); proto = sdscatlen(proto,"\r\n",2);
listDelNode(ldb.logs,ln); listDelNode(ldb.logs,ln);
} }
@ -1730,7 +1737,7 @@ int ldbStartSession(client *c) {
/* First argument of EVAL is the script itself. We split it into different /* First argument of EVAL is the script itself. We split it into different
* lines since this is the way the debugger accesses the source code. */ * lines since this is the way the debugger accesses the source code. */
sds srcstring = sdsdup(ptrFromObj(c->argv[1])); sds srcstring = sdsdup((sds)ptrFromObj(c->argv[1]));
size_t srclen = sdslen(srcstring); size_t srclen = sdslen(srcstring);
while(srclen && (srcstring[srclen-1] == '\n' || while(srclen && (srcstring[srclen-1] == '\n' ||
srcstring[srclen-1] == '\r')) srcstring[srclen-1] == '\r'))
@ -1820,7 +1827,7 @@ void evalGenericCommandWithDebugging(client *c, int evalsha) {
/* Return a pointer to ldb.src source code line, considering line to be /* Return a pointer to ldb.src source code line, considering line to be
* one-based, and returning a special string for out of range lines. */ * one-based, and returning a special string for out of range lines. */
char *ldbGetSourceLine(int line) { const char *ldbGetSourceLine(int line) {
int idx = line-1; int idx = line-1;
if (idx < 0 || idx >= ldb.lines) return "<out of range source code line>"; if (idx < 0 || idx >= ldb.lines) return "<out of range source code line>";
return ldb.src[idx]; return ldb.src[idx];
@ -1868,6 +1875,7 @@ int ldbDelBreakpoint(int line) {
sds *ldbReplParseCommand(int *argcp) { sds *ldbReplParseCommand(int *argcp) {
sds *argv = NULL; sds *argv = NULL;
int argc = 0; int argc = 0;
char *plen = NULL;
if (sdslen(ldb.cbuf) == 0) return NULL; if (sdslen(ldb.cbuf) == 0) return NULL;
/* Working on a copy is simpler in this case. We can modify it freely /* Working on a copy is simpler in this case. We can modify it freely
@ -1881,14 +1889,14 @@ sds *ldbReplParseCommand(int *argcp) {
/* Seek and parse *<count>\r\n. */ /* Seek and parse *<count>\r\n. */
p = strchr(p,'*'); if (!p) goto protoerr; p = strchr(p,'*'); if (!p) goto protoerr;
char *plen = p+1; /* Multi bulk len pointer. */ plen = p+1; /* Multi bulk len pointer. */
p = strstr(p,"\r\n"); if (!p) goto protoerr; p = strstr(p,"\r\n"); if (!p) goto protoerr;
*p = '\0'; p += 2; *p = '\0'; p += 2;
*argcp = atoi(plen); *argcp = atoi(plen);
if (*argcp <= 0 || *argcp > 1024) goto protoerr; if (*argcp <= 0 || *argcp > 1024) goto protoerr;
/* Parse each argument. */ /* Parse each argument. */
argv = zmalloc(sizeof(sds)*(*argcp), MALLOC_LOCAL); argv = (sds*)zmalloc(sizeof(sds)*(*argcp), MALLOC_LOCAL);
argc = 0; argc = 0;
while(argc < *argcp) { while(argc < *argcp) {
if (*p != '$') goto protoerr; if (*p != '$') goto protoerr;
@ -1913,8 +1921,8 @@ protoerr:
/* Log the specified line in the Lua debugger output. */ /* Log the specified line in the Lua debugger output. */
void ldbLogSourceLine(int lnum) { void ldbLogSourceLine(int lnum) {
char *line = ldbGetSourceLine(lnum); const char *line = ldbGetSourceLine(lnum);
char *prefix; const char *prefix;
int bp = ldbIsBreakpoint(lnum); int bp = ldbIsBreakpoint(lnum);
int current = ldb.currentline == lnum; int current = ldb.currentline == lnum;
@ -2020,12 +2028,12 @@ sds ldbCatStackValueRec(sds s, lua_State *lua, int idx, int level) {
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
{ {
const void *p = lua_topointer(lua,idx); const void *p = lua_topointer(lua,idx);
char *typename = "unknown"; const char *tname = "unknown";
if (t == LUA_TFUNCTION) typename = "function"; if (t == LUA_TFUNCTION) tname = "function";
else if (t == LUA_TUSERDATA) typename = "userdata"; else if (t == LUA_TUSERDATA) tname = "userdata";
else if (t == LUA_TTHREAD) typename = "thread"; else if (t == LUA_TTHREAD) tname = "thread";
else if (t == LUA_TLIGHTUSERDATA) typename = "light-userdata"; else if (t == LUA_TLIGHTUSERDATA) tname = "light-userdata";
s = sdscatprintf(s,"\"%s@%p\"",typename,p); s = sdscatprintf(s,"\"%s@%p\"",tname,p);
} }
break; break;
default: default:
@ -2044,7 +2052,7 @@ sds ldbCatStackValue(sds s, lua_State *lua, int idx) {
/* Produce a debugger log entry representing the value of the Lua object /* Produce a debugger log entry representing the value of the Lua object
* currently on the top of the stack. The element is ot popped nor modified. * currently on the top of the stack. The element is ot popped nor modified.
* Check ldbCatStackValue() for the actual implementation. */ * Check ldbCatStackValue() for the actual implementation. */
void ldbLogStackValue(lua_State *lua, char *prefix) { void ldbLogStackValue(lua_State *lua, const char *prefix) {
sds s = sdsnew(prefix); sds s = sdsnew(prefix);
s = ldbCatStackValue(s,lua,-1); s = ldbCatStackValue(s,lua,-1);
ldbLogWithMaxLen(s); ldbLogWithMaxLen(s);
@ -2466,7 +2474,7 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
} }
if (ldb.step || bp) { if (ldb.step || bp) {
char *reason = "step over"; const char *reason = "step over";
if (bp) reason = ldb.luabp ? "redis.breakpoint() called" : if (bp) reason = ldb.luabp ? "redis.breakpoint() called" :
"break point"; "break point";
else if (timeout) reason = "timeout reached, infinite loop?"; else if (timeout) reason = "timeout reached, infinite loop?";

View File

@ -1666,12 +1666,15 @@ void clientsCron(int iel) {
c = listNodeValue(head); c = listNodeValue(head);
if (c->iel == iel) if (c->iel == iel)
{ {
fastlock_lock(&c->lock);
/* The following functions do different service checks on the client. /* The following functions do different service checks on the client.
* The protocol is that they return non-zero if the client was * The protocol is that they return non-zero if the client was
* terminated. */ * terminated. */
if (clientsCronHandleTimeout(c,now)) continue; if (clientsCronHandleTimeout(c,now)) goto LContinue;
if (clientsCronResizeQueryBuffer(c)) continue; if (clientsCronResizeQueryBuffer(c)) goto LContinue;
if (clientsCronTrackExpansiveClients(c)) continue; if (clientsCronTrackExpansiveClients(c)) goto LContinue;
LContinue:
fastlock_unlock(&c->lock);
} }
} }
} }
@ -3135,6 +3138,7 @@ struct redisCommand *lookupCommandOrOriginal(sds name) {
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int flags) int flags)
{ {
serverAssert(aeThreadOwnsLock());
if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF) if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF)
feedAppendOnlyFile(cmd,dbid,argv,argc); feedAppendOnlyFile(cmd,dbid,argv,argc);
if (flags & PROPAGATE_REPL) if (flags & PROPAGATE_REPL)
@ -5034,7 +5038,7 @@ int main(int argc, char **argv) {
initServer(); initServer();
server.cthreads = 4; //testing server.cthreads = 2; //testing
initNetworking(1 /* fReusePort */); initNetworking(1 /* fReusePort */);
if (background || server.pidfile) createPidFile(); if (background || server.pidfile) createPidFile();

View File

@ -49,7 +49,13 @@
#include <pthread.h> #include <pthread.h>
#include <syslog.h> #include <syslog.h>
#include <netinet/in.h> #include <netinet/in.h>
#ifdef __cplusplus
extern "C" {
#include <lua.h> #include <lua.h>
}
#else
#include <lua.h>
#endif
#include <signal.h> #include <signal.h>
typedef long long mstime_t; /* millisecond time type. */ typedef long long mstime_t; /* millisecond time type. */
@ -869,8 +875,6 @@ typedef struct client {
int bufposAsync; int bufposAsync;
int buflenAsync; int buflenAsync;
char *bufAsync; char *bufAsync;
/* Async Done Buffer, moved after a thread is done async writing */
list *listbufferDoneAsync;
int iel; /* the event loop index we're registered with */ int iel; /* the event loop index we're registered with */
struct fastlock lock; struct fastlock lock;
@ -1621,7 +1625,7 @@ void pauseClients(mstime_t duration);
int clientsArePaused(void); int clientsArePaused(void);
int processEventsWhileBlocked(int iel); int processEventsWhileBlocked(int iel);
int handleClientsWithPendingWrites(int iel); int handleClientsWithPendingWrites(int iel);
int clientHasPendingReplies(client *c, int fIncludeAsync); int clientHasPendingReplies(client *c);
void unlinkClient(client *c); void unlinkClient(client *c);
int writeToClient(int fd, client *c, int handler_installed); int writeToClient(int fd, client *c, int handler_installed);
void linkClient(client *c); void linkClient(client *c);

View File

@ -7,6 +7,10 @@ By Steve Reid <steve@edmweb.com>
100% Public Domain 100% Public Domain
*/ */
#ifdef __cplusplus
extern "C" {
#endif
typedef struct { typedef struct {
uint32_t state[5]; uint32_t state[5];
uint32_t count[2]; uint32_t count[2];
@ -21,4 +25,9 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
#ifdef REDIS_TEST #ifdef REDIS_TEST
int sha1Test(int argc, char **argv); int sha1Test(int argc, char **argv);
#endif #endif
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@ -680,6 +680,7 @@ int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb
} else { } else {
/* BRPOPLPUSH failed because of wrong /* BRPOPLPUSH failed because of wrong
* destination type. */ * destination type. */
fastlock_unlock(&receiver->lock);
return C_ERR; return C_ERR;
} }
} }