forked from ray-project/ray
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make GCS Client thread-safe. (ray-project#5413)
- Loading branch information
Showing
8 changed files
with
236 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#include "ray/gcs/redis_async_context.h" | ||
|
||
extern "C" { | ||
#include "ray/thirdparty/hiredis/async.h" | ||
#include "ray/thirdparty/hiredis/hiredis.h" | ||
} | ||
|
||
namespace ray { | ||
|
||
namespace gcs { | ||
|
||
RedisAsyncContext::RedisAsyncContext(redisAsyncContext *redis_async_context) | ||
: redis_async_context_(redis_async_context) { | ||
RAY_CHECK(redis_async_context_ != nullptr); | ||
} | ||
|
||
RedisAsyncContext::~RedisAsyncContext() { | ||
if (redis_async_context_ != nullptr) { | ||
redisAsyncFree(redis_async_context_); | ||
redis_async_context_ = nullptr; | ||
} | ||
} | ||
|
||
redisAsyncContext *RedisAsyncContext::GetRawRedisAsyncContext() { | ||
return redis_async_context_; | ||
} | ||
|
||
void RedisAsyncContext::ResetRawRedisAsyncContext() { | ||
// Reset redis_async_context_ to nullptr because hiredis has released this context. | ||
redis_async_context_ = nullptr; | ||
} | ||
|
||
void RedisAsyncContext::RedisAsyncHandleRead() { | ||
// `redisAsyncHandleRead` is already thread-safe, so no lock here. | ||
redisAsyncHandleRead(redis_async_context_); | ||
} | ||
|
||
void RedisAsyncContext::RedisAsyncHandleWrite() { | ||
// `redisAsyncHandleWrite` will mutate `redis_async_context_`, use a lock to protect | ||
// it. | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
redisAsyncHandleWrite(redis_async_context_); | ||
} | ||
|
||
Status RedisAsyncContext::RedisAsyncCommand(redisCallbackFn *fn, void *privdata, | ||
const char *format, ...) { | ||
va_list ap; | ||
va_start(ap, format); | ||
|
||
int ret_code = 0; | ||
{ | ||
// `redisvAsyncCommand` will mutate `redis_async_context_`, use a lock to protect it. | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
ret_code = redisvAsyncCommand(redis_async_context_, fn, privdata, format, ap); | ||
} | ||
|
||
va_end(ap); | ||
|
||
if (ret_code == REDIS_ERR) { | ||
return Status::RedisError(std::string(redis_async_context_->errstr)); | ||
} | ||
RAY_CHECK(ret_code == REDIS_OK); | ||
return Status::OK(); | ||
} | ||
|
||
Status RedisAsyncContext::RedisAsyncCommandArgv(redisCallbackFn *fn, void *privdata, | ||
int argc, const char **argv, | ||
const size_t *argvlen) { | ||
int ret_code = 0; | ||
{ | ||
// `redisAsyncCommandArgv` will mutate `redis_async_context_`, use a lock to protect | ||
// it. | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
ret_code = | ||
redisAsyncCommandArgv(redis_async_context_, fn, privdata, argc, argv, argvlen); | ||
} | ||
|
||
if (ret_code == REDIS_ERR) { | ||
return Status::RedisError(std::string(redis_async_context_->errstr)); | ||
} | ||
RAY_CHECK(ret_code == REDIS_OK); | ||
return Status::OK(); | ||
} | ||
|
||
} // namespace gcs | ||
|
||
} // namespace ray |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#ifndef RAY_GCS_REDIS_ASYNC_CONTEXT_H | ||
#define RAY_GCS_REDIS_ASYNC_CONTEXT_H | ||
|
||
#include <stdarg.h> | ||
#include <mutex> | ||
#include "ray/common/status.h" | ||
|
||
extern "C" { | ||
#include "ray/thirdparty/hiredis/async.h" | ||
#include "ray/thirdparty/hiredis/hiredis.h" | ||
} | ||
|
||
namespace ray { | ||
|
||
namespace gcs { | ||
|
||
/// \class RedisAsyncContext | ||
/// RedisAsyncContext class is a wrapper of hiredis `asyncRedisContext`, providing | ||
/// C++ style and thread-safe API. | ||
class RedisAsyncContext { | ||
public: | ||
explicit RedisAsyncContext(redisAsyncContext *redis_async_context); | ||
|
||
~RedisAsyncContext(); | ||
|
||
/// Get the raw 'redisAsyncContext' pointer. | ||
/// | ||
/// \return redisAsyncContext * | ||
redisAsyncContext *GetRawRedisAsyncContext(); | ||
|
||
/// Reset the raw 'redisAsyncContext' pointer to nullptr. | ||
void ResetRawRedisAsyncContext(); | ||
|
||
/// Perform command 'redisAsyncHandleRead'. Thread-safe. | ||
void RedisAsyncHandleRead(); | ||
|
||
/// Perform command 'redisAsyncHandleWrite'. Thread-safe. | ||
void RedisAsyncHandleWrite(); | ||
|
||
/// Perform command 'redisvAsyncCommand'. Thread-safe. | ||
/// | ||
/// \param fn Callback that will be called after the command finishes. | ||
/// \param privdata User-defined pointer. | ||
/// \param format Command format. | ||
/// \param ... Command list. | ||
/// \return Status | ||
Status RedisAsyncCommand(redisCallbackFn *fn, void *privdata, const char *format, ...); | ||
|
||
/// Perform command 'redisAsyncCommandArgv'. Thread-safe. | ||
/// | ||
/// \param fn Callback that will be called after the command finishes. | ||
/// \param privdata User-defined pointer. | ||
/// \param argc Number of arguments. | ||
/// \param argv Array with arguments. | ||
/// \param argvlen Array with each argument's length. | ||
/// \return Status | ||
Status RedisAsyncCommandArgv(redisCallbackFn *fn, void *privdata, int argc, | ||
const char **argv, const size_t *argvlen); | ||
|
||
private: | ||
/// This mutex is used to protect `redis_async_context`. | ||
/// NOTE(micafan): All the `redisAsyncContext`-related functions only manipulate memory | ||
/// data and don't actually do any IO operations. So the perf impact of adding the lock | ||
/// should be minimum. | ||
std::mutex mutex_; | ||
redisAsyncContext *redis_async_context_{nullptr}; | ||
}; | ||
|
||
} // namespace gcs | ||
|
||
} // namespace ray | ||
|
||
#endif // RAY_GCS_REDIS_ASYNC_CONTEXT_H |
Oops, something went wrong.