Compare commits

..
Author SHA1 Message Date
Malavan Sotheeswaran 596c056c0c use fastlock for readwrite lock 2022-01-17 14:52:55 -08:00
Malavan Sotheeswaran ab973e8df4 use forklock in all threads again 2022-01-17 14:06:00 -08:00
16 changed files with 103 additions and 122 deletions

No files matched your search

+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
./runtest-moduleapi
build-ubuntu-old:
runs-on: ubuntu-18.04
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v1
- name: make -j2
+10 -4
View File
@@ -33,13 +33,19 @@ Because of this difference of opinion features which are right for KeyDB may not
Additional Resources
--------------------
Check out KeyDB's [Docker Image](https://hub.docker.com/r/eqalpha/keydb)
Try our docker container: https://hub.docker.com/r/eqalpha/keydb
Join us on [Slack](https://docs.keydb.dev/slack/)
Talk on Gitter: https://gitter.im/KeyDB
Post to the [Community Forum](https://community.keydb.dev)
Visit our Website: https://keydb.dev
Learn more through KeyDB's [Documentation & Learning Center](https://docs.keydb.dev)
See options for channel partners and support contracts: https://keydb.dev/support.html
Learn with KeyDB’s official documentation site: https://docs.keydb.dev
[Subscribe to the KeyDB mailing list](https://eqalpha.us20.list-manage.com/subscribe/post?u=978f486c2f95589b24591a9cc&id=4ab9220500)
Management GUI: We recommend [FastoNoSQL](https://fastonosql.com/) which has official KeyDB support.
Benchmarking KeyDB
+8 -55
View File
@@ -53,7 +53,6 @@
#include "zmalloc.h"
#include "config.h"
#include "serverassert.h"
#include "readwritelock.h"
#ifdef USE_MUTEX
thread_local int cOwnLock = 0;
@@ -88,7 +87,6 @@ mutex_wrapper g_lock;
#else
fastlock g_lock("AE (global)");
#endif
readWriteLock g_forkLock("Fork (global)");
thread_local aeEventLoop *g_eventLoopThisThread = NULL;
/* Include the best multiplexing layer supported by this system.
@@ -153,11 +151,8 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
case AE_ASYNC_OP::PostFunction:
{
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (cmd.fLock) {
g_forkLock.releaseRead();
if (cmd.fLock)
ulock.lock();
g_forkLock.acquireRead();
}
((aePostFunctionProc*)cmd.proc)(cmd.clientData);
break;
}
@@ -165,11 +160,8 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
case AE_ASYNC_OP::PostCppFunction:
{
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (cmd.fLock) {
g_forkLock.releaseRead();
if (cmd.fLock)
ulock.lock();
g_forkLock.acquireRead();
}
(*cmd.pfn)();
delete cmd.pfn;
@@ -553,11 +545,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
if (te->next)
te->next->prev = te->prev;
if (te->finalizerProc) {
if (!ulock.owns_lock()) {
g_forkLock.releaseRead();
ulock.lock();
g_forkLock.acquireRead();
}
if (!ulock.owns_lock()) ulock.lock();
te->finalizerProc(eventLoop, te->clientData);
now = getMonotonicUs();
}
@@ -577,11 +565,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
}
if (te->when <= now) {
if (!ulock.owns_lock()) {
g_forkLock.releaseRead();
ulock.lock();
g_forkLock.acquireRead();
}
if (!ulock.owns_lock()) ulock.lock();
int retval;
id = te->id;
@@ -605,11 +589,8 @@ extern "C" void ProcessEventCore(aeEventLoop *eventLoop, aeFileEvent *fe, int ma
{
#define LOCK_IF_NECESSARY(fe, tsmask) \
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock); \
if (!(fe->mask & tsmask)) { \
g_forkLock.releaseRead(); \
ulock.lock(); \
g_forkLock.acquireRead(); \
}
if (!(fe->mask & tsmask)) \
ulock.lock()
int fired = 0; /* Number of events fired for current fd. */
@@ -721,11 +702,8 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP) {
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE)) {
g_forkLock.releaseRead();
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE))
ulock.lock();
g_forkLock.acquireRead();
}
eventLoop->beforesleep(eventLoop);
}
@@ -736,11 +714,8 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
/* After sleep callback. */
if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP) {
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (!(eventLoop->aftersleepFlags & AE_SLEEP_THREADSAFE)) {
g_forkLock.releaseRead();
if (!(eventLoop->aftersleepFlags & AE_SLEEP_THREADSAFE))
ulock.lock();
g_forkLock.acquireRead();
}
eventLoop->aftersleep(eventLoop);
}
@@ -814,21 +789,9 @@ void setAeLockSetThreadSpinWorker(spin_worker worker)
tl_worker = worker;
}
void aeThreadOnline()
{
g_forkLock.acquireRead();
}
void aeAcquireLock()
{
g_forkLock.releaseRead();
g_lock.lock(tl_worker);
g_forkLock.acquireRead();
}
void aeAcquireForkLock()
{
g_forkLock.upgradeWrite();
}
int aeTryAcquireLock(int fWeak)
@@ -836,21 +799,11 @@ int aeTryAcquireLock(int fWeak)
return g_lock.try_lock(!!fWeak);
}
void aeThreadOffline()
{
g_forkLock.releaseRead();
}
void aeReleaseLock()
{
g_lock.unlock();
}
void aeReleaseForkLock()
{
g_forkLock.downgradeWrite();
}
int aeThreadOwnsLock()
{
return g_lock.fOwnLock();
-4
View File
@@ -164,13 +164,9 @@ void aeSetDontWait(aeEventLoop *eventLoop, int noWait);
void aeClosePipesForForkChild(aeEventLoop *eventLoop);
void setAeLockSetThreadSpinWorker(spin_worker worker);
void aeThreadOnline();
void aeAcquireLock();
void aeAcquireForkLock();
int aeTryAcquireLock(int fWeak);
void aeThreadOffline();
void aeReleaseLock();
void aeReleaseForkLock();
int aeThreadOwnsLock();
#ifdef __cplusplus
+14 -1
View File
@@ -1321,7 +1321,7 @@ void acceptOnThread(connection *conn, int flags, char *cip)
szT = (char*)zmalloc(NET_IP_STR_LEN, MALLOC_LOCAL);
memcpy(szT, cip, NET_IP_STR_LEN);
}
int res = aePostFunction(g_pserver->rgthreadvar[ielTarget].el, [conn, flags, ielTarget, szT] {
int res = aePostFunction(g_pserver->rgthreadvar[ielTarget].el, [conn, flags, ielTarget, szT, fBootLoad] {
connMarshalThread(conn);
acceptCommonHandler(conn,flags,szT,ielTarget);
rgacceptsInFlight[ielTarget].fetch_sub(1, std::memory_order_relaxed);
@@ -1907,6 +1907,19 @@ void ProcessPendingAsyncWrites()
zfree(c->replyAsync);
c->replyAsync = nullptr;
c->fPendingAsyncWrite = FALSE;
// Now install the write event handler
int ae_flags = AE_WRITABLE|AE_WRITE_THREADSAFE;
/* For the fsync=always policy, we want that a given FD is never
* served for reading and writing in the same event loop iteration,
* so that in the middle of receiving the query, and serving it
* to the client, we'll call beforeSleep() that will do the
* actual fsync of AOF to disk. AE_BARRIER ensures that. */
if (g_pserver->aof_state == AOF_ON &&
g_pserver->aof_fsync == AOF_FSYNC_ALWAYS)
{
ae_flags |= AE_BARRIER;
}
if (!((c->replstate == REPL_STATE_NONE ||
(c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack))))
+1 -1
View File
@@ -136,7 +136,7 @@ robj *createEmbeddedStringObject(const char *ptr, size_t len) {
*
* The current limit of 52 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
size_t OBJ_ENCODING_EMBSTR_SIZE_LIMIT = 52;
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 52
//static_assert((sizeof(redisObject)+OBJ_ENCODING_EMBSTR_SIZE_LIMIT-8) == 64, "Max EMBSTR obj should be 64 bytes total");
robj *createStringObject(const char *ptr, size_t len) {
+1 -2
View File
@@ -41,7 +41,6 @@
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
static inline char *med3 (char *, char *, char *,
int (*)(const void *, const void *));
@@ -63,7 +62,7 @@ static inline void swapfunc (char *, char *, size_t, int);
} while (--i > 0); \
}
#define SWAPINIT(a, es) swaptype = (uintptr_t)a % sizeof(long) || \
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
static inline void
+24 -5
View File
@@ -10,7 +10,7 @@ class readWriteLock {
bool m_writeWaiting = false;
public:
readWriteLock(const char *name) : m_readLock(name), m_writeLock(name) {}
void acquireRead() {
std::unique_lock<fastlock> rm(m_readLock);
while (m_writeCount > 0 || m_writeWaiting)
@@ -45,8 +45,20 @@ public:
}
void upgradeWrite(bool exclusive = true) {
releaseRead();
acquireWrite(exclusive);
std::unique_lock<fastlock> rm(m_readLock);
m_writeWaiting = true;
while (m_readCount > 1)
m_cv.wait(rm);
if (exclusive) {
/* Another thread might have the write lock while we have the read lock
but won't be able to release it until they can acquire the read lock
so release the read lock and try again instead of waiting to avoid deadlock */
while(!m_writeLock.try_lock())
m_cv.wait(rm);
}
m_writeCount++;
m_readCount--;
m_writeWaiting = false;
}
bool tryAcquireWrite(bool exclusive = true) {
@@ -64,6 +76,7 @@ public:
void releaseRead() {
std::unique_lock<fastlock> rm(m_readLock);
serverAssert(m_readCount > 0);
m_readCount--;
m_cv.notify_all();
}
@@ -78,8 +91,14 @@ public:
}
void downgradeWrite(bool exclusive = true) {
releaseWrite(exclusive);
acquireRead();
std::unique_lock<fastlock> rm(m_readLock);
serverAssert(m_writeCount > 0);
if (exclusive)
m_writeLock.unlock();
m_writeCount--;
while (m_writeCount > 0 || m_writeWaiting)
m_cv.wait(rm);
m_readCount++;
}
bool hasReader() {
+25 -25
View File
@@ -62,6 +62,7 @@
#include <sys/socket.h>
#include <algorithm>
#include <uuid/uuid.h>
#include <mutex>
#include <condition_variable>
#include "aelocker.h"
#include "motd.h"
@@ -91,12 +92,14 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
/* Global vars */
namespace GlobalHidden {
struct redisServer server; /* Server global state */
readWriteLock forkLock("Fork (global)");
}
redisServer *g_pserver = &GlobalHidden::server;
readWriteLock *g_forkLock = &GlobalHidden::forkLock;
struct redisServerConst cserver;
__thread struct redisServerThreadVars *serverTL = NULL; // thread local server vars
fastlock time_thread_lock("Time thread lock");
std::condition_variable_any time_thread_cv;
std::mutex time_thread_mutex;
std::condition_variable time_thread_cv;
int sleeping_threads = 0;
void wakeTimeThread();
@@ -2412,7 +2415,7 @@ int serverCronLite(struct aeEventLoop *eventLoop, long long id, void *clientData
void blockingOperationStarts() {
if(!g_pserver->blocking_op_nesting++){
__atomic_load(&g_pserver->mstime, &g_pserver->blocked_last_cron, __ATOMIC_ACQUIRE);
g_pserver->blocked_last_cron = g_pserver->mstime;
}
}
@@ -2623,14 +2626,14 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
if (!fSentReplies)
handleClientsWithPendingWrites(iel, aof_state);
aeThreadOffline();
// Scope lock_guard
{
std::unique_lock<fastlock> lock(time_thread_lock);
std::lock_guard<std::mutex> lock(time_thread_mutex);
sleeping_threads++;
serverAssert(sleeping_threads <= cserver.cthreads);
}
g_forkLock->releaseRead();
/* Determine whether the modules are enabled before sleeping, and use that result
both here, and after wakeup to avoid double acquire or release of the GIL */
serverTL->modulesEnabledThisAeLoop = !!moduleCount();
@@ -2651,7 +2654,7 @@ void afterSleep(struct aeEventLoop *eventLoop) {
Otherwise you may double acquire the GIL and cause deadlocks in the module */
if (!ProcessingEventsWhileBlocked) {
if (serverTL->modulesEnabledThisAeLoop) moduleAcquireGIL(TRUE /*fServerThread*/);
aeThreadOnline();
g_forkLock->acquireRead();
wakeTimeThread();
}
}
@@ -6289,7 +6292,8 @@ int redisFork(int purpose) {
openChildInfoPipe();
}
long long startWriteLock = ustime();
aeAcquireForkLock();
g_forkLock->releaseRead();
executeWithoutGlobalLock([](){ g_forkLock->acquireWrite(); });
latencyAddSampleIfNeeded("fork-lock",(ustime()-startWriteLock)/1000);
if ((childpid = fork()) == 0) {
/* Child */
@@ -6299,7 +6303,8 @@ int redisFork(int purpose) {
closeChildUnusedResourceAfterFork();
} else {
/* Parent */
aeReleaseForkLock();
g_forkLock->releaseWrite();
g_forkLock->acquireRead();
g_pserver->stat_total_forks++;
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
@@ -6617,9 +6622,7 @@ void OnTerminate()
void wakeTimeThread() {
updateCachedTime();
aeThreadOffline();
std::unique_lock<fastlock> lock(time_thread_lock);
aeThreadOnline();
std::lock_guard<std::mutex> lock(time_thread_mutex);
if (sleeping_threads >= cserver.cthreads)
time_thread_cv.notify_one();
sleeping_threads--;
@@ -6631,23 +6634,21 @@ void *timeThreadMain(void*) {
delay.tv_sec = 0;
delay.tv_nsec = 100;
int cycle_count = 0;
aeThreadOnline();
g_forkLock->acquireRead();
while (true) {
{
aeThreadOffline();
std::unique_lock<fastlock> lock(time_thread_lock);
aeThreadOnline();
std::unique_lock<std::mutex> lock(time_thread_mutex);
if (sleeping_threads >= cserver.cthreads) {
aeThreadOffline();
g_forkLock->releaseRead();
time_thread_cv.wait(lock);
aeThreadOnline();
g_forkLock->acquireRead();
cycle_count = 0;
}
}
updateCachedTime();
if (cycle_count == MAX_CYCLES_TO_HOLD_FORK_LOCK) {
aeThreadOffline();
aeThreadOnline();
g_forkLock->releaseRead();
g_forkLock->acquireRead();
cycle_count = 0;
}
#if defined(__APPLE__)
@@ -6657,7 +6658,7 @@ void *timeThreadMain(void*) {
#endif
cycle_count++;
}
aeThreadOffline();
g_forkLock->releaseRead();
}
void *workerThreadMain(void *parg)
@@ -6675,7 +6676,7 @@ void *workerThreadMain(void *parg)
}
moduleAcquireGIL(true); // Normally afterSleep acquires this, but that won't be called on the first run
aeThreadOnline();
g_forkLock->acquireRead();
aeEventLoop *el = g_pserver->rgthreadvar[iel].el;
try
{
@@ -6684,7 +6685,7 @@ void *workerThreadMain(void *parg)
catch (ShutdownException)
{
}
aeThreadOffline();
g_forkLock->releaseRead();
moduleReleaseGIL(true);
serverAssert(!GlobalLocksAcquired());
aeDeleteEventLoop(el);
@@ -6822,8 +6823,8 @@ int main(int argc, char **argv) {
g_pserver->sentinel_mode = checkForSentinelMode(argc,argv);
initServerConfig();
serverTL = &g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN];
aeThreadOnline();
aeAcquireLock(); // We own the lock on boot
ACLInit(); /* The ACL subsystem must be initialized ASAP because the
basic networking code and client creation depends on it. */
moduleInitModulesSystem();
@@ -7053,7 +7054,6 @@ int main(int argc, char **argv) {
redisSetCpuAffinity(g_pserver->server_cpulist);
aeReleaseLock(); //Finally we can dump the lock
aeThreadOffline();
moduleReleaseGIL(true);
setOOMScoreAdj(-1);
+1
View File
@@ -2177,6 +2177,7 @@ typedef struct {
//extern struct redisServer server;
extern redisServer *g_pserver;
extern readWriteLock *g_forkLock;
extern struct redisServerConst cserver;
extern __thread struct redisServerThreadVars *serverTL; // thread local server vars
extern struct sharedObjectsStruct shared;
-1
View File
@@ -114,7 +114,6 @@ void pool_free(struct alloc_pool *ppool, void *pv)
return;
}
extern size_t OBJ_ENCODING_EMBSTR_SIZE_LIMIT;
#define EMBSTR_ROBJ_SIZE (sizeof(robj)+sizeof(struct sdshdr8)+OBJ_ENCODING_EMBSTR_SIZE_LIMIT+1)
struct alloc_pool poolobj;
struct alloc_pool poolembstrobj;
+15 -17
View File
@@ -1191,26 +1191,24 @@ void zsetConvert(robj *zobj, int encoding) {
zs->dict = dictCreate(&zsetDictType,NULL);
zs->zsl = zslCreate();
if (ziplistLen(zl) > 0) {
eptr = ziplistIndex(zl,0);
serverAssertWithInfo(NULL,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
serverAssertWithInfo(NULL,zobj,sptr != NULL);
eptr = ziplistIndex(zl,0);
serverAssertWithInfo(NULL,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
serverAssertWithInfo(NULL,zobj,sptr != NULL);
while (eptr != NULL) {
score = zzlGetScore(sptr);
serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
if (vstr == NULL)
ele = sdsfromlonglong(vlong);
else
ele = sdsnewlen((char*)vstr,vlen);
while (eptr != NULL) {
score = zzlGetScore(sptr);
serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
if (vstr == NULL)
ele = sdsfromlonglong(vlong);
else
ele = sdsnewlen((char*)vstr,vlen);
node = zslInsert(zs->zsl,score,ele);
serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);
zzlNext(zl,&eptr,&sptr);
}
node = zslInsert(zs->zsl,score,ele);
serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);
zzlNext(zl,&eptr,&sptr);
}
zfree(zobj->m_ptr);
zobj->m_ptr = zs;
zobj->encoding = OBJ_ENCODING_SKIPLIST;
+1 -1
View File
@@ -243,7 +243,7 @@ void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable) {
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE, MALLOC_LOCAL);
newptr = realloc(realptr,size+PREFIX_SIZE);
if (newptr == NULL) {
if (usable) *usable = 0;
return NULL;
-1
View File
@@ -1,5 +1,4 @@
# Minimal configuration for testing.
bind 127.0.0.1
always-show-logo yes
daemonize no
pidfile /var/run/keydb.pid
+2 -3
View File
@@ -13,9 +13,8 @@ set ::tlsdir "../../tls"
proc main {} {
parse_options
spawn_instance redis $::redis_base_port $::instances_count {
"bind 127.0.0.1"
"cluster-enabled yes"
"appendonly yes"
"cluster-enabled yes"
"appendonly yes"
"testmode yes"
"server-threads 3"
}
-1
View File
@@ -32,7 +32,6 @@ TEST_MODULES = \
auth.so \
keyspace_events.so \
blockedclient.so \
timers.so \
getkeys.so \
test_lazyfree.so \
timer.so \