First Part of Internal Ray API Refactor (#1173)

* add Ray status class

* add C++ util files

* add ID types

* more APIs

* build system integration

* add test infrastructure and implement some APIs

* add more tests

* fix bugs

* add task table tests

* update

* add toolchain file

* fix

* test

* link with pthread

* update

* fix

* more fixes

* fixes

* always vendor gtest and gflags

* linting

* fixes

* add constants file

* comments

* more fixes

* fix linting
This commit is contained in:
Philipp Moritz
2017-12-14 14:54:09 -08:00
committed by Robert Nishihara
parent c5c83a4465
commit cac5f47600
27 changed files with 1801 additions and 1 deletions
+1
View File
@@ -94,6 +94,7 @@ install:
- ./.travis/install-cython-examples.sh
- cd python/ray/core
- bash ../../../src/ray/test/run_gcs_tests.sh
- bash ../../../src/common/test/run_tests.sh
- bash ../../../src/plasma/test/run_tests.sh
- bash ../../../src/local_scheduler/test/run_tests.sh
+29
View File
@@ -2,11 +2,40 @@ cmake_minimum_required(VERSION 3.4)
project(ray)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# This ensures that things like gnu++11 get passed correctly
set(CMAKE_CXX_STANDARD 11)
# We require a C++11 compliant compiler
set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(RAY_BUILD_STATIC
"Build the libarrow static libraries"
ON)
option(RAY_BUILD_SHARED
"Build the libarrow shared libraries"
ON)
option(RAY_BUILD_TESTS
"Build the Ray googletest unit tests"
ON)
include(ExternalProject)
include(GNUInstallDirs)
include(BuildUtils)
enable_testing()
include(ThirdpartyToolchain)
set(ARROW_DIR "${CMAKE_CURRENT_LIST_DIR}/src/thirdparty/arrow/"
CACHE STRING "Path of the arrow source directory")
include_directories("${ARROW_DIR}/cpp/src/")
include_directories("${CMAKE_CURRENT_LIST_DIR}/src/")
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src/ray/)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src/common/)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src/plasma/)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src/local_scheduler/)
+228
View File
@@ -0,0 +1,228 @@
function(ADD_THIRDPARTY_LIB LIB_NAME)
set(options)
set(one_value_args SHARED_LIB STATIC_LIB)
set(multi_value_args DEPS)
cmake_parse_arguments(ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif()
if(ARG_STATIC_LIB AND ARG_SHARED_LIB)
if(NOT ARG_STATIC_LIB)
message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}")
endif()
SET(AUG_LIB_NAME "${LIB_NAME}_static")
add_library(${AUG_LIB_NAME} STATIC IMPORTED)
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}")
message("Added static library dependency ${LIB_NAME}: ${ARG_STATIC_LIB}")
SET(AUG_LIB_NAME "${LIB_NAME}_shared")
add_library(${AUG_LIB_NAME} SHARED IMPORTED)
if(MSVC)
# Mark the ”.lib” location as part of a Windows DLL
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}")
else()
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}")
endif()
if(ARG_DEPS)
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES "${ARG_DEPS}")
endif()
message("Added shared library dependency ${LIB_NAME}: ${ARG_SHARED_LIB}")
elseif(ARG_STATIC_LIB)
add_library(${LIB_NAME} STATIC IMPORTED)
set_target_properties(${LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}")
SET(AUG_LIB_NAME "${LIB_NAME}_static")
add_library(${AUG_LIB_NAME} STATIC IMPORTED)
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}")
if(ARG_DEPS)
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES "${ARG_DEPS}")
endif()
message("Added static library dependency ${LIB_NAME}: ${ARG_STATIC_LIB}")
elseif(ARG_SHARED_LIB)
add_library(${LIB_NAME} SHARED IMPORTED)
set_target_properties(${LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}")
SET(AUG_LIB_NAME "${LIB_NAME}_shared")
add_library(${AUG_LIB_NAME} SHARED IMPORTED)
if(MSVC)
# Mark the ”.lib” location as part of a Windows DLL
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}")
else()
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}")
endif()
message("Added shared library dependency ${LIB_NAME}: ${ARG_SHARED_LIB}")
if(ARG_DEPS)
set_target_properties(${AUG_LIB_NAME}
PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES "${ARG_DEPS}")
endif()
else()
message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}")
endif()
endfunction()
function(ADD_RAY_LIB LIB_NAME)
set(options)
set(one_value_args SHARED_LINK_FLAGS)
set(multi_value_args SOURCES STATIC_LINK_LIBS STATIC_PRIVATE_LINK_LIBS SHARED_LINK_LIBS SHARED_PRIVATE_LINK_LIBS DEPENDENCIES)
cmake_parse_arguments(ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif()
add_library(${LIB_NAME}_objlib OBJECT
${ARG_SOURCES}
)
if (ARG_DEPENDENCIES)
add_dependencies(${LIB_NAME}_objlib ${ARG_DEPENDENCIES})
endif()
# Necessary to make static linking into other shared libraries work properly
set_property(TARGET ${LIB_NAME}_objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
set(RUNTIME_INSTALL_DIR bin)
if (RAY_BUILD_SHARED)
add_library(${LIB_NAME}_shared SHARED $<TARGET_OBJECTS:${LIB_NAME}_objlib>)
if(APPLE)
# On OS X, you can avoid linking at library load time and instead
# expecting that the symbols have been loaded separately. This happens
# with libpython* where there can be conflicts between system Python and
# the Python from a thirdparty distribution
set(ARG_SHARED_LINK_FLAGS
"-undefined dynamic_lookup ${ARG_SHARED_LINK_FLAGS}")
endif()
set_target_properties(${LIB_NAME}_shared
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}"
RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}"
PDB_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}"
LINK_FLAGS "${ARG_SHARED_LINK_FLAGS}"
OUTPUT_NAME ${LIB_NAME}
VERSION "${RAY_ABI_VERSION}"
SOVERSION "${RAY_SO_VERSION}")
target_link_libraries(${LIB_NAME}_shared
LINK_PUBLIC ${ARG_SHARED_LINK_LIBS}
LINK_PRIVATE ${ARG_SHARED_PRIVATE_LINK_LIBS})
if (RAY_RPATH_ORIGIN)
if (APPLE)
set(_lib_install_rpath "@loader_path")
else()
set(_lib_install_rpath "\$ORIGIN")
endif()
set_target_properties(${LIB_NAME}_shared PROPERTIES
INSTALL_RPATH ${_lib_install_rpath})
endif()
install(TARGETS ${LIB_NAME}_shared
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if (RAY_BUILD_STATIC)
if (MSVC)
set(LIB_NAME_STATIC ${LIB_NAME}_static)
else()
set(LIB_NAME_STATIC ${LIB_NAME})
endif()
add_library(${LIB_NAME}_static STATIC $<TARGET_OBJECTS:${LIB_NAME}_objlib>)
set_target_properties(${LIB_NAME}_static
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}"
OUTPUT_NAME ${LIB_NAME_STATIC})
target_link_libraries(${LIB_NAME}_static
LINK_PUBLIC ${ARG_STATIC_LINK_LIBS}
LINK_PRIVATE ${ARG_STATIC_PRIVATE_LINK_LIBS})
install(TARGETS ${LIB_NAME}_static
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if (APPLE)
set_target_properties(${LIB_NAME}_shared
PROPERTIES
BUILD_WITH_INSTALL_RPATH ON
INSTALL_NAME_DIR "@rpath")
endif()
endfunction()
############################################################
# Testing
############################################################
# Add a new test case, with or without an executable that should be built.
#
# REL_TEST_NAME is the name of the test. It may be a single component
# (e.g. monotime-test) or contain additional components (e.g.
# net/net_util-test). Either way, the last component must be a globally
# unique name.
#
# The unit test is added with a label of "unittest" to support filtering with
# ctest.
#
# Arguments after the test name will be passed to set_tests_properties().
function(ADD_RAY_TEST REL_TEST_NAME)
set(options NO_VALGRIND)
set(single_value_args)
set(multi_value_args STATIC_LINK_LIBS)
cmake_parse_arguments(ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif()
if(NO_TESTS OR NOT RAY_BUILD_STATIC)
return()
endif()
get_filename_component(TEST_NAME ${REL_TEST_NAME} NAME_WE)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${REL_TEST_NAME}.cc)
# This test has a corresponding .cc file, set it up as an executable.
set(TEST_PATH "${EXECUTABLE_OUTPUT_PATH}/${TEST_NAME}")
add_executable(${TEST_NAME} "${REL_TEST_NAME}.cc")
if (ARG_STATIC_LINK_LIBS)
# Customize link libraries
target_link_libraries(${TEST_NAME} ${ARG_STATIC_LINK_LIBS})
else()
target_link_libraries(${TEST_NAME} ${RAY_TEST_LINK_LIBS})
endif()
add_dependencies(unittest ${TEST_NAME})
else()
# No executable, just invoke the test (probably a script) directly.
set(TEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${REL_TEST_NAME})
endif()
if (RAY_TEST_MEMCHECK AND NOT ARG_NO_VALGRIND)
SET_PROPERTY(TARGET ${TEST_NAME}
APPEND_STRING PROPERTY
COMPILE_FLAGS " -RAY_VALGRIND")
add_test(${TEST_NAME}
bash -c "cd ${EXECUTABLE_OUTPUT_PATH}; valgrind --tool=memcheck --leak-check=full --leak-check-heuristics=stdstring --error-exitcode=1 ${TEST_PATH}")
elseif(MSVC)
add_test(${TEST_NAME} ${TEST_PATH})
else()
add_test(${TEST_NAME}
${BUILD_SUPPORT_DIR}/run-test.sh ${CMAKE_BINARY_DIR} test ${TEST_PATH})
endif()
set_tests_properties(${TEST_NAME} PROPERTIES LABELS "unittest")
endfunction()
+83
View File
@@ -0,0 +1,83 @@
set(GFLAGS_VERSION "2.2.0")
set(GTEST_VERSION "1.8.0")
set(GBENCHMARK_VERSION "1.1.0")
if(RAY_BUILD_TESTS OR RAY_BUILD_BENCHMARKS)
add_custom_target(unittest ctest -L unittest)
if(APPLE)
set(GTEST_CMAKE_CXX_FLAGS "-fPIC -DGTEST_USE_OWN_TR1_TUPLE=1 -Wno-unused-value -Wno-ignored-attributes")
elseif(NOT MSVC)
set(GTEST_CMAKE_CXX_FLAGS "-fPIC")
endif()
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPERCASE_BUILD_TYPE)
set(GTEST_CMAKE_CXX_FLAGS "${EP_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}} ${GTEST_CMAKE_CXX_FLAGS}")
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/googletest_ep-prefix/src/googletest_ep")
set(GTEST_INCLUDE_DIR "${GTEST_PREFIX}/include")
set(GTEST_STATIC_LIB
"${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_MAIN_STATIC_LIB
"${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${GTEST_PREFIX}
-DCMAKE_CXX_FLAGS=${GTEST_CMAKE_CXX_FLAGS})
if (MSVC AND NOT ARROW_USE_STATIC_CRT)
set(GTEST_CMAKE_ARGS ${GTEST_CMAKE_ARGS} -Dgtest_force_shared_crt=ON)
endif()
ExternalProject_Add(googletest_ep
URL "https://github.com/google/googletest/archive/release-${GTEST_VERSION}.tar.gz"
BUILD_BYPRODUCTS ${GTEST_STATIC_LIB} ${GTEST_MAIN_STATIC_LIB}
CMAKE_ARGS ${GTEST_CMAKE_ARGS}
${EP_LOG_OPTIONS})
message(STATUS "GTest include dir: ${GTEST_INCLUDE_DIR}")
message(STATUS "GTest static library: ${GTEST_STATIC_LIB}")
include_directories(SYSTEM ${GTEST_INCLUDE_DIR})
ADD_THIRDPARTY_LIB(gtest
STATIC_LIB ${GTEST_STATIC_LIB})
ADD_THIRDPARTY_LIB(gtest_main
STATIC_LIB ${GTEST_MAIN_STATIC_LIB})
add_dependencies(gtest googletest_ep)
add_dependencies(gtest_main googletest_ep)
set(GFLAGS_CMAKE_CXX_FLAGS ${EP_CXX_FLAGS})
set(GFLAGS_URL "https://github.com/gflags/gflags/archive/v${GFLAGS_VERSION}.tar.gz")
set(GFLAGS_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gflags_ep-prefix/src/gflags_ep")
set(GFLAGS_HOME "${GFLAGS_PREFIX}")
set(GFLAGS_INCLUDE_DIR "${GFLAGS_PREFIX}/include")
if(MSVC)
set(GFLAGS_STATIC_LIB "${GFLAGS_PREFIX}/lib/gflags_static.lib")
else()
set(GFLAGS_STATIC_LIB "${GFLAGS_PREFIX}/lib/libgflags.a")
endif()
set(GFLAGS_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${GFLAGS_PREFIX}
-DBUILD_SHARED_LIBS=OFF
-DBUILD_STATIC_LIBS=ON
-DBUILD_PACKAGING=OFF
-DBUILD_TESTING=OFF
-BUILD_CONFIG_TESTS=OFF
-DINSTALL_HEADERS=ON
-DCMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_CXX_FLAGS}
-DCMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_C_FLAGS}
-DCMAKE_CXX_FLAGS=${GFLAGS_CMAKE_CXX_FLAGS})
ExternalProject_Add(gflags_ep
URL ${GFLAGS_URL}
${EP_LOG_OPTIONS}
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS "${GFLAGS_STATIC_LIB}"
CMAKE_ARGS ${GFLAGS_CMAKE_ARGS})
message(STATUS "GFlags include dir: ${GFLAGS_INCLUDE_DIR}")
message(STATUS "GFlags static library: ${GFLAGS_STATIC_LIB}")
include_directories(SYSTEM ${GFLAGS_INCLUDE_DIR})
ADD_THIRDPARTY_LIB(gflags
STATIC_LIB ${GFLAGS_STATIC_LIB})
add_dependencies(gflags gflags_ep)
endif()
+3 -1
View File
@@ -22,6 +22,8 @@ if (NOT TARGET flatbuffers_ep)
"-DFLATBUFFERS_BUILD_TESTS=OFF")
endif()
set(FBS_DEPENDS flatbuffers_ep)
set(FLATBUFFERS_INCLUDE_DIR "${FLATBUFFERS_PREFIX}/include")
set(FLATBUFFERS_STATIC_LIB "${FLATBUFFERS_PREFIX}/lib/libflatbuffers.a")
set(FLATBUFFERS_COMPILER "${FLATBUFFERS_PREFIX}/bin/flatc")
@@ -33,7 +35,7 @@ include_directories(SYSTEM ${FLATBUFFERS_INCLUDE_DIR})
# Custom CFLAGS
set(CMAKE_C_FLAGS "-g -Wall -Wextra -Werror=implicit-function-declaration -Wno-sign-compare -Wno-unused-parameter -Wno-type-limits -Wno-missing-field-initializers --std=c99 -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L -fPIC -std=c99")
set(CMAKE_C_FLAGS "-g -Wall -Wextra -Werror=implicit-function-declaration -Wno-sign-compare -Wno-unused-parameter -Wno-type-limits -Wno-missing-field-initializers --std=c99 -fPIC -std=c99")
# Code for finding Python
find_package(PythonInterp REQUIRED)
@@ -388,6 +388,49 @@ bool PublishObjectNotification(RedisModuleCtx *ctx,
return true;
}
// This is a temporary redis command that will be removed once
// the GCS uses https://github.com/pcmoritz/credis.
int TableAdd_RedisCommand(RedisModuleCtx *ctx,
RedisModuleString **argv,
int argc) {
if (argc != 3) {
return RedisModule_WrongArity(ctx);
}
RedisModuleString *id = argv[1];
RedisModuleString *data = argv[2];
// Set the keys in the table
RedisModuleKey *key =
OpenPrefixedKey(ctx, "T:", id, REDISMODULE_READ | REDISMODULE_WRITE);
RedisModule_StringSet(key, data);
RedisModule_CloseKey(key);
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
// This is a temporary redis command that will be removed once
// the GCS uses https://github.com/pcmoritz/credis.
int TableLookup_RedisCommand(RedisModuleCtx *ctx,
RedisModuleString **argv,
int argc) {
if (argc != 2) {
return RedisModule_WrongArity(ctx);
}
RedisModuleString *id = argv[1];
RedisModuleKey *key = OpenPrefixedKey(ctx, "T:", id, REDISMODULE_READ);
size_t len = 0;
const char *buf = RedisModule_StringDMA(key, &len, REDISMODULE_READ);
RedisModule_ReplyWithStringBuffer(ctx, buf, len);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
}
/**
* Add a new entry to the object table or update an existing one.
*
@@ -1149,6 +1192,17 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx,
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx, "ray.table_add", TableAdd_RedisCommand,
"write", 0, 0, 0) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx, "ray.table_lookup",
TableLookup_RedisCommand, "readonly", 0, 0,
0) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx, "ray.object_table_lookup",
ObjectTableLookup_RedisCommand, "readonly", 0,
0, 0) == REDISMODULE_ERR) {
+52
View File
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.2)
set(CMAKE_CXX_FLAGS "-g -Wall -Werror -std=c++11")
include_directories(${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/ae)
add_subdirectory(util)
add_subdirectory(gcs)
include(${CMAKE_CURRENT_LIST_DIR}/../common/cmake/Common.cmake)
set(AE_SRCS
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/ae/ae.c
)
set(HIREDIS_SRCS
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/async.c
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/dict.c
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/hiredis.c
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/net.c
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/read.c
${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty/hiredis/sds.c
)
set(RAY_SRCS
id.cc
status.cc
gcs/client.cc
gcs/tables.cc
gcs/redis_context.cc
)
install(FILES
api.h
id.h
status.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ray")
# pkg-config support
configure_file(ray.pc.in
"${CMAKE_CURRENT_BINARY_DIR}/ray.pc"
@ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/ray.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/")
ADD_RAY_LIB(ray
SOURCES ${RAY_SRCS} ${AE_SRCS} ${HIREDIS_SRCS}
DEPENDENCIES gen_gcs_fbs
SHARED_LINK_LIBS
STATIC_LINK_LIBS)
set(RAY_TEST_LINK_LIBS ray_static gtest gtest_main)
+12
View File
@@ -0,0 +1,12 @@
#ifndef RAY_CONSTANTS_H_
#define RAY_CONSTANTS_H_
/// Length of Ray IDs in bytes.
constexpr int64_t kUniqueIDSize = 20;
/// Prefix for the object table keys in redis.
constexpr char kObjectTablePrefix[] = "ObjectTable";
/// Prefix for the task table keys in redis.
constexpr char kTaskTablePrefix[] = "TaskTable";
#endif // RAY_CONSTANTS_H_
+26
View File
@@ -0,0 +1,26 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../common/cmake/Common.cmake)
set(GCS_FBS_SRC "${CMAKE_CURRENT_LIST_DIR}/format/gcs.fbs")
set(OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/format/)
set(GCS_FBS_OUTPUT_FILES
"${OUTPUT_DIR}/gcs_generated.h")
add_custom_command(
OUTPUT ${GCS_FBS_OUTPUT_FILES}
# The --gen-object-api flag generates a C++ class MessageT for each
# flatbuffers message Message, which can be used to store deserialized
# messages in data structures. This is currently used for ObjectInfo for
# example.
COMMAND ${FLATBUFFERS_COMPILER} -c -o ${OUTPUT_DIR} ${GCS_FBS_SRC} --gen-object-api
DEPENDS ${FBS_DEPENDS}
COMMENT "Running flatc compiler on ${GCS_FBS_SRC}"
VERBATIM)
add_custom_target(gen_gcs_fbs DEPENDS ${GCS_FBS_OUTPUT_FILES})
ADD_RAY_TEST(client_test STATIC_LINK_LIBS ray_static gtest gtest_main pthread)
install(FILES
client.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ray/gcs")
+45
View File
@@ -0,0 +1,45 @@
#include "ray/gcs/client.h"
#include "ray/gcs/redis_context.h"
namespace ray {
namespace gcs {
AsyncGcsClient::AsyncGcsClient() {}
AsyncGcsClient::~AsyncGcsClient() {}
Status AsyncGcsClient::Connect(const std::string &address, int port) {
context_.reset(new RedisContext());
RETURN_NOT_OK(context_->Connect(address, port));
object_table_.reset(new ObjectTable(context_));
task_table_.reset(new TaskTable(context_));
return Status::OK();
}
Status Attach(plasma::EventLoop &event_loop) {
// TODO(pcm): Implement this via
// context()->AttachToEventLoop(event loop)
return Status::OK();
}
ObjectTable &AsyncGcsClient::object_table() {
return *object_table_;
}
TaskTable &AsyncGcsClient::task_table() {
return *task_table_;
}
FunctionTable &AsyncGcsClient::function_table() {
return *function_table_;
}
ClassTable &AsyncGcsClient::class_table() {
return *class_table_;
}
} // namespace gcs
} // namespace ray
+81
View File
@@ -0,0 +1,81 @@
#ifndef RAY_GCS_CLIENT_H
#define RAY_GCS_CLIENT_H
#include <map>
#include <string>
#include "plasma/events.h"
#include "ray/id.h"
#include "ray/status.h"
#include "ray/gcs/tables.h"
#include "ray/util/logging.h"
namespace ray {
namespace gcs {
class RedisContext;
class AsyncGcsClient {
public:
AsyncGcsClient();
~AsyncGcsClient();
Status Connect(const std::string &address, int port);
Status Attach(plasma::EventLoop &event_loop);
inline FunctionTable &function_table();
// TODO: Some API for getting the error on the driver
inline ClassTable &class_table();
inline ActorTable &actor_table();
inline CustomSerializerTable &custom_serializer_table();
inline ConfigTable &config_table();
ObjectTable &object_table();
TaskTable &task_table();
inline ErrorTable &error_table();
// We also need something to export generic code to run on workers from the
// driver (to set the PYTHONPATH)
using GetExportCallback = std::function<void(const std::string &data)>;
Status AddExport(const std::string &driver_id, std::string &export_data);
Status GetExport(const std::string &driver_id,
int64_t export_index,
const GetExportCallback &done_callback);
std::shared_ptr<RedisContext> context() { return context_; }
private:
std::unique_ptr<FunctionTable> function_table_;
std::unique_ptr<ClassTable> class_table_;
std::unique_ptr<ObjectTable> object_table_;
std::unique_ptr<TaskTable> task_table_;
std::shared_ptr<RedisContext> context_;
};
class SyncGcsClient {
Status LogEvent(const std::string &key,
const std::string &value,
double timestamp);
Status NotifyError(const std::map<std::string, std::string> &error_info);
Status RegisterFunction(const JobID &job_id,
const FunctionID &function_id,
const std::string &language,
const std::string &name,
const std::string &data);
Status RetrieveFunction(const JobID &job_id,
const FunctionID &function_id,
std::string *name,
std::string *data);
Status AddExport(const std::string &driver_id, std::string &export_data);
Status GetExport(const std::string &driver_id,
int64_t export_index,
std::string *data);
};
} // namespace gcs
} // namespace ray
#endif // RAY_GCS_CLIENT_H
+83
View File
@@ -0,0 +1,83 @@
#include "gtest/gtest.h"
// TODO(pcm): get rid of this and replace with the type safe plasma event loop
extern "C" {
#include "hiredis/async.h"
#include "hiredis/hiredis.h"
#include "hiredis/adapters/ae.h"
}
#include "ray/gcs/client.h"
#include "ray/gcs/tables.h"
namespace ray {
aeEventLoop *loop;
class TestGcs : public ::testing::Test {
public:
TestGcs() {
RAY_CHECK_OK(client_.Connect("127.0.0.1", 6379));
job_id_ = UniqueID::from_random();
}
protected:
gcs::AsyncGcsClient client_;
UniqueID job_id_;
};
void ObjectAdded(gcs::AsyncGcsClient *client,
const UniqueID &id,
std::shared_ptr<ObjectTableDataT> data) {
ASSERT_EQ(data->managers, std::vector<std::string>({"A", "B"}));
}
void Lookup(gcs::AsyncGcsClient *client,
const UniqueID &id,
std::shared_ptr<ObjectTableDataT> data) {
ASSERT_EQ(data->managers, std::vector<std::string>({"A", "B"}));
aeStop(loop);
}
TEST_F(TestGcs, TestObjectTable) {
loop = aeCreateEventLoop(1024);
RAY_CHECK_OK(client_.context()->AttachToEventLoop(loop));
auto data = std::make_shared<ObjectTableDataT>();
data->managers.push_back("A");
data->managers.push_back("B");
ObjectID object_id = ObjectID::from_random();
RAY_CHECK_OK(
client_.object_table().Add(job_id_, object_id, data, &ObjectAdded));
RAY_CHECK_OK(
client_.object_table().Lookup(job_id_, object_id, &Lookup, &Lookup));
aeMain(loop);
aeDeleteEventLoop(loop);
}
void TaskAdded(gcs::AsyncGcsClient *client,
const TaskID &id,
std::shared_ptr<TaskTableDataT> data) {
ASSERT_EQ(data->scheduling_state, 3);
}
void TaskLookup(gcs::AsyncGcsClient *client,
const TaskID &id,
std::shared_ptr<TaskTableDataT> data) {
ASSERT_EQ(data->scheduling_state, 3);
aeStop(loop);
}
TEST_F(TestGcs, TestTaskTable) {
loop = aeCreateEventLoop(1024);
RAY_CHECK_OK(client_.context()->AttachToEventLoop(loop));
auto data = std::make_shared<TaskTableDataT>();
data->scheduling_state = 3;
TaskID task_id = TaskID::from_random();
RAY_CHECK_OK(client_.task_table().Add(job_id_, task_id, data, &TaskAdded));
RAY_CHECK_OK(
client_.task_table().Lookup(job_id_, task_id, &TaskLookup, &TaskLookup));
aeMain(loop);
aeDeleteEventLoop(loop);
}
} // namespace
+41
View File
@@ -0,0 +1,41 @@
enum Language:int {
PYTHON = 0,
CPP = 1,
JAVA = 2
}
table FunctionTableData {
language: Language;
name: string;
data: string;
}
table ObjectTableData {
task_id: string;
object_size: long;
is_put: bool;
never_created: bool;
managers: [string];
}
table TaskTableData {
scheduling_state: int;
scheduler_id: string;
execution_arg_ids: [string];
task_info: string;
}
table ClassTableData {
}
table ActorTableData {
}
table ErrorTableData {
}
table CustomSerializerData {
}
table ConfigTableData {
}
+139
View File
@@ -0,0 +1,139 @@
#include "ray/gcs/redis_context.h"
#include <unistd.h>
extern "C" {
#include "hiredis/async.h"
#include "hiredis/hiredis.h"
#include "hiredis/adapters/ae.h"
}
// TODO(pcm): Integrate into the C++ tree.
#include "state/ray_config.h"
namespace ray {
namespace gcs {
// This is a global redis callback which will be registered for every
// asynchronous redis call. It dispatches the appropriate callback
// that was registered with the RedisCallbackManager.
void GlobalRedisCallback(void *c, void *r, void *privdata) {
if (r == NULL) {
return;
}
int64_t callback_index = reinterpret_cast<int64_t>(privdata);
redisReply *reply = reinterpret_cast<redisReply *>(r);
std::string data = "";
if (reply->type == REDIS_REPLY_NIL) {
} else if (reply->type == REDIS_REPLY_STRING) {
data = std::string(reply->str, reply->len);
} else if (reply->type == REDIS_REPLY_STATUS) {
} else if (reply->type == REDIS_REPLY_ERROR) {
RAY_LOG(ERROR) << "Redis error " << reply->str;
} else {
RAY_LOG(FATAL) << "Fatal redis error of type " << reply->type
<< " and with string " << reply->str;
}
RedisCallbackManager::instance().get(callback_index)(data);
}
int64_t RedisCallbackManager::add(const RedisCallback &function) {
callbacks_.emplace(num_callbacks, std::unique_ptr<RedisCallback>(
new RedisCallback(function)));
return num_callbacks++;
}
RedisCallbackManager::RedisCallback &RedisCallbackManager::get(
int64_t callback_index) {
return *callbacks_[callback_index];
}
#define REDIS_CHECK_ERROR(CONTEXT, REPLY) \
if (REPLY == nullptr || REPLY->type == REDIS_REPLY_ERROR) { \
return Status::RedisError(CONTEXT->errstr); \
}
RedisContext::~RedisContext() {
if (context_) {
redisFree(context_);
}
if (async_context_) {
redisAsyncFree(async_context_);
}
}
Status RedisContext::Connect(const std::string &address, int port) {
int connection_attempts = 0;
context_ = redisConnect(address.c_str(), port);
while (context_ == nullptr || context_->err) {
if (connection_attempts >=
RayConfig::instance().redis_db_connect_retries()) {
if (context_ == nullptr) {
RAY_LOG(FATAL) << "Could not allocate redis context.";
}
if (context_->err) {
RAY_LOG(FATAL) << "Could not establish connection to redis " << address
<< ":" << port;
}
break;
}
RAY_LOG(WARNING) << "Failed to connect to Redis, retrying.";
// Sleep for a little.
usleep(RayConfig::instance().redis_db_connect_wait_milliseconds() * 1000);
context_ = redisConnect(address.c_str(), port);
connection_attempts += 1;
}
redisReply *reply = reinterpret_cast<redisReply *>(
redisCommand(context_, "CONFIG SET notify-keyspace-events Kl"));
REDIS_CHECK_ERROR(context_, reply);
// Connect to async context
async_context_ = redisAsyncConnect(address.c_str(), port);
if (async_context_ == nullptr || async_context_->err) {
RAY_LOG(FATAL) << "Could not establish connection to redis " << address
<< ":" << port;
}
return Status::OK();
}
Status RedisContext::AttachToEventLoop(aeEventLoop *loop) {
if (redisAeAttach(loop, async_context_) != REDIS_OK) {
return Status::RedisError("could not attach redis event loop");
} else {
return Status::OK();
}
}
Status RedisContext::RunAsync(const std::string &command,
const UniqueID &id,
uint8_t *data,
int64_t length,
int64_t callback_index) {
if (length > 0) {
std::string redis_command = command + " %b %b";
int status = redisAsyncCommand(
async_context_,
reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
reinterpret_cast<void *>(callback_index), redis_command.c_str(),
id.data(), id.size(), data, length);
if (status == REDIS_ERR) {
return Status::RedisError(std::string(async_context_->errstr));
}
} else {
std::string redis_command = command + " %b";
int status = redisAsyncCommand(
async_context_,
reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
reinterpret_cast<void *>(callback_index), redis_command.c_str(),
id.data(), id.size());
if (status == REDIS_ERR) {
return Status::RedisError(std::string(async_context_->errstr));
}
}
return Status::OK();
}
} // namespace gcs
} // namespace ray
+63
View File
@@ -0,0 +1,63 @@
#ifndef RAY_GCS_REDIS_CONTEXT_H
#define RAY_GCS_REDIS_CONTEXT_H
#include <functional>
#include <memory>
#include <unordered_map>
#include "ray/id.h"
#include "ray/status.h"
#include "ray/util/logging.h"
struct redisContext;
struct redisAsyncContext;
struct aeEventLoop;
namespace ray {
namespace gcs {
class RedisCallbackManager {
public:
using RedisCallback = std::function<void(const std::string &)>;
static RedisCallbackManager &instance() {
static RedisCallbackManager instance;
return instance;
}
int64_t add(const RedisCallback &function);
RedisCallback &get(int64_t callback_index);
private:
RedisCallbackManager() : num_callbacks(0){};
~RedisCallbackManager() { printf("shut down callback manager\n"); }
int64_t num_callbacks;
std::unordered_map<int64_t, std::unique_ptr<RedisCallback>> callbacks_;
};
class RedisContext {
public:
RedisContext() {}
~RedisContext();
Status Connect(const std::string &address, int port);
Status AttachToEventLoop(aeEventLoop *loop);
Status RunAsync(const std::string &command,
const UniqueID &id,
uint8_t *data,
int64_t length,
int64_t callback_index);
private:
redisContext *context_;
redisAsyncContext *async_context_;
};
} // namespace gcs
} // namespace ray
#endif // RAY_GCS_REDIS_CONTEXT_H
+7
View File
@@ -0,0 +1,7 @@
#include "ray/gcs/tables.h"
namespace ray {
namespace gcs {} // namespace gcs
} // namespace ray
+195
View File
@@ -0,0 +1,195 @@
#ifndef RAY_GCS_TABLES_H
#define RAY_GCS_TABLES_H
#include <map>
#include <string>
#include <unordered_map>
#include "ray/constants.h"
#include "ray/id.h"
#include "ray/status.h"
#include "ray/util/logging.h"
#include "ray/gcs/format/gcs_generated.h"
#include "ray/gcs/redis_context.h"
struct redisAsyncContext;
namespace ray {
namespace gcs {
class RedisContext;
class AsyncGcsClient;
template <typename ID, typename Data>
class Table {
public:
using DataT = typename Data::NativeTableType;
using Callback = std::function<
void(AsyncGcsClient *client, const ID &id, std::shared_ptr<DataT> data)>;
struct CallbackData {
ID id;
std::shared_ptr<DataT> data;
Callback callback;
Table<ID, Data> *table;
AsyncGcsClient *client;
};
Table(const std::shared_ptr<RedisContext> &context) : context_(context){};
/// Add an entry to the table
Status Add(const JobID &job_id,
const ID &id,
std::shared_ptr<DataT> data,
const Callback &done) {
auto d =
std::shared_ptr<CallbackData>(new CallbackData({id, data, done, this}));
int64_t callback_index = RedisCallbackManager::instance().add([d](
const std::string &data) { (d->callback)(d->client, d->id, d->data); });
flatbuffers::FlatBufferBuilder fbb;
fbb.Finish(Data::Pack(fbb, data.get()));
RETURN_NOT_OK(context_->RunAsync("RAY.TABLE_ADD", id,
fbb.GetBufferPointer(), fbb.GetSize(),
callback_index));
return Status::OK();
}
/// Lookup an entry asynchronously
Status Lookup(const JobID &job_id,
const ID &id,
const Callback &lookup,
const Callback &done) {
auto d = std::shared_ptr<CallbackData>(
new CallbackData({id, nullptr, done, this}));
int64_t callback_index =
RedisCallbackManager::instance().add([d](const std::string &data) {
auto result = std::make_shared<DataT>();
auto root = flatbuffers::GetRoot<Data>(data.data());
root->UnPackTo(result.get());
(d->callback)(d->client, d->id, result);
});
std::vector<uint8_t> nil;
RETURN_NOT_OK(context_->RunAsync("RAY.TABLE_LOOKUP", id, nil.data(),
nil.size(), callback_index));
return Status::OK();
}
/// Subscribe to updates of this table
Status Subscribe(const JobID &job_id,
const ID &id,
const Callback &subscribe,
const Callback &done);
/// Remove and entry from the table
Status Remove(const JobID &job_id, const ID &id, const Callback &done);
private:
std::unordered_map<ID, std::unique_ptr<CallbackData>, UniqueIDHasher>
callback_data_;
std::shared_ptr<RedisContext> context_;
};
class ObjectTable : public Table<ObjectID, ObjectTableData> {
public:
ObjectTable(const std::shared_ptr<RedisContext> &context) : Table(context){};
/// Set up a client-specific channel for receiving notifications about
/// available
/// objects from the object table. The callback will be called once per
/// notification received on this channel.
///
/// @param subscribe_all
/// @param object_available_callback Callback to be called when new object
/// becomes available.
/// @param done_callback Callback to be called when subscription is installed.
/// This is only used for the tests.
Status SubscribeToNotifications(const JobID &job_id,
bool subscribe_all,
const Callback &object_available,
const Callback &done);
/// Request notifications about the availability of some objects from the
/// object
/// table. The notifications will be published to this client's object
/// notification channel, which was set up by the method
/// ObjectTableSubscribeToNotifications.
///
/// @param object_ids The object IDs to receive notifications about.
Status RequestNotifications(const JobID &job_id,
const std::vector<ObjectID> &object_ids);
};
using FunctionTable = Table<FunctionID, FunctionTableData>;
using ClassTable = Table<ClassID, ClassTableData>;
using ActorTable = Table<ActorID, ActorTableData>;
class TaskTable : public Table<TaskID, TaskTableData> {
public:
TaskTable(const std::shared_ptr<RedisContext> &context) : Table(context){};
using TestAndUpdateCallback =
std::function<void(std::shared_ptr<TaskTableDataT> task)>;
using SubscribeToTaskCallback =
std::function<void(std::shared_ptr<TaskTableDataT> task)>;
/// Update a task's scheduling information in the task table, if the current
/// value matches the given test value. If the update succeeds, it also
/// updates
/// the task entry's local scheduler ID with the ID of the client who called
/// this function. This assumes that the task spec already exists in the task
/// table entry.
///
/// @param task_id The task ID of the task entry to update.
/// @param test_state_bitmask The bitmask to apply to the task entry's current
/// scheduling state. The update happens if and only if the current
/// scheduling state AND-ed with the bitmask is greater than 0.
/// @param update_state The value to update the task entry's scheduling state
/// with, if the current state matches test_state_bitmask.
/// @param callback Function to be called when database returns result.
Status TestAndUpdate(const JobID &job_id,
const TaskID &task_id,
int test_state_bitmask,
int updata_state,
const TaskTableData &data,
const TestAndUpdateCallback &callback);
/// This has a separate signature from Subscribe in Table
/// Register a callback for a task event. An event is any update of a task in
/// the task table.
/// Events include changes to the task's scheduling state or changes to the
/// task's local scheduler ID.
///
/// @param local_scheduler_id The db_client_id of the local scheduler whose
/// events we want to listen to. If you want to subscribe to updates
/// from
/// all local schedulers, pass in NIL_ID.
/// @param subscribe_callback Callback that will be called when the task table
/// is
/// updated.
/// @param state_filter Events we want to listen to. Can have values from the
/// enum "scheduling_state" in task.h.
/// TODO(pcm): Make it possible to combine these using flags like
/// TASK_STATUS_WAITING | TASK_STATUS_SCHEDULED.
/// @param callback Function to be called when database returns result.
Status SubscribeToTask(const JobID &job_id,
const DBClientID &local_scheduler_id,
int state_filter,
const SubscribeToTaskCallback &callback,
const Callback &done);
};
using ErrorTable = Table<TaskID, ErrorTableData>;
using CustomSerializerTable = Table<ClassID, CustomSerializerData>;
using ConfigTable = Table<ConfigID, ConfigTableData>;
} // namespace gcs
} // namespace ray
#endif // RAY_GCS_TABLES_H
+68
View File
@@ -0,0 +1,68 @@
#include "ray/id.h"
#include <random>
namespace ray {
UniqueID UniqueID::from_random() {
UniqueID id;
uint8_t *data = id.mutable_data();
std::random_device engine;
for (int i = 0; i < kUniqueIDSize; i++) {
data[i] = static_cast<uint8_t>(engine());
}
return id;
}
UniqueID UniqueID::from_binary(const std::string &binary) {
UniqueID id;
std::memcpy(&id, binary.data(), sizeof(id));
return id;
}
const UniqueID UniqueID::nil() {
UniqueID result;
std::fill_n(result.id_, kUniqueIDSize, 255);
return result;
}
bool is_nil(const UniqueID &rhs) {
int i = kUniqueIDSize;
const uint8_t *data = rhs.data();
while (--i > 0 && data[i] == 255) {
}
return i != 0;
}
const uint8_t *UniqueID::data() const {
return id_;
}
uint8_t *UniqueID::mutable_data() {
return id_;
}
size_t UniqueID::size() const {
return kUniqueIDSize;
}
std::string UniqueID::binary() const {
return std::string(reinterpret_cast<const char *>(id_), kUniqueIDSize);
}
std::string UniqueID::hex() const {
constexpr char hex[] = "0123456789abcdef";
std::string result;
for (int i = 0; i < kUniqueIDSize; i++) {
unsigned int val = id_[i];
result.push_back(hex[val >> 4]);
result.push_back(hex[val & 0xf]);
}
return result;
}
bool UniqueID::operator==(const UniqueID &rhs) const {
return std::memcmp(data(), rhs.data(), kUniqueIDSize) == 0;
}
} // namespace ray
+55
View File
@@ -0,0 +1,55 @@
#ifndef RAY_ID_H_
#define RAY_ID_H_
#include <inttypes.h>
#include <cstring>
#include <string>
#include "ray/constants.h"
#include "ray/util/visibility.h"
namespace ray {
class RAY_EXPORT UniqueID {
public:
static UniqueID from_random();
static UniqueID from_binary(const std::string &binary);
static const UniqueID nil();
bool is_nil(const UniqueID &rhs) const;
bool operator==(const UniqueID &rhs) const;
const uint8_t *data() const;
uint8_t *mutable_data();
size_t size() const;
std::string binary() const;
std::string hex() const;
private:
uint8_t id_[kUniqueIDSize];
};
static_assert(std::is_pod<UniqueID>::value, "UniqueID must be plain old data");
struct UniqueIDHasher {
// ID hashing function.
size_t operator()(const UniqueID &id) const {
size_t result;
std::memcpy(&result, id.data(), sizeof(size_t));
return result;
}
};
typedef UniqueID TaskID;
typedef UniqueID JobID;
typedef UniqueID ObjectID;
typedef UniqueID FunctionID;
typedef UniqueID ClassID;
typedef UniqueID ActorID;
typedef UniqueID ActorHandleID;
typedef UniqueID WorkerID;
typedef UniqueID DBClientID;
typedef UniqueID ConfigID;
} // namespace ray
#endif // RAY_ID_H_
+11
View File
@@ -0,0 +1,11 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
so_version=@RAY_SO_VERSION@
abi_version=@RAY_ABI_VERSION@
Name: Ray
Description: Ray is a set of technologies for distributed execution.
Version: @RAY_VERSION@
Libs: -L${libdir} -lray
Cflags: -I${includedir}
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// A Status encapsulates the result of an operation. It may indicate success,
// or it may indicate an error with an associated error message.
//
// Multiple threads can invoke const methods on a Status without
// external synchronization, but if any of the threads may call a
// non-const method, all threads accessing the same Status must use
// external synchronization.
// Adapted from Apache Arrow, Apache Kudu, TensorFlow
#include "ray/status.h"
#include <assert.h>
namespace ray {
Status::Status(StatusCode code, const std::string &msg) {
assert(code != StatusCode::OK);
state_ = new State;
state_->code = code;
state_->msg = msg;
}
void Status::CopyFrom(const State *state) {
delete state_;
if (state == nullptr) {
state_ = nullptr;
} else {
state_ = new State(*state);
}
}
std::string Status::CodeAsString() const {
if (state_ == NULL) {
return "OK";
}
const char *type;
switch (code()) {
case StatusCode::OK:
type = "OK";
break;
case StatusCode::OutOfMemory:
type = "Out of memory";
break;
case StatusCode::KeyError:
type = "Key error";
break;
case StatusCode::TypeError:
type = "Type error";
break;
case StatusCode::Invalid:
type = "Invalid";
break;
case StatusCode::IOError:
type = "IOError";
break;
case StatusCode::UnknownError:
type = "Unknown error";
break;
case StatusCode::NotImplemented:
type = "NotImplemented";
break;
case StatusCode::RedisError:
type = "RedisError";
break;
default:
type = "Unknown";
break;
}
return std::string(type);
}
std::string Status::ToString() const {
std::string result(CodeAsString());
if (state_ == NULL) {
return result;
}
result += ": ";
result += state_->msg;
return result;
}
} // namespace ray
+184
View File
@@ -0,0 +1,184 @@
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// A Status encapsulates the result of an operation. It may indicate success,
// or it may indicate an error with an associated error message.
//
// Multiple threads can invoke const methods on a Status without
// external synchronization, but if any of the threads may call a
// non-const method, all threads accessing the same Status must use
// external synchronization.
// Adapted from Apache Arrow, Apache Kudu, TensorFlow
#ifndef RAY_STATUS_H_
#define RAY_STATUS_H_
#include <cstring>
#include <iosfwd>
#include <string>
#include "ray/util/macros.h"
#include "ray/util/visibility.h"
// Return the given status if it is not OK.
#define RAY_RETURN_NOT_OK(s) \
do { \
::ray::Status _s = (s); \
if (RAY_PREDICT_FALSE(!_s.ok())) { \
return _s; \
} \
} while (0)
// If 'to_call' returns a bad status, CHECK immediately with a logged message
// of 'msg' followed by the status.
#define RAY_CHECK_OK_PREPEND(to_call, msg) \
do { \
::ray::Status _s = (to_call); \
RAY_CHECK(_s.ok()) << (msg) << ": " << _s.ToString(); \
} while (0)
// If the status is bad, CHECK immediately, appending the status to the
// logged message.
#define RAY_CHECK_OK(s) RAY_CHECK_OK_PREPEND(s, "Bad status")
namespace ray {
#define RETURN_NOT_OK(s) \
do { \
Status _s = (s); \
if (RAY_PREDICT_FALSE(!_s.ok())) { \
return _s; \
} \
} while (0)
#define RETURN_NOT_OK_ELSE(s, else_) \
do { \
Status _s = (s); \
if (!_s.ok()) { \
else_; \
return _s; \
} \
} while (0)
enum class StatusCode : char {
OK = 0,
OutOfMemory = 1,
KeyError = 2,
TypeError = 3,
Invalid = 4,
IOError = 5,
UnknownError = 9,
NotImplemented = 10,
RedisError = 11
};
#if defined(__clang__)
// Only clang supports warn_unused_result as a type annotation.
class RAY_MUST_USE_RESULT RAY_EXPORT Status;
#endif
class RAY_EXPORT Status {
public:
// Create a success status.
Status() : state_(NULL) {}
~Status() { delete state_; }
Status(StatusCode code, const std::string &msg);
// Copy the specified status.
Status(const Status &s);
void operator=(const Status &s);
// Return a success status.
static Status OK() { return Status(); }
// Return error status of an appropriate type.
static Status OutOfMemory(const std::string &msg) {
return Status(StatusCode::OutOfMemory, msg);
}
static Status KeyError(const std::string &msg) {
return Status(StatusCode::KeyError, msg);
}
static Status TypeError(const std::string &msg) {
return Status(StatusCode::TypeError, msg);
}
static Status UnknownError(const std::string &msg) {
return Status(StatusCode::UnknownError, msg);
}
static Status NotImplemented(const std::string &msg) {
return Status(StatusCode::NotImplemented, msg);
}
static Status Invalid(const std::string &msg) {
return Status(StatusCode::Invalid, msg);
}
static Status IOError(const std::string &msg) {
return Status(StatusCode::IOError, msg);
}
static Status RedisError(const std::string &msg) {
return Status(StatusCode::RedisError, msg);
}
// Returns true iff the status indicates success.
bool ok() const { return (state_ == NULL); }
bool IsOutOfMemory() const { return code() == StatusCode::OutOfMemory; }
bool IsKeyError() const { return code() == StatusCode::KeyError; }
bool IsInvalid() const { return code() == StatusCode::Invalid; }
bool IsIOError() const { return code() == StatusCode::IOError; }
bool IsTypeError() const { return code() == StatusCode::TypeError; }
bool IsUnknownError() const { return code() == StatusCode::UnknownError; }
bool IsNotImplemented() const { return code() == StatusCode::NotImplemented; }
bool IsRedisError() const { return code() == StatusCode::RedisError; }
// Return a string representation of this status suitable for printing.
// Returns the string "OK" for success.
std::string ToString() const;
// Return a string representation of the status code, without the message
// text or posix code information.
std::string CodeAsString() const;
StatusCode code() const { return ok() ? StatusCode::OK : state_->code; }
std::string message() const { return ok() ? "" : state_->msg; }
private:
struct State {
StatusCode code;
std::string msg;
};
// OK status has a `NULL` state_. Otherwise, `state_` points to
// a `State` structure containing the error code and message(s)
State *state_;
void CopyFrom(const State *s);
};
static inline std::ostream &operator<<(std::ostream &os, const Status &x) {
os << x.ToString();
return os;
}
inline Status::Status(const Status &s)
: state_((s.state_ == NULL) ? NULL : new State(*s.state_)) {}
inline void Status::operator=(const Status &s) {
// The following condition catches both aliasing (when this == &s),
// and the common case where both s and *this are ok.
if (state_ != s.state_) {
CopyFrom(s.state_);
}
}
} // namespace ray
#endif // RAY_STATUS_H_
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# This needs to be run in the build tree, which is normally ray/python/ray/core
# Cause the script to exit if a single command fails.
set -e
# Start Redis.
./src/common/thirdparty/redis/src/redis-server --loglevel warning --loadmodule ./src/common/redis_module/libray_redis_module.so --port 6379 &
sleep 1s
./src/ray/gcs/client_test
./src/common/thirdparty/redis/src/redis-cli -p 6379 shutdown
+6
View File
@@ -0,0 +1,6 @@
install(FILES
logging.h
macros.h
visibility.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ray/util"
)
+145
View File
@@ -0,0 +1,145 @@
#ifndef RAY_UTIL_LOGGING_H
#define RAY_UTIL_LOGGING_H
#ifndef _WIN32
#include <execinfo.h>
#endif
#include <cstdlib>
#include <iostream>
#include "ray/util/macros.h"
namespace ray {
// Stubbed versions of macros defined in glog/logging.h, intended for
// environments where glog headers aren't available.
//
// Add more as needed.
// Log levels. LOG ignores them, so their values are abitrary.
#define RAY_DEBUG (-1)
#define RAY_INFO 0
#define RAY_WARNING 1
#define RAY_ERROR 2
#define RAY_FATAL 3
#define RAY_LOG_INTERNAL(level) ::ray::internal::CerrLog(level)
#define RAY_LOG(level) RAY_LOG_INTERNAL(RAY_##level)
#define RAY_IGNORE_EXPR(expr) ((void) (expr));
#define RAY_CHECK(condition) \
(condition) ? 0 : ::ray::internal::FatalLog(RAY_FATAL) \
<< __FILE__ << __LINE__ \
<< " Check failed: " #condition " "
#ifdef NDEBUG
#define RAY_DFATAL RAY_WARNING
#define DCHECK(condition) \
RAY_IGNORE_EXPR(condition) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_EQ(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_NE(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_LE(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_LT(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_GE(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#define DCHECK_GT(val1, val2) \
RAY_IGNORE_EXPR(val1) \
while (false) \
::ray::internal::NullLog()
#else
#define RAY_DFATAL RAY_FATAL
#define DCHECK(condition) RAY_CHECK(condition)
#define DCHECK_EQ(val1, val2) RAY_CHECK((val1) == (val2))
#define DCHECK_NE(val1, val2) RAY_CHECK((val1) != (val2))
#define DCHECK_LE(val1, val2) RAY_CHECK((val1) <= (val2))
#define DCHECK_LT(val1, val2) RAY_CHECK((val1) < (val2))
#define DCHECK_GE(val1, val2) RAY_CHECK((val1) >= (val2))
#define DCHECK_GT(val1, val2) RAY_CHECK((val1) > (val2))
#endif // NDEBUG
namespace internal {
class NullLog {
public:
template <class T>
NullLog &operator<<(const T &t) {
return *this;
}
};
class CerrLog {
public:
CerrLog(int severity) // NOLINT(runtime/explicit)
: severity_(severity),
has_logged_(false) {}
virtual ~CerrLog() {
if (has_logged_) {
std::cerr << std::endl;
}
if (severity_ == RAY_FATAL) {
std::exit(1);
}
}
template <class T>
CerrLog &operator<<(const T &t) {
if (severity_ != RAY_DEBUG) {
has_logged_ = true;
std::cerr << t;
}
return *this;
}
protected:
const int severity_;
bool has_logged_;
};
// Clang-tidy isn't smart enough to determine that DCHECK using CerrLog doesn't
// return so we create a new class to give it a hint.
class FatalLog : public CerrLog {
public:
explicit FatalLog(int /* severity */) // NOLINT
: CerrLog(RAY_FATAL) {} // NOLINT
RAY_NORETURN ~FatalLog() {
if (has_logged_) {
std::cerr << std::endl;
#if defined(_EXECINFO_H) || !defined(_WIN32)
void *buffer[255];
const int calls = backtrace(buffer, sizeof(buffer) / sizeof(void *));
backtrace_symbols_fd(buffer, calls, 1);
#endif
}
std::abort();
}
};
} // namespace internal
} // namespace ray
#endif // RAY_UTIL_LOGGING_H
+44
View File
@@ -0,0 +1,44 @@
#ifndef RAY_UTIL_MACROS_H
#define RAY_UTIL_MACROS_H
// From Google gutil
#ifndef RAY_DISALLOW_COPY_AND_ASSIGN
#define RAY_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName &) = delete; \
void operator=(const TypeName &) = delete
#endif
#define RAY_UNUSED(x) (void) x
//
// GCC can be told that a certain branch is not likely to be taken (for
// instance, a CHECK failure), and use that information in static analysis.
// Giving it this information can help it optimize for the common case in
// the absence of better information (ie. -fprofile-arcs).
//
#if defined(__GNUC__)
#define RAY_PREDICT_FALSE(x) (__builtin_expect(x, 0))
#define RAY_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#define RAY_NORETURN __attribute__((noreturn))
#define RAY_PREFETCH(addr) __builtin_prefetch(addr)
#elif defined(_MSC_VER)
#define RAY_NORETURN __declspec(noreturn)
#define RAY_PREDICT_FALSE(x) x
#define RAY_PREDICT_TRUE(x) x
#define RAY_PREFETCH(addr)
#else
#define RAY_NORETURN
#define RAY_PREDICT_FALSE(x) x
#define RAY_PREDICT_TRUE(x) x
#define RAY_PREFETCH(addr)
#endif
#if (defined(__GNUC__) || defined(__APPLE__))
#define RAY_MUST_USE_RESULT __attribute__((warn_unused_result))
#elif defined(_MSC_VER)
#define RAY_MUST_USE_RESULT
#else
#define RAY_MUST_USE_RESULT
#endif
#endif // RAY_UTIL_MACROS_H
+44
View File
@@ -0,0 +1,44 @@
#ifndef RAY_UTIL_VISIBILITY_H
#define RAY_UTIL_VISIBILITY_H
#if defined(_WIN32) || defined(__CYGWIN__)
#if defined(_MSC_VER)
#pragma warning(disable : 4251)
#else
#pragma GCC diagnostic ignored "-Wattributes"
#endif
#ifdef RAY_EXPORTING
#define RAY_EXPORT __declspec(dllexport)
#else
#define RAY_EXPORT __declspec(dllimport)
#endif
#define RAY_NO_EXPORT
#else // Not Windows
#ifndef RAY_EXPORT
#define RAY_EXPORT __attribute__((visibility("default")))
#endif
#ifndef RAY_NO_EXPORT
#define RAY_NO_EXPORT __attribute__((visibility("hidden")))
#endif
#endif // Non-Windows
// gcc and clang disagree about how to handle template visibility when you have
// explicit specializations https://llvm.org/bugs/show_bug.cgi?id=24815
#if defined(__clang__)
#define RAY_EXTERN_TEMPLATE extern template class RAY_EXPORT
#else
#define RAY_EXTERN_TEMPLATE extern template class
#endif
// This is a complicated topic, some reading on it:
// http://www.codesynthesis.com/~boris/blog/2010/01/18/dll-export-cxx-templates/
#if defined(_MSC_VER) || defined(__clang__)
#define RAY_TEMPLATE_EXPORT RAY_EXPORT
#else
#define RAY_TEMPLATE_EXPORT
#endif
#endif // RAY_UTIL_VISIBILITY_H