Pull Plasma from Apache Arrow and remove Plasma store from Ray. (#692)

* Rebase Ray on top of Plasma in Apache Arrow

* add thirdparty building scripts

* use rebased arrow

* fix

* fix build

* fix python visibility

* comment out C tests for now

* fix multithreading

* fix

* reduce logging

* fix plasma manager multithreading

* make sure old and new object IDs can coexist peacefully

* more rebasing

* update

* fixes

* fix

* install pyarrow

* install cython

* fix

* install newer cmake

* fix

* rebase on top of latest arrow

* getting runtest.py run locally (needed to comment out a test for that to work)

* work on plasma tests

* more fixes

* fix local scheduler tests

* fix global scheduler test

* more fixes

* fix python 3 bytes vs string

* fix manager tests valgrind

* fix documentation building

* fix linting

* fix c++ linting

* fix linting

* add tests back in

* Install without sudo.

* Set PKG_CONFIG_PATH in build.sh so that Ray can find plasma.

* Install pkg-config

* Link -lpthread, note that find_package(Threads) doesn't seem to work reliably.

* Comment in testGPUIDs in runtest.py.

* Set PKG_CONFIG_PATH when building pyarrow.

* Pull apache/arrow and not pcmoritz/arrow.

* Fix installation in docker image.

* adapt to changes of the plasma api

* Fix installation of pyarrow module.

* Fix linting.

* Use correct python executable to build pyarrow.
This commit is contained in:
Philipp Moritz
2017-07-31 21:04:15 -07:00
committed by Robert Nishihara
parent dfcd399dbb
commit c3b39b4d86
64 changed files with 470 additions and 5761 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ target_link_libraries(common "${CMAKE_CURRENT_LIST_DIR}/thirdparty/hiredis/libhi
function(define_test test_name library)
add_executable(${test_name} test/${test_name}.cc ${ARGN})
add_dependencies(${test_name} hiredis flatbuffers_ep)
target_link_libraries(${test_name} common ${FLATBUFFERS_STATIC_LIB} ${library})
target_link_libraries(${test_name} common ${FLATBUFFERS_STATIC_LIB} ${ARROW_DIR}/cpp/build/release/libarrow.a ${library} -lpthread)
target_compile_options(${test_name} PUBLIC "-DPLASMA_TEST -DLOCAL_SCHEDULER_TEST -DCOMMON_TEST -DRAY_COMMON_LOG_LEVEL=4")
endfunction()
+1 -1
View File
@@ -5,7 +5,7 @@ include(CMakeParseArguments)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(FLATBUFFERS_VERSION "1.6.0")
set(FLATBUFFERS_VERSION "1.7.1")
set(FLATBUFFERS_PREFIX "${CMAKE_BINARY_DIR}/flatbuffers_ep-prefix/src/flatbuffers_ep-install")
if (NOT TARGET flatbuffers_ep)
+1 -2
View File
@@ -15,8 +15,7 @@
/* This is used to define the array of object IDs. */
const UT_icd object_id_icd = {sizeof(ObjectID), NULL, NULL, NULL};
const UniqueID NIL_ID = {{255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255}};
const UniqueID NIL_ID = UniqueID::nil();
const unsigned char NIL_DIGEST[DIGEST_SIZE] = {0};
+21 -20
View File
@@ -22,6 +22,9 @@ extern "C" {
}
#endif
#include "plasma/common.h"
#include "arrow/util/macros.h"
/** The duration between heartbeats. These are sent by the plasma manager and
* local scheduler. */
#define HEARTBEAT_TIMEOUT_MILLISECONDS 100
@@ -46,12 +49,6 @@ extern "C" {
#define RAY_COMMON_LOG_LEVEL RAY_COMMON_INFO
#endif
/* Arrow defines the same macro, only define it if it has not already been
* defined. */
#ifndef UNUSED
#define UNUSED(x) ((void) (x))
#endif
/**
* Macros to enable each level of Ray logging statements depending on the
* current logging level. */
@@ -113,19 +110,7 @@ extern "C" {
#define CHECK(COND) CHECKM(COND, "")
/* This should be defined if we want to check calls to DCHECK. */
#define RAY_DCHECK
/* Arrow also defines the DCHECK macro, so undo that definition. */
#ifdef DCHECK
#undef DCHECK
#endif
#ifdef RAY_DCHECK
#define DCHECK(COND) CHECK(COND)
#else
#define DCHECK(COND)
#endif
#define RAY_DCHECK(COND) CHECK(COND)
/* These are exit codes for common errors that can occur in Ray components. */
#define EXIT_COULD_NOT_BIND_PORT -2
@@ -141,7 +126,23 @@ extern "C" {
#define IS_NIL_ID(id) UNIQUE_ID_EQ(id, NIL_ID)
typedef struct { unsigned char id[UNIQUE_ID_SIZE]; } UniqueID;
struct UniqueID {
unsigned char id[UNIQUE_ID_SIZE];
UniqueID(const plasma::UniqueID &from) {
memcpy(&id[0], from.data(), UNIQUE_ID_SIZE);
}
UniqueID() {}
static const UniqueID nil() {
UniqueID result;
std::fill_n(result.id, UNIQUE_ID_SIZE, 255);
return result;
}
plasma::UniqueID to_plasma_id() {
plasma::UniqueID result;
memcpy(result.mutable_data(), &id[0], UNIQUE_ID_SIZE);
return result;
}
};
extern const UniqueID NIL_ID;
+2 -2
View File
@@ -11,7 +11,7 @@ extern "C" {
ObjectID task_compute_return_id(TaskID task_id, int64_t return_index) {
/* Here, return_indices need to be >= 0, so we can use negative
* indices for put. */
DCHECK(return_index >= 0);
RAY_DCHECK(return_index >= 0);
/* TODO(rkn): This line requires object and task IDs to be the same size. */
ObjectID return_id = task_id;
int64_t *first_bytes = (int64_t *) &return_id;
@@ -22,7 +22,7 @@ ObjectID task_compute_return_id(TaskID task_id, int64_t return_index) {
}
ObjectID task_compute_put_id(TaskID task_id, int64_t put_index) {
DCHECK(put_index >= 0);
RAY_DCHECK(put_index >= 0);
/* TODO(pcm): This line requires object and task IDs to be the same size. */
ObjectID put_id = task_id;
int64_t *first_bytes = (int64_t *) &put_id;
+10 -2
View File
@@ -5,6 +5,12 @@ project(local_scheduler)
# Recursively include common
include(${CMAKE_CURRENT_LIST_DIR}/../common/cmake/Common.cmake)
# Include plasma
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/arrow/python/cmake_modules)
find_package(Plasma)
include_directories(SYSTEM ${PLASMA_INCLUDE_DIR})
add_definitions(-fPIC)
if(APPLE)
@@ -21,7 +27,9 @@ endif()
include_directories("${CMAKE_CURRENT_LIST_DIR}/")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../")
# TODO(pcm): get rid of this:
include_directories("${CMAKE_CURRENT_LIST_DIR}/../plasma/")
include_directories("${ARROW_DIR}/cpp/src/")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../common/format/")
# Compile flatbuffers
@@ -60,10 +68,10 @@ endif(APPLE)
add_dependencies(local_scheduler_library gen_local_scheduler_fbs)
add_executable(local_scheduler local_scheduler.cc local_scheduler_algorithm.cc)
target_link_libraries(local_scheduler local_scheduler_client common ${HIREDIS_LIB} plasma_lib)
target_link_libraries(local_scheduler local_scheduler_client common ${HIREDIS_LIB} ${PLASMA_STATIC_LIB} ${ARROW_DIR}/cpp/build/release/libarrow.a -lpthread)
add_executable(local_scheduler_tests test/local_scheduler_tests.cc local_scheduler.cc local_scheduler_algorithm.cc)
target_link_libraries(local_scheduler_tests local_scheduler_client common ${HIREDIS_LIB} plasma_lib)
target_link_libraries(local_scheduler_tests local_scheduler_client common ${HIREDIS_LIB} ${PLASMA_STATIC_LIB} ${ARROW_DIR}/cpp/build/release/libarrow.a -lpthread)
target_compile_options(local_scheduler_tests PUBLIC "-DLOCAL_SCHEDULER_TEST")
install(TARGETS local_scheduler_library DESTINATION ${CMAKE_SOURCE_DIR}/local_scheduler)
+2 -2
View File
@@ -359,7 +359,7 @@ LocalSchedulerState *LocalSchedulerState_init(
state->db = NULL;
}
/* Connect to Plasma. This method will retry if Plasma hasn't started yet. */
state->plasma_conn = new PlasmaClient();
state->plasma_conn = new plasma::PlasmaClient();
if (plasma_manager_socket_name != NULL) {
ARROW_CHECK_OK(state->plasma_conn->Connect(plasma_store_socket_name,
plasma_manager_socket_name,
@@ -370,7 +370,7 @@ LocalSchedulerState *LocalSchedulerState_init(
}
/* Subscribe to notifications about sealed objects. */
int plasma_fd;
ARROW_CHECK_OK(state->plasma_conn->Subscribe(plasma_fd));
ARROW_CHECK_OK(state->plasma_conn->Subscribe(&plasma_fd));
/* Add the callback that processes the notification to the event loop. */
event_loop_add_file(loop, plasma_fd, EVENT_LOOP_READ,
process_plasma_notification, state);
@@ -456,11 +456,11 @@ void add_task_to_actor_queue(LocalSchedulerState *state,
void fetch_missing_dependency(LocalSchedulerState *state,
SchedulingAlgorithmState *algorithm_state,
std::list<TaskQueueEntry>::iterator task_entry_it,
ObjectID obj_id) {
plasma::ObjectID obj_id) {
if (algorithm_state->remote_objects.count(obj_id) == 0) {
/* We weren't actively fetching this object. Try the fetch once
* immediately. */
if (plasma_manager_is_connected(state->plasma_conn)) {
if (state->plasma_conn->get_manager_fd() != -1) {
ARROW_CHECK_OK(state->plasma_conn->Fetch(1, &obj_id));
}
/* Create an entry and add it to the list of active fetch requests to
@@ -497,7 +497,8 @@ void fetch_missing_dependencies(
ObjectID obj_id = TaskSpec_arg_id(task, i);
if (algorithm_state->local_objects.count(obj_id) == 0) {
/* If the entry is not yet available locally, record the dependency. */
fetch_missing_dependency(state, algorithm_state, task_entry_it, obj_id);
fetch_missing_dependency(state, algorithm_state, task_entry_it,
obj_id.to_plasma_id());
++num_missing_dependencies;
}
}
@@ -536,7 +537,7 @@ int fetch_object_timeout_handler(event_loop *loop, timer_id id, void *context) {
LocalSchedulerState *state = (LocalSchedulerState *) context;
/* Only try the fetches if we are connected to the object store manager. */
if (!plasma_manager_is_connected(state->plasma_conn)) {
if (state->plasma_conn->get_manager_fd() == -1) {
LOG_INFO("Local scheduler is not connected to a object store manager");
return kLocalSchedulerFetchTimeoutMilliseconds;
}
@@ -555,8 +556,9 @@ int fetch_object_timeout_handler(event_loop *loop, timer_id id, void *context) {
for (int64_t j = 0; j < num_object_ids; j += fetch_request_size) {
int num_objects_in_request =
std::min(num_object_ids, j + fetch_request_size) - j;
ARROW_CHECK_OK(
state->plasma_conn->Fetch(num_objects_in_request, &object_ids[j]));
ARROW_CHECK_OK(state->plasma_conn->Fetch(
num_objects_in_request,
reinterpret_cast<plasma::ObjectID *>(&object_ids[j])));
}
/* Print a warning if this method took too long. */
@@ -1237,7 +1239,7 @@ void handle_object_removed(LocalSchedulerState *state,
ObjectID arg_id = TaskSpec_arg_id(it->spec, i);
if (ObjectID_equal(arg_id, removed_object_id)) {
fetch_missing_dependency(state, algorithm_state, it,
removed_object_id);
removed_object_id.to_plasma_id());
}
}
}
@@ -6,6 +6,8 @@
#include "common/io.h"
#include "common/task.h"
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
LocalSchedulerConnection *LocalSchedulerConnection_init(
const char *local_scheduler_socket,
+2 -2
View File
@@ -4,7 +4,7 @@
#include "common/task.h"
#include "common/state/table.h"
#include "common/state/db.h"
#include "plasma_client.h"
#include "plasma/client.h"
#include <list>
#include <unordered_map>
@@ -57,7 +57,7 @@ struct LocalSchedulerState {
/** The handle to the database. */
DBHandle *db;
/** The Plasma client. */
PlasmaClient *plasma_conn;
plasma::PlasmaClient *plasma_conn;
/** State for the scheduling algorithm. */
SchedulingAlgorithmState *algorithm_state;
/** Input buffer, used for reading input in process_message to avoid
+11 -14
View File
@@ -6,6 +6,12 @@ include(${CMAKE_CURRENT_LIST_DIR}/../common/cmake/Common.cmake)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
# Include plasma
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/arrow/python/cmake_modules)
find_package(Plasma)
include_directories(SYSTEM ${PLASMA_INCLUDE_DIR})
option(HAS_PLASMA
"Are we linking with the plasma object store? Recommended if numbuf is used as part of ray."
ON)
@@ -29,24 +35,15 @@ if(UNIX AND NOT APPLE)
link_libraries(rt)
endif()
set(ARROW_DIR "${CMAKE_CURRENT_LIST_DIR}/thirdparty/arrow/"
CACHE STRING "Path of the arrow source directory")
set(ARROW_LIB "${CMAKE_CURRENT_LIST_DIR}/thirdparty/arrow/cpp/build/release/libarrow.a"
set(ARROW_LIB "${ARROW_DIR}/cpp/build/release/libarrow.a"
CACHE STRING "Path to libarrow.a (needs to be changed if arrow is build in debug mode)")
set(ARROW_PYTHON_LIB "${CMAKE_CURRENT_LIST_DIR}/thirdparty/arrow/cpp/build/release/libarrow_python.a"
set(ARROW_PYTHON_LIB "${ARROW_DIR}/cpp/build/release/libarrow_python.a"
CACHE STRING "Path to libarrow_python.a (needs to be changed if arrow is build in debug mode)")
include_directories("${ARROW_DIR}/cpp/src/")
# include_directories("${ARROW_DIR}/cpp/src/")
include_directories("cpp/src/")
include_directories("python/src/")
if(HAS_PLASMA)
include_directories("${CMAKE_CURRENT_LIST_DIR}/../plasma")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../common")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../common/thirdparty")
endif()
add_definitions(-fPIC)
add_library(numbuf SHARED
@@ -60,11 +57,11 @@ add_library(numbuf SHARED
if(APPLE)
target_link_libraries(numbuf "-undefined dynamic_lookup" ${ARROW_LIB} ${ARROW_PYTHON_LIB} -lpthread)
else()
target_link_libraries(numbuf -Wl,--whole-archive ${ARROW_LIB} -Wl,--no-whole-archive ${ARROW_PYTHON_LIB} -lpthread)
target_link_libraries(numbuf -Wl,--whole-archive ${ARROW_LIB} -Wl,--no-whole-archive ${ARROW_PYTHON_LIB} -lpthread -lboost_system -lboost_filesystem)
endif()
if(HAS_PLASMA)
target_link_libraries(numbuf plasma_lib common)
target_link_libraries(numbuf ${PLASMA_STATIC_LIB} ${ARROW_DIR}/cpp/build/release/libarrow.a common)
endif()
install(TARGETS numbuf DESTINATION ${CMAKE_SOURCE_DIR}/numbuf/)
+11 -10
View File
@@ -104,17 +104,18 @@ Status SequenceBuilder::AppendDict(int32_t size) {
#define ADD_SUBSEQUENCE(DATA, OFFSETS, BUILDER, TAG, NAME) \
if (DATA) { \
DCHECK(DATA->length() == OFFSETS.back()); \
auto list_builder = std::make_shared<ListBuilder>(pool_, DATA); \
auto field = std::make_shared<Field>(NAME, list_builder->type()); \
std::shared_ptr<Array> offset_array; \
Int32Builder builder(pool_, std::make_shared<Int32Type>()); \
RETURN_NOT_OK(builder.Append(OFFSETS.data(), OFFSETS.size())); \
RETURN_NOT_OK(builder.Finish(&offset_array)); \
std::shared_ptr<Array> list_array; \
ListArray::FromArrays(*offset_array, *DATA, pool_, &list_array); \
auto field = std::make_shared<Field>(NAME, list_array->type()); \
auto type = std::make_shared<StructType>(std::vector<FieldPtr>({field})); \
auto lists = std::vector<std::shared_ptr<ArrayBuilder>>({list_builder}); \
StructBuilder builder(pool_, type, lists); \
OFFSETS.pop_back(); \
RETURN_NOT_OK(list_builder->Append(OFFSETS.data(), OFFSETS.size())); \
for (int i = 0; i < list_builder->length(); ++i) { \
RETURN_NOT_OK(builder.Append()); \
} \
ADD_ELEMENT(builder, TAG); \
types[TAG] = std::make_shared<Field>("", type); \
children[TAG] = std::shared_ptr<StructArray>( \
new StructArray(type, list_array->length(), {list_array})); \
RETURN_NOT_OK(nones_.AppendToBitmap(true)); \
type_ids.push_back(TAG); \
} else { \
DCHECK(OFFSETS.size() == 1); \
+32 -12
View File
@@ -12,24 +12,23 @@
// plasma_protocol, because that file is used both with the store and the
// manager, the store uses it the ObjectID from plasma_common.h and the
// manager uses it with the ObjectID from common.h.
#include "plasma_common.h"
#include "plasma/common.h"
#include "plasma_client.h"
#include "plasma_protocol.h"
#include "plasma/client.h"
#include "plasma/protocol.h"
extern "C" {
PyObject* NumbufPlasmaOutOfMemoryError;
PyObject* NumbufPlasmaObjectExistsError;
}
#include "plasma_extension.h"
using namespace plasma;
#endif
#include <arrow/api.h>
#include <arrow/io/memory.h>
#include <arrow/ipc/api.h>
#include <arrow/ipc/util.h>
#include <arrow/ipc/writer.h>
#include <arrow/python/numpy_convert.h>
@@ -85,7 +84,7 @@ Status read_batch_and_tensors(uint8_t* data, int64_t size,
auto source = std::make_shared<arrow::io::BufferReader>(
LENGTH_PREFIX_SIZE + data, size - LENGTH_PREFIX_SIZE);
RETURN_NOT_OK(arrow::ipc::FileReader::Open(source, batch_size, &reader));
RETURN_NOT_OK(reader->GetRecordBatch(0, batch_out));
RETURN_NOT_OK(reader->ReadRecordBatch(0, batch_out));
int64_t offset = batch_size;
while (true) {
std::shared_ptr<Tensor> tensor;
@@ -129,6 +128,26 @@ static void ArrowCapsule_Destructor(PyObject* capsule) {
delete reinterpret_cast<RayObject*>(PyCapsule_GetPointer(capsule, "arrow"));
}
static int PyObjectToPlasmaClient(PyObject* object, PlasmaClient** client) {
if (PyCapsule_IsValid(object, "plasma")) {
*client = reinterpret_cast<PlasmaClient*>(PyCapsule_GetPointer(object, "plasma"));
return 1;
} else {
PyErr_SetString(PyExc_TypeError, "must be a 'plasma' capsule");
return 0;
}
}
int PyStringToUniqueID(PyObject* object, ObjectID* object_id) {
if (PyBytes_Check(object)) {
memcpy(object_id, PyBytes_AsString(object), sizeof(ObjectID));
return 1;
} else {
PyErr_SetString(PyExc_TypeError, "must be a 20 character string");
return 0;
}
}
/* Documented in doc/numbuf.rst in ray-core */
static PyObject* serialize_list(PyObject* self, PyObject* args) {
PyObject* value;
@@ -152,7 +171,7 @@ static PyObject* serialize_list(PyObject* self, PyObject* args) {
object->batch = make_batch(array);
int64_t data_size, total_size;
auto mock = std::make_shared<arrow::ipc::MockOutputStream>();
auto mock = std::make_shared<arrow::io::MockOutputStream>();
write_batch_and_tensors(
mock.get(), object->batch, object->arrays, &data_size, &total_size);
@@ -253,14 +272,15 @@ static PyObject* register_callbacks(PyObject* self, PyObject* args) {
* @return Void.
*/
static void BufferCapsule_Destructor(PyObject* capsule) {
ObjectID* id = reinterpret_cast<ObjectID*>(PyCapsule_GetPointer(capsule, "buffer"));
plasma::ObjectID* id =
reinterpret_cast<plasma::ObjectID*>(PyCapsule_GetPointer(capsule, "buffer"));
auto context = reinterpret_cast<PyObject*>(PyCapsule_GetContext(capsule));
/* We use the context of the connection capsule to indicate if the connection
* is still active (if the context is NULL) or if it is closed (if the context
* is (void*) 0x1). This is neccessary because the primary pointer of the
* capsule cannot be NULL. */
if (PyCapsule_GetContext(context) == NULL) {
PlasmaClient* client;
plasma::PlasmaClient* client;
ARROW_CHECK(PyObjectToPlasmaClient(context, &client));
ARROW_CHECK_OK(client->Release(*id));
}
@@ -282,7 +302,7 @@ static void BufferCapsule_Destructor(PyObject* capsule) {
*/
static PyObject* store_list(PyObject* self, PyObject* args) {
ObjectID obj_id;
PlasmaClient* client;
plasma::PlasmaClient* client;
PyObject* value;
if (!PyArg_ParseTuple(args, "O&O&O", PyStringToUniqueID, &obj_id,
PyObjectToPlasmaClient, &client, &value)) {
@@ -300,7 +320,7 @@ static PyObject* store_list(PyObject* self, PyObject* args) {
std::shared_ptr<RecordBatch> batch = make_batch(array);
int64_t data_size, total_size;
auto mock = std::make_shared<arrow::ipc::MockOutputStream>();
auto mock = std::make_shared<arrow::io::MockOutputStream>();
write_batch_and_tensors(mock.get(), batch, tensors, &data_size, &total_size);
uint8_t* data;
@@ -363,7 +383,7 @@ static PyObject* retrieve_list(PyObject* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "OOL", &object_id_list, &plasma_client, &timeout_ms)) {
return NULL;
}
PlasmaClient* client;
plasma::PlasmaClient* client;
if (!PyObjectToPlasmaClient(plasma_client, &client)) { return NULL; }
Py_ssize_t num_object_ids = PyList_Size(object_id_list);
-28
View File
@@ -1,28 +0,0 @@
#!/bin/bash
set -x
# Cause the script to exit if a single command fails.
set -e
TP_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd)
PREFIX=$TP_DIR/installed
# Determine how many parallel jobs to use for make based on the number of cores
unamestr="$(uname)"
if [[ "$unamestr" == "Linux" ]]; then
PARALLEL=$(nproc)
elif [[ "$unamestr" == "Darwin" ]]; then
PARALLEL=$(sysctl -n hw.ncpu)
echo "Platform is macosx."
else
echo "Unrecognized platform."
exit 1
fi
echo "building arrow"
cd $TP_DIR/arrow/cpp
mkdir -p $TP_DIR/arrow/cpp/build
cd $TP_DIR/arrow/cpp/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-g -O3" -DCMAKE_CXX_FLAGS="-g -O3" -DARROW_BUILD_TESTS=OFF -DARROW_HDFS=OFF -DARROW_PYTHON=on ..
make VERBOSE=1 -j$PARALLEL
+9 -81
View File
@@ -5,102 +5,30 @@ project(plasma)
# Recursively include common
include(${CMAKE_CURRENT_LIST_DIR}/../common/cmake/Common.cmake)
if(APPLE)
SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
endif(APPLE)
# Include plasma
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/arrow/python/cmake_modules)
include_directories("${PYTHON_INCLUDE_DIRS}" thirdparty)
find_package(Plasma)
include_directories(SYSTEM ${PLASMA_INCLUDE_DIR})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99 -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L -O3")
# Compile flatbuffers
set(PLASMA_FBS_SRC "${CMAKE_CURRENT_LIST_DIR}/format/plasma.fbs")
set(OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/format/)
set(PLASMA_FBS_OUTPUT_FILES
"${OUTPUT_DIR}/plasma_generated.h")
add_custom_command(
OUTPUT ${PLASMA_FBS_OUTPUT_FILES}
COMMAND ${FLATBUFFERS_COMPILER} -c -o ${OUTPUT_DIR} ${PLASMA_FBS_SRC}
DEPENDS ${PLASMA_FBS_SRC}
COMMENT "Running flatc compiler on ${PLASMA_FBS_SRC}"
VERBATIM)
add_custom_target(gen_plasma_fbs DEPENDS ${PLASMA_FBS_OUTPUT_FILES})
add_dependencies(gen_plasma_fbs flatbuffers_ep)
if(UNIX AND NOT APPLE)
link_libraries(rt)
endif()
include_directories("${CMAKE_CURRENT_LIST_DIR}/")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../")
add_library(plasma SHARED
plasma.cc
plasma_extension.cc
plasma_protocol.cc
plasma_client.cc
thirdparty/xxhash.c
fling.c)
add_dependencies(plasma gen_plasma_fbs)
if(APPLE)
target_link_libraries(plasma plasma_lib "-undefined dynamic_lookup" -Wl,-force_load,${FLATBUFFERS_STATIC_LIB} ${PYTHON_LIBRARIES} ${FLATBUFFERS_STATIC_LIB} -lpthread)
else(APPLE)
target_link_libraries(plasma plasma_lib -Wl,--whole-archive ${FLATBUFFERS_STATIC_LIB} -Wl,--no-whole-archive ${PYTHON_LIBRARIES} ${FLATBUFFERS_STATIC_LIB} -lpthread)
endif(APPLE)
include_directories("${ARROW_DIR}/cpp/src/")
# include_directories("${CMAKE_CURRENT_LIST_DIR}/../")
include_directories("${FLATBUFFERS_INCLUDE_DIR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set_source_files_properties(thirdparty/dlmalloc.c PROPERTIES COMPILE_FLAGS -Wno-all)
add_library(plasma_lib STATIC
plasma_client.cc
plasma.cc
plasma_common.cc
plasma_io.cc
plasma_protocol.cc
status.cc
fling.c
thirdparty/xxhash.c)
target_link_libraries(plasma_lib ${FLATBUFFERS_STATIC_LIB} -lpthread)
add_dependencies(plasma_lib gen_plasma_fbs)
add_executable(plasma_store
plasma_store.cc
thirdparty/ae/ae.c
plasma.cc
plasma_events.cc
plasma_protocol.cc
eviction_policy.cc
fling.c
malloc.cc)
add_dependencies(plasma_store hiredis gen_plasma_fbs)
target_link_libraries(plasma_store plasma_lib ${FLATBUFFERS_STATIC_LIB})
add_dependencies(plasma protocol_fbs)
add_executable(plasma_manager
plasma_manager.cc)
target_link_libraries(plasma_manager common plasma_lib ${FLATBUFFERS_STATIC_LIB})
target_link_libraries(plasma_manager common ${PLASMA_STATIC_LIB} ${ARROW_DIR}/cpp/build/release/libarrow.a -lpthread)
add_library(plasma_client SHARED plasma_client.cc)
target_link_libraries(plasma_client ${FLATBUFFERS_STATIC_LIB})
target_link_libraries(plasma_client plasma_lib ${FLATBUFFERS_STATIC_LIB})
define_test(client_tests plasma_lib)
define_test(manager_tests plasma_lib plasma_manager.cc)
define_test(serialization_tests plasma_lib)
define_test(client_tests ${PLASMA_STATIC_LIB})
define_test(manager_tests ${PLASMA_STATIC_LIB} plasma_manager.cc)
-95
View File
@@ -1,95 +0,0 @@
#include "eviction_policy.h"
void LRUCache::add(const ObjectID &key, int64_t size) {
auto it = item_map_.find(key);
ARROW_CHECK(it == item_map_.end());
/* Note that it is important to use a list so the iterators stay valid. */
item_list_.emplace_front(key, size);
item_map_.emplace(key, item_list_.begin());
}
void LRUCache::remove(const ObjectID &key) {
auto it = item_map_.find(key);
ARROW_CHECK(it != item_map_.end());
item_list_.erase(it->second);
item_map_.erase(it);
}
int64_t LRUCache::choose_objects_to_evict(
int64_t num_bytes_required,
std::vector<ObjectID> &objects_to_evict) {
int64_t bytes_evicted = 0;
auto it = item_list_.end();
while (bytes_evicted < num_bytes_required && it != item_list_.begin()) {
it--;
objects_to_evict.push_back(it->first);
bytes_evicted += it->second;
}
return bytes_evicted;
}
EvictionPolicy::EvictionPolicy(PlasmaStoreInfo *store_info)
: memory_used_(0), store_info_(store_info) {}
int64_t EvictionPolicy::choose_objects_to_evict(
int64_t num_bytes_required,
std::vector<ObjectID> &objects_to_evict) {
int64_t bytes_evicted =
cache_.choose_objects_to_evict(num_bytes_required, objects_to_evict);
/* Update the LRU cache. */
for (auto &object_id : objects_to_evict) {
cache_.remove(object_id);
}
/* Update the number of bytes used. */
memory_used_ -= bytes_evicted;
return bytes_evicted;
}
void EvictionPolicy::object_created(ObjectID object_id) {
auto entry = store_info_->objects[object_id].get();
cache_.add(object_id, entry->info.data_size + entry->info.metadata_size);
}
bool EvictionPolicy::require_space(int64_t size,
std::vector<ObjectID> &objects_to_evict) {
/* Check if there is enough space to create the object. */
int64_t required_space = memory_used_ + size - store_info_->memory_capacity;
int64_t num_bytes_evicted;
if (required_space > 0) {
/* Try to free up at least as much space as we need right now but ideally
* up to 20% of the total capacity. */
int64_t space_to_free = std::max(size, store_info_->memory_capacity / 5);
ARROW_LOG(DEBUG)
<< "not enough space to create this object, so evicting objects";
/* Choose some objects to evict, and update the return pointers. */
num_bytes_evicted =
choose_objects_to_evict(space_to_free, objects_to_evict);
ARROW_LOG(INFO)
<< "There is not enough space to create this object, so evicting "
<< objects_to_evict.size() << " objects to free up "
<< num_bytes_evicted << " bytes.";
} else {
num_bytes_evicted = 0;
}
if (num_bytes_evicted >= required_space) {
/* We only increment the space used if there is enough space to create the
* object. */
memory_used_ += size;
}
return num_bytes_evicted >= required_space;
}
void EvictionPolicy::begin_object_access(
ObjectID object_id,
std::vector<ObjectID> &objects_to_evict) {
/* If the object is in the LRU cache, remove it. */
cache_.remove(object_id);
}
void EvictionPolicy::end_object_access(
ObjectID object_id,
std::vector<ObjectID> &objects_to_evict) {
auto entry = store_info_->objects[object_id].get();
/* Add the object to the LRU cache.*/
cache_.add(object_id, entry->info.data_size + entry->info.metadata_size);
}
-128
View File
@@ -1,128 +0,0 @@
#ifndef EVICTION_POLICY_H
#define EVICTION_POLICY_H
#include <list>
#include <unordered_map>
#include "plasma_common.h"
#include "plasma.h"
/* ==== The eviction policy ====
*
* This file contains declaration for all functions and data structures that
* need to be provided if you want to implement a new eviction algorithm for the
* Plasma store.
*/
class LRUCache {
private:
/** A doubly-linked list containing the items in the cache and
* their sizes in LRU order. */
typedef std::list<std::pair<ObjectID, int64_t>> ItemList;
ItemList item_list_;
/** A hash table mapping the object ID of an object in the cache to its
* location in the doubly linked list item_list_. */
std::unordered_map<ObjectID, ItemList::iterator, UniqueIDHasher> item_map_;
public:
LRUCache(){};
void add(const ObjectID &key, int64_t size);
void remove(const ObjectID &key);
int64_t choose_objects_to_evict(int64_t num_bytes_required,
std::vector<ObjectID> &objects_to_evict);
};
/** The eviction policy. */
class EvictionPolicy {
public:
/**
* Construct an eviction policy.
*
* @param store_info Information about the Plasma store that is exposed
* to the eviction policy.
*/
EvictionPolicy(PlasmaStoreInfo *store_info);
/**
* This method will be called whenever an object is first created in order to
* add it to the LRU cache. This is done so that the first time, the Plasma
* store calls begin_object_access, we can remove the object from the LRU
* cache.
*
* @param object_id The object ID of the object that was created.
* @return Void.
*/
void object_created(ObjectID object_id);
/**
* This method will be called when the Plasma store needs more space, perhaps
* to create a new object. If the required amount of space cannot be freed up,
* then a fatal error will be thrown. When this method is called, the eviction
* policy will assume that the objects chosen to be evicted will in fact be
* evicted from the Plasma store by the caller.
*
* @param size The size in bytes of the new object, including both data and
* metadata.
* @param objects_to_evict The object IDs that were chosen for eviction will
* be stored into this vector.
* @return True if enough space can be freed and false otherwise.
*/
bool require_space(int64_t size, std::vector<ObjectID> &objects_to_evict);
/**
* This method will be called whenever an unused object in the Plasma store
* starts to be used. When this method is called, the eviction policy will
* assume that the objects chosen to be evicted will in fact be evicted from
* the Plasma store by the caller.
*
* @param object_id The ID of the object that is now being used.
* @param objects_to_evict The object IDs that were chosen for eviction will
* be stored into this vector.
* @return Void.
*/
void begin_object_access(ObjectID object_id,
std::vector<ObjectID> &objects_to_evict);
/**
* This method will be called whenever an object in the Plasma store that was
* being used is no longer being used. When this method is called, the
* eviction policy will assume that the objects chosen to be evicted will in
* fact be evicted from the Plasma store by the caller.
*
* @param object_id The ID of the object that is no longer being used.
* @param objects_to_evict The object IDs that were chosen for eviction will
* be stored into this vector.
* @return Void.
*/
void end_object_access(ObjectID object_id,
std::vector<ObjectID> &objects_to_evict);
/**
* Choose some objects to evict from the Plasma store. When this method is
* called, the eviction policy will assume that the objects chosen to be
* evicted will in fact be evicted from the Plasma store by the caller.
*
* @note This method is not part of the API. It is exposed in the header file
* only for testing.
*
* @param num_bytes_required The number of bytes of space to try to free up.
* @param objects_to_evict The object IDs that were chosen for eviction will
* be stored into this vector.
* @return The total number of bytes of space chosen to be evicted.
*/
int64_t choose_objects_to_evict(int64_t num_bytes_required,
std::vector<ObjectID> &objects_to_evict);
private:
/** Pointer to the plasma store info. */
PlasmaStoreInfo *store_info_;
/** The amount of memory (in bytes) currently being used. */
int64_t memory_used_;
/** Datastructure for the LRU cache. */
LRUCache cache_;
};
#endif /* EVICTION_POLICY_H */
-76
View File
@@ -1,76 +0,0 @@
#include "fling.h"
#include <string.h>
void init_msg(struct msghdr *msg,
struct iovec *iov,
char *buf,
size_t buf_len) {
iov->iov_base = buf;
iov->iov_len = 1;
msg->msg_iov = iov;
msg->msg_iovlen = 1;
msg->msg_control = buf;
msg->msg_controllen = buf_len;
msg->msg_name = NULL;
msg->msg_namelen = 0;
}
int send_fd(int conn, int fd) {
struct msghdr msg;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(int))];
memset(&buf, 0, CMSG_SPACE(sizeof(int)));
init_msg(&msg, &iov, buf, sizeof(buf));
struct cmsghdr *header = CMSG_FIRSTHDR(&msg);
header->cmsg_level = SOL_SOCKET;
header->cmsg_type = SCM_RIGHTS;
header->cmsg_len = CMSG_LEN(sizeof(int));
*(int *) CMSG_DATA(header) = fd;
/* Send file descriptor. */
return sendmsg(conn, &msg, 0);
}
int recv_fd(int conn) {
struct msghdr msg;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(int))];
init_msg(&msg, &iov, buf, sizeof(buf));
if (recvmsg(conn, &msg, 0) == -1)
return -1;
int found_fd = -1;
int oh_noes = 0;
for (struct cmsghdr *header = CMSG_FIRSTHDR(&msg); header != NULL;
header = CMSG_NXTHDR(&msg, header))
if (header->cmsg_level == SOL_SOCKET && header->cmsg_type == SCM_RIGHTS) {
int count =
(header->cmsg_len - (CMSG_DATA(header) - (unsigned char *) header)) /
sizeof(int);
for (int i = 0; i < count; ++i) {
int fd = ((int *) CMSG_DATA(header))[i];
if (found_fd == -1) {
found_fd = fd;
} else {
close(fd);
oh_noes = 1;
}
}
}
/* The sender sent us more than one file descriptor. We've closed
* them all to prevent fd leaks but notify the caller that we got
* a bad message. */
if (oh_noes) {
close(found_fd);
errno = EBADMSG;
return -1;
}
return found_fd;
}
-43
View File
@@ -1,43 +0,0 @@
/* FLING: Exchanging file descriptors over sockets
*
* This is a little library for sending file descriptors over a socket
* between processes. The reason for doing that (as opposed to using
* filenames to share the files) is so (a) no files remain in the
* filesystem after all the processes terminate, (b) to make sure that
* there are no name collisions and (c) to be able to control who has
* access to the data.
*
* Most of the code is from https://github.com/sharvil/flingfd */
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
/* This is neccessary for Mac OS X, see http://www.apuebook.com/faqs2e.html
* (10). */
#if !defined(CMSG_SPACE) && !defined(CMSG_LEN)
#define CMSG_SPACE(len) \
(__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + __DARWIN_ALIGN32(len))
#define CMSG_LEN(len) (__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + (len))
#endif
void init_msg(struct msghdr *msg, struct iovec *iov, char *buf, size_t buf_len);
/**
* Send a file descriptor over a unix domain socket.
*
* @param conn Unix domain socket to send the file descriptor over.
* @param fd File descriptor to send over.
* @return Status code which is < 0 on failure.
*/
int send_fd(int conn, int fd);
/**
* Receive a file descriptor over a unix domain socket.
*
* @param conn Unix domain socket to receive the file descriptor from.
* @return File descriptor or a value < 0 on failure.
*/
int recv_fd(int conn);
-147
View File
@@ -1,147 +0,0 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef ARROW_UTIL_LOGGING_H
#define ARROW_UTIL_LOGGING_H
#include <cstdlib>
#include <iostream>
namespace arrow {
// 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 ARROW_DEBUG (-1)
#define ARROW_INFO 0
#define ARROW_WARNING 1
#define ARROW_ERROR 2
#define ARROW_FATAL 3
#define ARROW_LOG_INTERNAL(level) ::arrow::internal::CerrLog(level)
#define ARROW_LOG(level) ARROW_LOG_INTERNAL(ARROW_##level)
#define ARROW_CHECK(condition) \
(condition) ? 0 : ::arrow::internal::FatalLog(ARROW_FATAL) \
<< __FILE__ << __LINE__ \
<< " Check failed: " #condition " "
#ifdef NDEBUG
#define ARROW_DFATAL ARROW_WARNING
#define DCHECK(condition) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_EQ(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_NE(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_LE(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_LT(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_GE(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#define DCHECK_GT(val1, val2) \
while (false) \
::arrow::internal::NullLog()
#else
#define ARROW_DFATAL ARROW_FATAL
#define DCHECK(condition) ARROW_CHECK(condition)
#define DCHECK_EQ(val1, val2) ARROW_CHECK((val1) == (val2))
#define DCHECK_NE(val1, val2) ARROW_CHECK((val1) != (val2))
#define DCHECK_LE(val1, val2) ARROW_CHECK((val1) <= (val2))
#define DCHECK_LT(val1, val2) ARROW_CHECK((val1) < (val2))
#define DCHECK_GE(val1, val2) ARROW_CHECK((val1) >= (val2))
#define DCHECK_GT(val1, val2) ARROW_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_ == ARROW_FATAL) {
std::exit(1);
}
}
template <class T>
CerrLog &operator<<(const T &t) {
// TODO(pcm): Print this if in debug mode, but not if in valgrind
// mode
if (severity_ == ARROW_DEBUG) {
return *this;
}
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(ARROW_FATAL){} // NOLINT
[[noreturn]] ~FatalLog() {
if (has_logged_) {
std::cerr << std::endl;
}
std::exit(1);
}
};
} // namespace internal
} // namespace arrow
#endif // ARROW_UTIL_LOGGING_H
-168
View File
@@ -1,168 +0,0 @@
#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <unordered_map>
#include "common.h"
extern "C" {
void *fake_mmap(size_t);
int fake_munmap(void *, size_t);
#define MMAP(s) fake_mmap(s)
#define MUNMAP(a, s) fake_munmap(a, s)
#define DIRECT_MMAP(s) fake_mmap(s)
#define DIRECT_MUNMAP(a, s) fake_munmap(a, s)
#define USE_DL_PREFIX
#define HAVE_MORECORE 0
#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
#define DEFAULT_GRANULARITY ((size_t) 128U * 1024U)
#include "thirdparty/dlmalloc.c"
#undef MMAP
#undef MUNMAP
#undef DIRECT_MMAP
#undef DIRECT_MUNMAP
#undef USE_DL_PREFIX
#undef HAVE_MORECORE
#undef DEFAULT_GRANULARITY
}
struct mmap_record {
int fd;
int64_t size;
};
namespace {
/** Hashtable that contains one entry per segment that we got from the OS
* via mmap. Associates the address of that segment with its file descriptor
* and size. */
std::unordered_map<void *, mmap_record> mmap_records;
} /* namespace */
constexpr int GRANULARITY_MULTIPLIER = 2;
static void *pointer_advance(void *p, ptrdiff_t n) {
return (unsigned char *) p + n;
}
static void *pointer_retreat(void *p, ptrdiff_t n) {
return (unsigned char *) p - n;
}
static ptrdiff_t pointer_distance(void const *pfrom, void const *pto) {
return (unsigned char const *) pto - (unsigned char const *) pfrom;
}
/* Create a buffer. This is creating a temporary file and then
* immediately unlinking it so we do not leave traces in the system. */
int create_buffer(int64_t size) {
int fd;
#ifdef _WIN32
if (!CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
(DWORD)((uint64_t) size >> (CHAR_BIT * sizeof(DWORD))),
(DWORD)(uint64_t) size, NULL)) {
fd = -1;
}
#else
#ifdef __linux__
constexpr char file_template[] = "/dev/shm/plasmaXXXXXX";
#else
constexpr char file_template[] = "/tmp/plasmaXXXXXX";
#endif
char file_name[32];
strncpy(file_name, file_template, 32);
fd = mkstemp(file_name);
if (fd < 0)
return -1;
FILE *file = fdopen(fd, "a+");
if (!file) {
close(fd);
return -1;
}
if (unlink(file_name) != 0) {
LOG_ERROR("unlink error");
return -1;
}
if (ftruncate(fd, (off_t) size) != 0) {
LOG_ERROR("ftruncate error");
return -1;
}
#endif
return fd;
}
void *fake_mmap(size_t size) {
/* Add sizeof(size_t) so that the returned pointer is deliberately not
* page-aligned. This ensures that the segments of memory returned by
* fake_mmap are never contiguous. */
size += sizeof(size_t);
int fd = create_buffer(size);
CHECKM(fd >= 0, "Failed to create buffer during mmap");
void *pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pointer == MAP_FAILED) {
return pointer;
}
/* Increase dlmalloc's allocation granularity directly. */
mparams.granularity *= GRANULARITY_MULTIPLIER;
mmap_record &record = mmap_records[pointer];
record.fd = fd;
record.size = size;
/* We lie to dlmalloc about where mapped memory actually lives. */
pointer = pointer_advance(pointer, sizeof(size_t));
LOG_DEBUG("%p = fake_mmap(%lu)", pointer, size);
return pointer;
}
int fake_munmap(void *addr, size_t size) {
LOG_DEBUG("fake_munmap(%p, %lu)", addr, size);
addr = pointer_retreat(addr, sizeof(size_t));
size += sizeof(size_t);
auto entry = mmap_records.find(addr);
if (entry == mmap_records.end() || entry->second.size != size) {
/* Reject requests to munmap that don't directly match previous
* calls to mmap, to prevent dlmalloc from trimming. */
return -1;
}
int r = munmap(addr, size);
if (r == 0) {
close(entry->second.fd);
}
mmap_records.erase(entry);
return r;
}
void get_malloc_mapinfo(void *addr,
int *fd,
int64_t *map_size,
ptrdiff_t *offset) {
/* TODO(rshin): Implement a more efficient search through mmap_records. */
for (const auto &entry : mmap_records) {
if (addr >= entry.first &&
addr < pointer_advance(entry.first, entry.second.size)) {
*fd = entry.second.fd;
*map_size = entry.second.size;
*offset = pointer_distance(entry.first, addr);
return;
}
}
*fd = -1;
*map_size = 0;
*offset = 0;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef MALLOC_H
#define MALLOC_H
void get_malloc_mapinfo(void *addr,
int *fd,
int64_t *map_length,
ptrdiff_t *offset);
#endif /* MALLOC_H */
-53
View File
@@ -1,53 +0,0 @@
#include "plasma_common.h"
#include "plasma.h"
#include "io.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "plasma_protocol.h"
int warn_if_sigpipe(int status, int client_sock) {
if (status >= 0) {
return 0;
}
if (errno == EPIPE || errno == EBADF || errno == ECONNRESET) {
ARROW_LOG(WARNING)
<< "Received SIGPIPE, BAD FILE DESCRIPTOR, or ECONNRESET when "
"sending a message to client on fd "
<< client_sock << ". The client on the other end may "
"have hung up.";
return errno;
}
ARROW_LOG(FATAL) << "Failed to write message to client on fd " << client_sock
<< ".";
}
/**
* This will create a new ObjectInfo buffer. The first sizeof(int64_t) bytes
* of this buffer are the length of the remaining message and the
* remaining message is a serialized version of the object info.
*
* @param object_info The object info to be serialized
* @return The object info buffer. It is the caller's responsibility to free
* this buffer with "free" after it has been used.
*/
uint8_t *create_object_info_buffer(ObjectInfoT *object_info) {
flatbuffers::FlatBufferBuilder fbb;
auto message = CreateObjectInfo(fbb, object_info);
fbb.Finish(message);
uint8_t *notification = (uint8_t *) malloc(sizeof(int64_t) + fbb.GetSize());
*((int64_t *) notification) = fbb.GetSize();
memcpy(notification + sizeof(int64_t), fbb.GetBufferPointer(), fbb.GetSize());
return notification;
}
ObjectTableEntry *get_object_table_entry(PlasmaStoreInfo *store_info,
ObjectID object_id) {
auto it = store_info->objects.find(object_id);
if (it == store_info->objects.end()) {
return NULL;
}
return it->second.get();
}
-186
View File
@@ -1,186 +0,0 @@
#ifndef PLASMA_H
#define PLASMA_H
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h> /* pid_t */
extern "C" {
#include "sha256.h"
}
#include <unordered_map>
#include <unordered_set>
#include "format/common_generated.h"
#include "logging.h"
#include "status.h"
#include <inttypes.h>
#define HANDLE_SIGPIPE(s, fd_) \
do { \
Status _s = (s); \
if (!_s.ok()) { \
if (errno == EPIPE || errno == EBADF || errno == ECONNRESET) { \
ARROW_LOG(WARNING) \
<< "Received SIGPIPE, BAD FILE DESCRIPTOR, or ECONNRESET when " \
"sending a message to client on fd " \
<< fd_ << ". " \
"The client on the other end may have hung up."; \
} else { \
return _s; \
} \
} \
} while (0);
/** Allocation granularity used in plasma for object allocation. */
#define BLOCK_SIZE 64
// Size of object hash digests.
constexpr int64_t kDigestSize = SHA256_BLOCK_SIZE;
struct Client;
/**
* Object request data structure. Used in the plasma_wait_for_objects()
* argument.
*/
typedef struct {
/** The ID of the requested object. If ID_NIL request any object. */
ObjectID object_id;
/** Request associated to the object. It can take one of the following values:
* - PLASMA_QUERY_LOCAL: return if or when the object is available in the
* local Plasma Store.
* - PLASMA_QUERY_ANYWHERE: return if or when the object is available in
* the system (i.e., either in the local or a remote Plasma Store). */
int type;
/** Object status. Same as the status returned by plasma_status() function
* call. This is filled in by plasma_wait_for_objects1():
* - ObjectStatus_Local: object is ready at the local Plasma Store.
* - ObjectStatus_Remote: object is ready at a remote Plasma Store.
* - ObjectStatus_Nonexistent: object does not exist in the system.
* - PLASMA_CLIENT_IN_TRANSFER, if the object is currently being scheduled
* for being transferred or it is transferring. */
int status;
} ObjectRequest;
/** Mapping from object IDs to type and status of the request. */
typedef std::unordered_map<ObjectID, ObjectRequest, UniqueIDHasher>
ObjectRequestMap;
/* Handle to access memory mapped file and map it into client address space. */
typedef struct {
/** The file descriptor of the memory mapped file in the store. It is used as
* a unique identifier of the file in the client to look up the corresponding
* file descriptor on the client's side. */
int store_fd;
/** The size in bytes of the memory mapped file. */
int64_t mmap_size;
} object_handle;
typedef struct {
/** Handle for memory mapped file the object is stored in. */
object_handle handle;
/** The offset in bytes in the memory mapped file of the data. */
ptrdiff_t data_offset;
/** The offset in bytes in the memory mapped file of the metadata. */
ptrdiff_t metadata_offset;
/** The size in bytes of the data. */
int64_t data_size;
/** The size in bytes of the metadata. */
int64_t metadata_size;
} PlasmaObject;
typedef enum {
/** Object was created but not sealed in the local Plasma Store. */
PLASMA_CREATED = 1,
/** Object is sealed and stored in the local Plasma Store. */
PLASMA_SEALED
} object_state;
typedef enum {
/** The object was not found. */
OBJECT_NOT_FOUND = 0,
/** The object was found. */
OBJECT_FOUND = 1
} object_status;
typedef enum {
/** Query for object in the local plasma store. */
PLASMA_QUERY_LOCAL = 1,
/** Query for object in the local plasma store or in a remote plasma store. */
PLASMA_QUERY_ANYWHERE
} object_request_type;
/** This type is used by the Plasma store. It is here because it is exposed to
* the eviction policy. */
struct ObjectTableEntry {
/** Object id of this object. */
ObjectID object_id;
/** Object info like size, creation time and owner. */
ObjectInfoT info;
/** Memory mapped file containing the object. */
int fd;
/** Size of the underlying map. */
int64_t map_size;
/** Offset from the base of the mmap. */
ptrdiff_t offset;
/** Pointer to the object data. Needed to free the object. */
uint8_t *pointer;
/** Set of clients currently using this object. */
std::unordered_set<Client *> clients;
/** The state of the object, e.g., whether it is open or sealed. */
object_state state;
/** The digest of the object. Used to see if two objects are the same. */
unsigned char digest[kDigestSize];
};
/** The plasma store information that is exposed to the eviction policy. */
struct PlasmaStoreInfo {
/** Objects that are in the Plasma store. */
std::unordered_map<ObjectID,
std::unique_ptr<ObjectTableEntry>,
UniqueIDHasher>
objects;
/** The amount of memory (in bytes) that we allow to be allocated in the
* store. */
int64_t memory_capacity;
};
/**
* Get an entry from the object table and return NULL if the object_id
* is not present.
*
* @param store_info The PlasmaStoreInfo that contains the object table.
* @param object_id The object_id of the entry we are looking for.
* @return The entry associated with the object_id or NULL if the object_id
* is not present.
*/
ObjectTableEntry *get_object_table_entry(PlasmaStoreInfo *store_info,
ObjectID object_id);
/**
* Print a warning if the status is less than zero. This should be used to check
* the success of messages sent to plasma clients. We print a warning instead of
* failing because the plasma clients are allowed to die. This is used to handle
* situations where the store writes to a client file descriptor, and the client
* may already have disconnected. If we have processed the disconnection and
* closed the file descriptor, we should get a BAD FILE DESCRIPTOR error. If we
* have not, then we should get a SIGPIPE. If we write to a TCP socket that
* isn't connected yet, then we should get an ECONNRESET.
*
* @param status The status to check. If it is less less than zero, we will
* print a warning.
* @param client_sock The client socket. This is just used to print some extra
* information.
* @return The errno set.
*/
int warn_if_sigpipe(int status, int client_sock);
uint8_t *create_object_info_buffer(ObjectInfoT *object_info);
#endif /* PLASMA_H */
-624
View File
@@ -1,624 +0,0 @@
// PLASMA CLIENT: Client library for using the plasma store and manager
#ifdef _WIN32
#include <Win32_Interop/win32_types.h>
#endif
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <netinet/in.h>
#include "plasma_common.h"
#include "plasma.h"
#include "plasma_io.h"
#include "plasma_protocol.h"
#include "plasma_client.h"
#include <vector>
#include <thread>
extern "C" {
#include "sha256.h"
#include "fling.h"
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h"
#define XXH64_DEFAULT_SEED 0
}
// Number of threads used for memcopy and hash computations.
constexpr int64_t kThreadPoolSize = 8;
constexpr int64_t kBytesInMB = 1 << 20;
static std::vector<std::thread> threadpool_(kThreadPoolSize);
struct ClientMmapTableEntry {
/// The result of mmap for this file descriptor.
uint8_t *pointer;
/// The length of the memory-mapped file.
size_t length;
/// The number of objects in this memory-mapped file that are currently being
/// used by the client. When this count reaches zeros, we unmap the file.
int count;
};
struct ObjectInUseEntry {
/// A count of the number of times this client has called PlasmaClient::Create
/// or
/// PlasmaClient::Get on this object ID minus the number of calls to
/// PlasmaClient::Release.
/// When this count reaches zero, we remove the entry from the ObjectsInUse
/// and decrement a count in the relevant ClientMmapTableEntry.
int count;
/// Cached information to read the object.
PlasmaObject object;
/// A flag representing whether the object has been sealed.
bool is_sealed;
};
// If the file descriptor fd has been mmapped in this client process before,
// return the pointer that was returned by mmap, otherwise mmap it and store the
// pointer in a hash table.
uint8_t *lookup_or_mmap(PlasmaClient *conn,
int fd,
int store_fd_val,
int64_t map_size) {
auto entry = conn->mmap_table.find(store_fd_val);
if (entry != conn->mmap_table.end()) {
close(fd);
return entry->second->pointer;
} else {
uint8_t *result = (uint8_t *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (result == MAP_FAILED) {
ARROW_LOG(FATAL) << "mmap failed";
}
close(fd);
ClientMmapTableEntry *entry = new ClientMmapTableEntry();
entry->pointer = result;
entry->length = map_size;
entry->count = 0;
conn->mmap_table[store_fd_val] = entry;
return result;
}
}
// Get a pointer to a file that we know has been memory mapped in this client
// process before.
uint8_t *lookup_mmapped_file(PlasmaClient *conn, int store_fd_val) {
auto entry = conn->mmap_table.find(store_fd_val);
ARROW_CHECK(entry != conn->mmap_table.end());
return entry->second->pointer;
}
void increment_object_count(PlasmaClient *conn,
ObjectID object_id,
PlasmaObject *object,
bool is_sealed) {
// Increment the count of the object to track the fact that it is being used.
// The corresponding decrement should happen in PlasmaClient::Release.
auto elem = conn->objects_in_use.find(object_id);
ObjectInUseEntry *object_entry;
if (elem == conn->objects_in_use.end()) {
// Add this object ID to the hash table of object IDs in use. The
// corresponding call to free happens in PlasmaClient::Release.
object_entry = new ObjectInUseEntry();
object_entry->object = *object;
object_entry->count = 0;
object_entry->is_sealed = is_sealed;
conn->objects_in_use[object_id] = object_entry;
// Increment the count of the number of objects in the memory-mapped file
// that are being used. The corresponding decrement should happen in
// PlasmaClient::Release.
auto entry = conn->mmap_table.find(object->handle.store_fd);
ARROW_CHECK(entry != conn->mmap_table.end());
ARROW_CHECK(entry->second->count >= 0);
// Update the in_use_object_bytes.
conn->in_use_object_bytes +=
(object_entry->object.data_size + object_entry->object.metadata_size);
entry->second->count += 1;
} else {
object_entry = elem->second;
ARROW_CHECK(object_entry->count > 0);
}
// Increment the count of the number of instances of this object that are
// being used by this client. The corresponding decrement should happen in
// PlasmaClient::Release.
object_entry->count += 1;
}
Status PlasmaClient::Create(ObjectID object_id,
int64_t data_size,
uint8_t *metadata,
int64_t metadata_size,
uint8_t **data) {
ARROW_LOG(DEBUG) << "called plasma_create on conn " << store_conn
<< " with size " << data_size << " and metadata size "
<< metadata_size;
RETURN_NOT_OK(
SendCreateRequest(store_conn, object_id, data_size, metadata_size));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(
PlasmaReceive(store_conn, MessageType_PlasmaCreateReply, buffer));
ObjectID id;
PlasmaObject object;
RETURN_NOT_OK(ReadCreateReply(buffer.data(), &id, &object));
// If the CreateReply included an error, then the store will not send a file
// descriptor.
int fd = recv_fd(store_conn);
ARROW_CHECK(fd >= 0) << "recv not successful";
ARROW_CHECK(object.data_size == data_size);
ARROW_CHECK(object.metadata_size == metadata_size);
// The metadata should come right after the data.
ARROW_CHECK(object.metadata_offset == object.data_offset + data_size);
*data = lookup_or_mmap(this, fd, object.handle.store_fd,
object.handle.mmap_size) +
object.data_offset;
// If plasma_create is being called from a transfer, then we will not copy the
// metadata here. The metadata will be written along with the data streamed
// from the transfer.
if (metadata != NULL) {
// Copy the metadata to the buffer.
memcpy(*data + object.data_size, metadata, metadata_size);
}
// Increment the count of the number of instances of this object that this
// client is using. A call to PlasmaClient::Release is required to decrement
// this
// count. Cache the reference to the object.
increment_object_count(this, object_id, &object, false);
// We increment the count a second time (and the corresponding decrement will
// happen in a PlasmaClient::Release call in plasma_seal) so even if the
// buffer
// returned by PlasmaClient::Dreate goes out of scope, the object does not get
// released before the call to PlasmaClient::Seal happens.
increment_object_count(this, object_id, &object, false);
return Status::OK();
}
Status PlasmaClient::Get(ObjectID object_ids[],
int64_t num_objects,
int64_t timeout_ms,
ObjectBuffer object_buffers[]) {
// Fill out the info for the objects that are already in use locally.
bool all_present = true;
for (int i = 0; i < num_objects; ++i) {
auto object_entry = objects_in_use.find(object_ids[i]);
if (object_entry == objects_in_use.end()) {
// This object is not currently in use by this client, so we need to send
// a request to the store.
all_present = false;
// Make a note to ourselves that the object is not present.
object_buffers[i].data_size = -1;
} else {
// NOTE: If the object is still unsealed, we will deadlock, since we must
// have been the one who created it.
ARROW_CHECK(object_entry->second->is_sealed)
<< "Plasma client called get on an unsealed object that it created";
PlasmaObject *object = &object_entry->second->object;
object_buffers[i].data =
lookup_mmapped_file(this, object->handle.store_fd);
object_buffers[i].data = object_buffers[i].data + object->data_offset;
object_buffers[i].data_size = object->data_size;
object_buffers[i].metadata = object_buffers[i].data + object->data_size;
object_buffers[i].metadata_size = object->metadata_size;
// Increment the count of the number of instances of this object that this
// client is using. A call to PlasmaClient::Release is required to
// decrement this
// count. Cache the reference to the object.
increment_object_count(this, object_ids[i], object, true);
}
}
if (all_present) {
return Status::OK();
}
// If we get here, then the objects aren't all currently in use by this
// client, so we need to send a request to the plasma store.
RETURN_NOT_OK(
SendGetRequest(store_conn, object_ids, num_objects, timeout_ms));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(PlasmaReceive(store_conn, MessageType_PlasmaGetReply, buffer));
std::vector<ObjectID> received_object_ids(num_objects);
std::vector<PlasmaObject> object_data(num_objects);
PlasmaObject *object;
RETURN_NOT_OK(ReadGetReply(buffer.data(), received_object_ids.data(),
object_data.data(), num_objects));
for (int i = 0; i < num_objects; ++i) {
DCHECK(received_object_ids[i] == object_ids[i]);
object = &object_data[i];
if (object_buffers[i].data_size != -1) {
// If the object was already in use by the client, then the store should
// have returned it.
DCHECK(object->data_size != -1);
// We won't use this file descriptor, but the store sent us one, so we
// need to receive it and then close it right away so we don't leak file
// descriptors.
int fd = recv_fd(store_conn);
close(fd);
ARROW_CHECK(fd >= 0);
// We've already filled out the information for this object, so we can
// just continue.
continue;
}
// If we are here, the object was not currently in use, so we need to
// process the reply from the object store.
if (object->data_size != -1) {
// The object was retrieved. The user will be responsible for releasing
// this object.
int fd = recv_fd(store_conn);
ARROW_CHECK(fd >= 0);
object_buffers[i].data = lookup_or_mmap(this, fd, object->handle.store_fd,
object->handle.mmap_size);
// Finish filling out the return values.
object_buffers[i].data = object_buffers[i].data + object->data_offset;
object_buffers[i].data_size = object->data_size;
object_buffers[i].metadata = object_buffers[i].data + object->data_size;
object_buffers[i].metadata_size = object->metadata_size;
// Increment the count of the number of instances of this object that this
// client is using. A call to PlasmaClient::Release is required to
// decrement this
// count. Cache the reference to the object.
increment_object_count(this, received_object_ids[i], object, true);
} else {
// The object was not retrieved. Make sure we already put a -1 here to
// indicate that the object was not retrieved. The caller is not
// responsible for releasing this object.
DCHECK(object_buffers[i].data_size == -1);
object_buffers[i].data_size = -1;
}
}
return Status::OK();
}
/// This is a helper method for implementing plasma_release. We maintain a
/// buffer
/// of release calls and only perform them once the buffer becomes full (as
/// judged by the aggregate sizes of the objects). There may be multiple release
/// calls for the same object ID in the buffer. In this case, the first release
/// calls will not do anything. The client will only send a message to the store
/// releasing the object when the client is truly done with the object.
///
/// @param conn The plasma connection.
/// @param object_id The object ID to attempt to release.
Status PlasmaClient::PerformRelease(ObjectID object_id) {
// Decrement the count of the number of instances of this object that are
// being used by this client. The corresponding increment should have happened
// in PlasmaClient::Get.
auto object_entry = objects_in_use.find(object_id);
ARROW_CHECK(object_entry != objects_in_use.end());
object_entry->second->count -= 1;
ARROW_CHECK(object_entry->second->count >= 0);
// Check if the client is no longer using this object.
if (object_entry->second->count == 0) {
// Decrement the count of the number of objects in this memory-mapped file
// that the client is using. The corresponding increment should have
// happened in plasma_get.
int fd = object_entry->second->object.handle.store_fd;
auto entry = mmap_table.find(fd);
ARROW_CHECK(entry != mmap_table.end());
entry->second->count -= 1;
ARROW_CHECK(entry->second->count >= 0);
// If none are being used then unmap the file.
if (entry->second->count == 0) {
munmap(entry->second->pointer, entry->second->length);
// Remove the corresponding entry from the hash table.
delete entry->second;
mmap_table.erase(fd);
}
// Tell the store that the client no longer needs the object.
RETURN_NOT_OK(SendReleaseRequest(store_conn, object_id));
// Update the in_use_object_bytes.
in_use_object_bytes -= (object_entry->second->object.data_size +
object_entry->second->object.metadata_size);
DCHECK(in_use_object_bytes >= 0);
// Remove the entry from the hash table of objects currently in use.
delete object_entry->second;
objects_in_use.erase(object_id);
}
return Status::OK();
}
Status PlasmaClient::Release(ObjectID object_id) {
// Add the new object to the release history.
release_history.push_front(object_id);
// If there are too many bytes in use by the client or if there are too many
// pending release calls, and there are at least some pending release calls in
// the release_history list, then release some objects.
while ((in_use_object_bytes >
std::min(kL3CacheSizeBytes, store_capacity / 100) ||
release_history.size() > config.release_delay) &&
release_history.size() > 0) {
// Perform a release for the object ID for the first pending release.
RETURN_NOT_OK(PerformRelease(release_history.back()));
// Remove the last entry from the release history.
release_history.pop_back();
}
return Status::OK();
}
// This method is used to query whether the plasma store contains an object.
Status PlasmaClient::Contains(ObjectID object_id, int *has_object) {
// Check if we already have a reference to the object.
if (objects_in_use.count(object_id) > 0) {
*has_object = 1;
} else {
// If we don't already have a reference to the object, check with the store
// to see if we have the object.
RETURN_NOT_OK(SendContainsRequest(store_conn, object_id));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(
PlasmaReceive(store_conn, MessageType_PlasmaContainsReply, buffer));
ObjectID object_id2;
RETURN_NOT_OK(ReadContainsReply(buffer.data(), &object_id2, has_object));
}
return Status::OK();
}
static void compute_block_hash(const unsigned char *data,
int64_t nbytes,
uint64_t *hash) {
XXH64_state_t hash_state;
XXH64_reset(&hash_state, XXH64_DEFAULT_SEED);
XXH64_update(&hash_state, data, nbytes);
*hash = XXH64_digest(&hash_state);
}
static inline bool compute_object_hash_parallel(XXH64_state_t *hash_state,
const unsigned char *data,
int64_t nbytes) {
// Note that this function will likely be faster if the address of data is
// aligned on a 64-byte boundary.
const uint64_t num_threads = kThreadPoolSize;
uint64_t threadhash[num_threads + 1];
const uint64_t data_address = reinterpret_cast<uint64_t>(data);
const uint64_t num_blocks = nbytes / BLOCK_SIZE;
const uint64_t chunk_size = (num_blocks / num_threads) * BLOCK_SIZE;
const uint64_t right_address = data_address + chunk_size * num_threads;
const uint64_t suffix = (data_address + nbytes) - right_address;
// Now the data layout is | k * num_threads * block_size | suffix | ==
// | num_threads * chunk_size | suffix |, where chunk_size = k * block_size.
// Each thread gets a "chunk" of k blocks, except the suffix thread.
for (int i = 0; i < num_threads; i++) {
threadpool_[i] =
std::thread(compute_block_hash,
reinterpret_cast<uint8_t *>(data_address) + i * chunk_size,
chunk_size, &threadhash[i]);
}
compute_block_hash(reinterpret_cast<uint8_t *>(right_address), suffix,
&threadhash[num_threads]);
// Join the threads.
for (auto &t : threadpool_) {
if (t.joinable()) {
t.join();
}
}
XXH64_update(hash_state, (unsigned char *) threadhash, sizeof(threadhash));
return true;
}
static uint64_t compute_object_hash(const ObjectBuffer &obj_buffer) {
XXH64_state_t hash_state;
XXH64_reset(&hash_state, XXH64_DEFAULT_SEED);
if (obj_buffer.data_size >= kBytesInMB) {
compute_object_hash_parallel(&hash_state, (unsigned char *) obj_buffer.data,
obj_buffer.data_size);
} else {
XXH64_update(&hash_state, (unsigned char *) obj_buffer.data,
obj_buffer.data_size);
}
XXH64_update(&hash_state, (unsigned char *) obj_buffer.metadata,
obj_buffer.metadata_size);
return XXH64_digest(&hash_state);
}
bool plasma_compute_object_hash(PlasmaClient *conn,
ObjectID obj_id,
unsigned char *digest) {
// Get the plasma object data. We pass in a timeout of 0 to indicate that
// the operation should timeout immediately.
ObjectBuffer obj_buffer;
ObjectID obj_id_array[1] = {obj_id};
uint64_t hash;
ARROW_CHECK_OK(conn->Get(obj_id_array, 1, 0, &obj_buffer));
// If the object was not retrieved, return false.
if (obj_buffer.data_size == -1) {
return false;
}
// Compute the hash.
hash = compute_object_hash(obj_buffer);
memcpy(digest, &hash, sizeof(hash));
// Release the plasma object.
ARROW_CHECK_OK(conn->Release(obj_id));
return true;
}
Status PlasmaClient::Seal(ObjectID object_id) {
// Make sure this client has a reference to the object before sending the
// request to Plasma.
auto object_entry = objects_in_use.find(object_id);
ARROW_CHECK(object_entry != objects_in_use.end())
<< "Plasma client called seal an object without a reference to it";
ARROW_CHECK(!object_entry->second->is_sealed)
<< "Plasma client called seal an already sealed object";
object_entry->second->is_sealed = true;
/// Send the seal request to Plasma.
static unsigned char digest[kDigestSize];
ARROW_CHECK(plasma_compute_object_hash(this, object_id, &digest[0]));
RETURN_NOT_OK(SendSealRequest(store_conn, object_id, &digest[0]));
// We call PlasmaClient::Release to decrement the number of instances of this
// object
// that are currently being used by this client. The corresponding increment
// happened in plasma_create and was used to ensure that the object was not
// released before the call to PlasmaClient::Seal.
return Release(object_id);
}
Status PlasmaClient::Delete(ObjectID object_id) {
// TODO(rkn): In the future, we can use this method to give hints to the
// eviction policy about when an object will no longer be needed.
return Status::NotImplemented("PlasmaClient::Delete is not implemented.");
}
Status PlasmaClient::Evict(int64_t num_bytes, int64_t &num_bytes_evicted) {
// Send a request to the store to evict objects.
RETURN_NOT_OK(SendEvictRequest(store_conn, num_bytes));
// Wait for a response with the number of bytes actually evicted.
std::vector<uint8_t> buffer;
int64_t type;
RETURN_NOT_OK(ReadMessage(store_conn, &type, buffer));
return ReadEvictReply(buffer.data(), num_bytes_evicted);
}
Status PlasmaClient::Subscribe(int &fd) {
int sock[2];
// Create a non-blocking socket pair. This will only be used to send
// notifications from the Plasma store to the client.
socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
// Make the socket non-blocking.
int flags = fcntl(sock[1], F_GETFL, 0);
ARROW_CHECK(fcntl(sock[1], F_SETFL, flags | O_NONBLOCK) == 0);
// Tell the Plasma store about the subscription.
RETURN_NOT_OK(SendSubscribeRequest(store_conn));
// Send the file descriptor that the Plasma store should use to push
// notifications about sealed objects to this client.
ARROW_CHECK(send_fd(store_conn, sock[1]) >= 0);
close(sock[1]);
// Return the file descriptor that the client should use to read notifications
// about sealed objects.
fd = sock[0];
return Status::OK();
}
Status PlasmaClient::Connect(const std::string &store_socket_name,
const std::string &manager_socket_name,
int release_delay) {
store_conn = connect_ipc_sock_retry(store_socket_name, -1, -1);
if (manager_socket_name != "") {
manager_conn = connect_ipc_sock_retry(manager_socket_name, -1, -1);
} else {
manager_conn = -1;
}
config.release_delay = release_delay;
in_use_object_bytes = 0;
// Send a ConnectRequest to the store to get its memory capacity.
RETURN_NOT_OK(SendConnectRequest(store_conn));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(
PlasmaReceive(store_conn, MessageType_PlasmaConnectReply, buffer));
RETURN_NOT_OK(ReadConnectReply(buffer.data(), &store_capacity));
return Status::OK();
}
Status PlasmaClient::Disconnect() {
// NOTE: We purposefully do not finish sending release calls for objects in
// use, so that we don't duplicate PlasmaClient::Release calls (when handling
// a
// SIGTERM, for example).
for (auto &entry : objects_in_use) {
delete entry.second;
}
for (auto &entry : mmap_table) {
delete entry.second;
}
// Close the connections to Plasma. The Plasma store will release the objects
// that were in use by us when handling the SIGPIPE.
close(store_conn);
if (manager_conn >= 0) {
close(manager_conn);
}
return Status::OK();
}
bool plasma_manager_is_connected(PlasmaClient *conn) {
return conn->manager_conn >= 0;
}
#define h_addr h_addr_list[0]
Status PlasmaClient::Transfer(const char *address,
int port,
ObjectID object_id) {
return SendDataRequest(manager_conn, object_id, address, port);
}
Status PlasmaClient::Fetch(int num_object_ids, ObjectID object_ids[]) {
ARROW_CHECK(manager_conn >= 0);
return SendFetchRequest(manager_conn, object_ids, num_object_ids);
}
int get_manager_fd(PlasmaClient *conn) {
return conn->manager_conn;
}
Status PlasmaClient::Info(ObjectID object_id, int *object_status) {
ARROW_CHECK(manager_conn >= 0);
RETURN_NOT_OK(SendStatusRequest(manager_conn, &object_id, 1));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(
PlasmaReceive(manager_conn, MessageType_PlasmaStatusReply, buffer));
return ReadStatusReply(buffer.data(), &object_id, object_status, 1);
}
Status PlasmaClient::Wait(int num_object_requests,
ObjectRequest object_requests[],
int num_ready_objects,
uint64_t timeout_ms,
int &num_objects_ready) {
ARROW_CHECK(manager_conn >= 0);
ARROW_CHECK(num_object_requests > 0);
ARROW_CHECK(num_ready_objects > 0);
ARROW_CHECK(num_ready_objects <= num_object_requests);
for (int i = 0; i < num_object_requests; ++i) {
ARROW_CHECK(object_requests[i].type == PLASMA_QUERY_LOCAL ||
object_requests[i].type == PLASMA_QUERY_ANYWHERE);
}
RETURN_NOT_OK(SendWaitRequest(manager_conn, object_requests,
num_object_requests, num_ready_objects,
timeout_ms));
std::vector<uint8_t> buffer;
RETURN_NOT_OK(
PlasmaReceive(manager_conn, MessageType_PlasmaWaitReply, buffer));
RETURN_NOT_OK(
ReadWaitReply(buffer.data(), object_requests, &num_ready_objects));
num_objects_ready = 0;
for (int i = 0; i < num_object_requests; ++i) {
int type = object_requests[i].type;
int status = object_requests[i].status;
switch (type) {
case PLASMA_QUERY_LOCAL:
if (status == ObjectStatus_Local) {
num_objects_ready += 1;
}
break;
case PLASMA_QUERY_ANYWHERE:
if (status == ObjectStatus_Local || status == ObjectStatus_Remote) {
num_objects_ready += 1;
} else {
ARROW_CHECK(status == ObjectStatus_Nonexistent);
}
break;
default:
ARROW_LOG(FATAL) << "This code should be unreachable.";
}
}
return Status::OK();
}
-334
View File
@@ -1,334 +0,0 @@
#ifndef PLASMA_CLIENT_H
#define PLASMA_CLIENT_H
#include <time.h>
#include <deque>
#include "plasma.h"
using arrow::Status;
#define PLASMA_DEFAULT_RELEASE_DELAY 64
// Use 100MB as an overestimate of the L3 cache size.
constexpr int64_t kL3CacheSizeBytes = 100000000;
/// Object buffer data structure.
struct ObjectBuffer {
/// The size in bytes of the data object.
int64_t data_size;
/// The address of the data object.
uint8_t *data;
/// The metadata size in bytes.
int64_t metadata_size;
/// The address of the metadata.
uint8_t *metadata;
};
/// Configuration options for the plasma client.
struct PlasmaClientConfig {
/// Number of release calls we wait until the object is actually released.
/// This allows us to avoid invalidating the cpu cache on workers if objects
/// are reused accross tasks.
int release_delay;
};
struct ClientMmapTableEntry;
struct ObjectInUseEntry;
class PlasmaClient {
public:
/// Connect to the local plasma store and plasma manager. Return
/// the resulting connection.
///
/// @param store_socket_name The name of the UNIX domain socket to use to
/// connect to the Plasma store.
/// @param manager_socket_name The name of the UNIX domain socket to use to
/// connect to the local Plasma manager. If this is NULL, then this
/// function will not connect to a manager.
/// @param release_delay Number of released objects that are kept around
/// and not evicted to avoid too many munmaps.
/// @return The return status.
Status Connect(const std::string &store_socket_name,
const std::string &manager_socket_name,
int release_delay);
/// Create an object in the Plasma Store. Any metadata for this object must be
/// be passed in when the object is created.
///
/// @param object_id The ID to use for the newly created object.
/// @param data_size The size in bytes of the space to be allocated for this
/// object's
/// data (this does not include space used for metadata).
/// @param metadata The object's metadata. If there is no metadata, this
/// pointer
/// should be NULL.
/// @param metadata_size The size in bytes of the metadata. If there is no
/// metadata, this should be 0.
/// @param data The address of the newly created object will be written here.
/// @return The return status.
Status Create(ObjectID object_id,
int64_t data_size,
uint8_t *metadata,
int64_t metadata_size,
uint8_t **data);
/// Get some objects from the Plasma Store. This function will block until the
/// objects have all been created and sealed in the Plasma Store or the
/// timeout
/// expires. The caller is responsible for releasing any retrieved objects,
/// but
/// the caller should not release objects that were not retrieved.
///
/// @param object_ids The IDs of the objects to get.
/// @param num_object_ids The number of object IDs to get.
/// @param timeout_ms The amount of time in milliseconds to wait before this
/// request times out. If this value is -1, then no timeout is set.
/// @param object_buffers An array where the results will be stored. If the
/// data
/// size field is -1, then the object was not retrieved.
/// @return The return status.
Status Get(ObjectID object_ids[],
int64_t num_objects,
int64_t timeout_ms,
ObjectBuffer object_buffers[]);
/// Tell Plasma that the client no longer needs the object. This should be
/// called
/// after Get when the client is done with the object. After this call,
/// the address returned by Get is no longer valid. This should be called
/// once for each call to Get (with the same object ID).
///
/// @param object_id The ID of the object that is no longer needed.
/// @return The return status.
Status Release(ObjectID object_id);
/// Check if the object store contains a particular object and the object has
/// been sealed. The result will be stored in has_object.
///
/// @todo: We may want to indicate if the object has been created but not
/// sealed.
///
/// @param object_id The ID of the object whose presence we are checking.
/// @param has_object The function will write 1 at this address if the object
/// is
/// present and 0 if it is not present.
/// @return The return status.
Status Contains(ObjectID object_id, int *has_object);
/// Seal an object in the object store. The object will be immutable after
/// this
/// call.
///
/// @param object_id The ID of the object to seal.
/// @return The return status.
Status Seal(ObjectID object_id);
/// Delete an object from the object store. This currently assumes that the
/// object is present and has been sealed.
///
/// @todo We may want to allow the deletion of objects that are not present or
/// haven't been sealed.
///
/// @param object_id The ID of the object to delete.
/// @return The return status.
Status Delete(ObjectID object_id);
/// Delete objects until we have freed up num_bytes bytes or there are no more
/// released objects that can be deleted.
///
/// @param num_bytes The number of bytes to try to free up.
/// @param num_bytes_evicted Out parameter for total number of bytes of space
/// retrieved.
/// @return The return status.
Status Evict(int64_t num_bytes, int64_t &num_bytes_evicted);
/// Subscribe to notifications when objects are sealed in the object store.
/// Whenever an object is sealed, a message will be written to the client
/// socket
/// that is returned by this method.
///
/// @param fd Out parameter for the file descriptor the client should use to
/// read notifications
/// from the object store about sealed objects.
/// @return The return status.
Status Subscribe(int &fd);
/// Disconnect from the local plasma instance, including the local store and
/// manager.
///
/// @return The return status.
Status Disconnect();
/// Attempt to initiate the transfer of some objects from remote Plasma
/// Stores.
/// This method does not guarantee that the fetched objects will arrive
/// locally.
///
/// For an object that is available in the local Plasma Store, this method
/// will
/// not do anything. For an object that is not available locally, it will
/// check
/// if the object are already being fetched. If so, it will not do anything.
/// If
/// not, it will query the object table for a list of Plasma Managers that
/// have
/// the object. The object table will return a non-empty list, and this Plasma
/// Manager will attempt to initiate transfers from one of those Plasma
/// Managers.
///
/// This function is non-blocking.
///
/// This method is idempotent in the sense that it is ok to call it multiple
/// times.
///
/// @param num_object_ids The number of object IDs fetch is being called on.
/// @param object_ids The IDs of the objects that fetch is being called on.
/// @return The return status.
Status Fetch(int num_object_ids, ObjectID object_ids[]);
/// Wait for (1) a specified number of objects to be available (sealed) in the
/// local Plasma Store or in a remote Plasma Store, or (2) for a timeout to
/// expire. This is a blocking call.
///
/// @param num_object_requests Size of the object_requests array.
/// @param object_requests Object event array. Each element contains a request
/// for a particular object_id. The type of request is specified in the
/// "type" field.
/// - A PLASMA_QUERY_LOCAL request is satisfied when object_id becomes
/// available in the local Plasma Store. In this case, this function
/// sets the "status" field to ObjectStatus_Local. Note, if the
/// status
/// is not ObjectStatus_Local, it will be ObjectStatus_Nonexistent,
/// but it may exist elsewhere in the system.
/// - A PLASMA_QUERY_ANYWHERE request is satisfied when object_id
/// becomes
/// available either at the local Plasma Store or on a remote Plasma
/// Store. In this case, the functions sets the "status" field to
/// ObjectStatus_Local or ObjectStatus_Remote.
/// @param num_ready_objects The number of requests in object_requests array
/// that
/// must be satisfied before the function returns, unless it timeouts.
/// The num_ready_objects should be no larger than num_object_requests.
/// @param timeout_ms Timeout value in milliseconds. If this timeout expires
/// before min_num_ready_objects of requests are satisfied, the
/// function
/// returns.
/// @param num_objects_ready Out parameter for number of satisfied requests in
/// the object_requests list. If the returned number is less than
/// min_num_ready_objects this means that timeout expired.
/// @return The return status.
Status Wait(int num_object_requests,
ObjectRequest object_requests[],
int num_ready_objects,
uint64_t timeout_ms,
int &num_objects_ready);
/// Transfer local object to a different plasma manager.
///
/// @param conn The object containing the connection state.
/// @param addr IP address of the plasma manager we are transfering to.
/// @param port Port of the plasma manager we are transfering to.
/// @object_id ObjectID of the object we are transfering.
/// @return The return status.
Status Transfer(const char *addr, int port, ObjectID object_id);
/// Return the status of a given object. This method may query the object
/// table.
///
/// @param conn The object containing the connection state.
/// @param object_id The ID of the object whose status we query.
/// @param object_status Out parameter for object status. Can take the
/// following values.
/// - PLASMA_CLIENT_LOCAL, if object is stored in the local Plasma
/// Store.
/// has been already scheduled by the Plasma Manager.
/// - PLASMA_CLIENT_TRANSFER, if the object is either currently being
/// transferred or just scheduled.
/// - PLASMA_CLIENT_REMOTE, if the object is stored at a remote
/// Plasma Store.
/// - PLASMA_CLIENT_DOES_NOT_EXIST, if the object doesnt exist in the
/// system.
/// @return The return status.
Status Info(ObjectID object_id, int *object_status);
// private:
Status PerformRelease(ObjectID object_id);
/// File descriptor of the Unix domain socket that connects to the store.
int store_conn;
/// File descriptor of the Unix domain socket that connects to the manager.
int manager_conn;
/// File descriptor of the Unix domain socket on which client receives event
/// notifications for the objects it subscribes for when these objects are
/// sealed either locally or remotely.
int manager_conn_subscribe;
/// Table of dlmalloc buffer files that have been memory mapped so far. This
/// is a hash table mapping a file descriptor to a struct containing the
/// address of the corresponding memory-mapped file.
std::unordered_map<int, ClientMmapTableEntry *> mmap_table;
/// A hash table of the object IDs that are currently being used by this
/// client.
std::unordered_map<ObjectID, ObjectInUseEntry *, UniqueIDHasher>
objects_in_use;
/// Object IDs of the last few release calls. This is a deque and
/// is used to delay releasing objects to see if they can be reused by
/// subsequent tasks so we do not unneccessarily invalidate cpu caches.
/// TODO(pcm): replace this with a proper lru cache using the size of the L3
/// cache.
std::deque<ObjectID> release_history;
/// The number of bytes in the combined objects that are held in the release
/// history doubly-linked list. If this is too large then the client starts
/// releasing objects.
int64_t in_use_object_bytes;
/// Configuration options for the plasma client.
PlasmaClientConfig config;
/// The amount of memory available to the Plasma store. The client needs this
/// information to make sure that it does not delay in releasing so much
/// memory that the store is unable to evict enough objects to free up space.
int64_t store_capacity;
};
/// Return true if the plasma manager is connected.
///
/// @param conn The connection to the local plasma store and plasma manager.
/// @return True if the plasma manager is connected and false otherwise.
bool plasma_manager_is_connected(PlasmaClient *conn);
/// Compute the hash of an object in the object store.
///
/// @param conn The object containing the connection state.
/// @param object_id The ID of the object we want to hash.
/// @param digest A pointer at which to return the hash digest of the object.
/// The pointer must have at least DIGEST_SIZE bytes allocated.
/// @return A boolean representing whether the hash operation succeeded.
bool plasma_compute_object_hash(PlasmaClient *conn,
ObjectID object_id,
unsigned char *digest);
/**
* Get the file descriptor for the socket connection to the plasma manager.
*
* @param conn The plasma connection.
* @return The file descriptor for the manager connection. If there is no
* connection to the manager, this is -1.
*/
int get_manager_fd(PlasmaClient *conn);
/**
* Return the information associated to a given object.
*
* @param conn The object containing the connection state.
* @param object_id The ID of the object whose info the client queries.
* @param object_info The object's infirmation.
* @return PLASMA_CLIENT_LOCAL, if the object is in the local Plasma Store.
* PLASMA_CLIENT_NOT_LOCAL, if not. In this case, the caller needs to
* ignore data, metadata_size, and metadata fields.
*/
// int plasma_info(PlasmaConnection *conn,
// ObjectID object_id,
// ObjectInfo *object_info);
#endif /* PLASMA_CLIENT_H */
-67
View File
@@ -1,67 +0,0 @@
#include "plasma_common.h"
#include <random>
#include "format/plasma_generated.h"
using arrow::Status;
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] = engine();
}
return id;
}
UniqueID UniqueID::from_binary(const std::string &binary) {
UniqueID id;
std::memcpy(&id, binary.data(), sizeof(id));
return id;
}
const uint8_t *UniqueID::data() const {
return id_;
}
uint8_t *UniqueID::mutable_data() {
return id_;
}
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 < sizeof(UniqueID); 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;
}
Status plasma_error_status(int plasma_error) {
switch (plasma_error) {
case PlasmaError_OK:
return Status::OK();
case PlasmaError_ObjectExists:
return Status::PlasmaObjectExists(
"object already exists in the plasma store");
case PlasmaError_ObjectNonexistent:
return Status::PlasmaObjectNonexistent(
"object does not exist in the plasma store");
case PlasmaError_OutOfMemory:
return Status::PlasmaStoreFull("object does not fit in the plasma store");
default:
ARROW_LOG(FATAL) << "unknown plasma error code " << plasma_error;
}
}
-46
View File
@@ -1,46 +0,0 @@
#ifndef PLASMA_COMMON_H
#define PLASMA_COMMON_H
#include <cstring>
#include <string>
// TODO(pcm): Convert getopt and sscanf in the store to use more idiomatic C++
// and get rid of the next three lines:
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include "logging.h"
#include "status.h"
constexpr int64_t kUniqueIDSize = 20;
class UniqueID {
public:
static UniqueID from_random();
static UniqueID from_binary(const std::string &binary);
bool operator==(const UniqueID &rhs) const;
const uint8_t *data() const;
uint8_t *mutable_data();
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 {
/* ObjectID hashing function. */
size_t operator()(const UniqueID &id) const {
size_t result;
std::memcpy(&result, id.data(), sizeof(size_t));
return result;
}
};
typedef UniqueID ObjectID;
arrow::Status plasma_error_status(int plasma_error);
#endif // PLASMA_COMMON_H
-74
View File
@@ -1,74 +0,0 @@
#include "plasma_events.h"
#include <errno.h>
void EventLoop::file_event_callback(aeEventLoop *loop,
int fd,
void *context,
int events) {
FileCallback *callback = reinterpret_cast<FileCallback *>(context);
(*callback)(events);
}
int EventLoop::timer_event_callback(aeEventLoop *loop,
long long timer_id,
void *context) {
TimerCallback *callback = reinterpret_cast<TimerCallback *>(context);
return (*callback)(timer_id);
}
constexpr int kInitialEventLoopSize = 1024;
EventLoop::EventLoop() {
loop_ = aeCreateEventLoop(kInitialEventLoopSize);
}
bool EventLoop::add_file_event(int fd, int events, FileCallback callback) {
if (file_callbacks_.find(fd) != file_callbacks_.end()) {
return false;
}
auto data = std::unique_ptr<FileCallback>(new FileCallback(callback));
void *context = reinterpret_cast<void *>(data.get());
// Try to add the file descriptor.
int err = aeCreateFileEvent(loop_, fd, events, EventLoop::file_event_callback,
context);
// If it cannot be added, increase the size of the event loop.
if (err == AE_ERR && errno == ERANGE) {
err = aeResizeSetSize(loop_, 3 * aeGetSetSize(loop_) / 2);
if (err != AE_OK) {
return false;
}
err = aeCreateFileEvent(loop_, fd, events, EventLoop::file_event_callback,
context);
}
// In any case, test if there were errors.
if (err == AE_OK) {
file_callbacks_.emplace(fd, std::move(data));
return true;
}
return false;
}
void EventLoop::remove_file_event(int fd) {
aeDeleteFileEvent(loop_, fd, AE_READABLE | AE_WRITABLE);
file_callbacks_.erase(fd);
}
void EventLoop::run() {
aeMain(loop_);
}
int64_t EventLoop::add_timer(int64_t timeout, TimerCallback callback) {
auto data = std::unique_ptr<TimerCallback>(new TimerCallback(callback));
void *context = reinterpret_cast<void *>(data.get());
int64_t timer_id = aeCreateTimeEvent(
loop_, timeout, EventLoop::timer_event_callback, context, NULL);
timer_callbacks_.emplace(timer_id, std::move(data));
return timer_id;
}
int EventLoop::remove_timer(int64_t timer_id) {
int err = aeDeleteTimeEvent(loop_, timer_id);
timer_callbacks_.erase(timer_id);
return err;
}
-85
View File
@@ -1,85 +0,0 @@
#ifndef PLASMA_EVENTS
#define PLASMA_EVENTS
#include <functional>
#include <memory>
#include <unordered_map>
extern "C" {
#include "ae/ae.h"
}
/// Constant specifying that the timer is done and it will be removed.
constexpr int kEventLoopTimerDone = AE_NOMORE;
/// Read event on the file descriptor.
constexpr int kEventLoopRead = AE_READABLE;
/// Write event on the file descriptor.
constexpr int kEventLoopWrite = AE_WRITABLE;
class EventLoop {
public:
// Signature of the handler that will be called when there is a new event
// on the file descriptor that this handler has been registered for.
//
// The arguments are the event flags (read or write).
typedef std::function<void(int)> FileCallback;
// This handler will be called when a timer times out. The timer id is
// passed as an argument. The return is the number of milliseconds the timer
// shall be reset to or kEventLoopTimerDone if the timer shall not be
// triggered again.
typedef std::function<int(int64_t)> TimerCallback;
EventLoop();
/// Add a new file event handler to the event loop.
///
/// @param fd The file descriptor we are listening to.
/// @param events The flags for events we are listening to (read or write).
/// @param callback The callback that will be called when the event happens.
/// @return Returns true if the event handler was added successfully.
bool add_file_event(int fd, int events, FileCallback callback);
/// Remove a file event handler from the event loop.
///
/// @param fd The file descriptor of the event handler.
/// @return Void.
void remove_file_event(int fd);
/// Register a handler that will be called after a time slice of
/// "timeout" milliseconds.
///
/// @param timeout The timeout in milliseconds.
/// @param callback The callback for the timeout.
/// @return The ID of the newly created timer.
int64_t add_timer(int64_t timeout, TimerCallback callback);
/// Remove a timer handler from the event loop.
///
/// @param timer_id The ID of the timer that is to be removed.
/// @return The ae.c error code. TODO(pcm): needs to be standardized
int remove_timer(int64_t timer_id);
/// Run the event loop.
///
/// @return Void.
void run();
private:
static void file_event_callback(aeEventLoop *loop,
int fd,
void *context,
int events);
static int timer_event_callback(aeEventLoop *loop,
long long timer_id,
void *context);
aeEventLoop *loop_;
std::unordered_map<int, std::unique_ptr<FileCallback>> file_callbacks_;
std::unordered_map<int64_t, std::unique_ptr<TimerCallback>> timer_callbacks_;
};
#endif // PLASMA_EVENTS
-463
View File
@@ -1,463 +0,0 @@
#include <Python.h>
#include "bytesobject.h"
#include "plasma_io.h"
#include "plasma_common.h"
#include "plasma_protocol.h"
#include "plasma_client.h"
PyObject *PlasmaOutOfMemoryError;
PyObject *PlasmaObjectExistsError;
#include "plasma_extension.h"
PyObject *PyPlasma_connect(PyObject *self, PyObject *args) {
const char *store_socket_name;
const char *manager_socket_name;
int release_delay;
if (!PyArg_ParseTuple(args, "ssi", &store_socket_name, &manager_socket_name,
&release_delay)) {
return NULL;
}
PlasmaClient *client = new PlasmaClient();
ARROW_CHECK_OK(
client->Connect(store_socket_name, manager_socket_name, release_delay));
return PyCapsule_New(client, "plasma", NULL);
}
PyObject *PyPlasma_disconnect(PyObject *self, PyObject *args) {
PyObject *client_capsule;
if (!PyArg_ParseTuple(args, "O", &client_capsule)) {
return NULL;
}
PlasmaClient *client;
ARROW_CHECK(PyObjectToPlasmaClient(client_capsule, &client));
ARROW_CHECK_OK(client->Disconnect());
/* We use the context of the connection capsule to indicate if the connection
* is still active (if the context is NULL) or if it is closed (if the context
* is (void*) 0x1). This is neccessary because the primary pointer of the
* capsule cannot be NULL. */
PyCapsule_SetContext(client_capsule, (void *) 0x1);
Py_RETURN_NONE;
}
PyObject *PyPlasma_create(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
long long size;
PyObject *metadata;
if (!PyArg_ParseTuple(args, "O&O&LO", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id, &size, &metadata)) {
return NULL;
}
if (!PyByteArray_Check(metadata)) {
PyErr_SetString(PyExc_TypeError, "metadata must be a bytearray");
return NULL;
}
uint8_t *data;
Status s = client->Create(object_id, size,
(uint8_t *) PyByteArray_AsString(metadata),
PyByteArray_Size(metadata), &data);
if (s.IsPlasmaObjectExists()) {
PyErr_SetString(PlasmaObjectExistsError,
"An object with this ID already exists in the plasma "
"store.");
return NULL;
}
if (s.IsPlasmaStoreFull()) {
PyErr_SetString(PlasmaOutOfMemoryError,
"The plasma store ran out of memory and could not create "
"this object.");
return NULL;
}
ARROW_CHECK(s.ok());
#if PY_MAJOR_VERSION >= 3
return PyMemoryView_FromMemory((char *) data, (Py_ssize_t) size, PyBUF_WRITE);
#else
return PyBuffer_FromReadWriteMemory((void *) data, (Py_ssize_t) size);
#endif
}
PyObject *PyPlasma_hash(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
if (!PyArg_ParseTuple(args, "O&O&", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id)) {
return NULL;
}
unsigned char digest[kDigestSize];
bool success = plasma_compute_object_hash(client, object_id, digest);
if (success) {
PyObject *digest_string =
PyBytes_FromStringAndSize((char *) digest, kDigestSize);
return digest_string;
} else {
Py_RETURN_NONE;
}
}
PyObject *PyPlasma_seal(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
if (!PyArg_ParseTuple(args, "O&O&", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id)) {
return NULL;
}
ARROW_CHECK_OK(client->Seal(object_id));
Py_RETURN_NONE;
}
PyObject *PyPlasma_release(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
if (!PyArg_ParseTuple(args, "O&O&", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id)) {
return NULL;
}
ARROW_CHECK_OK(client->Release(object_id));
Py_RETURN_NONE;
}
PyObject *PyPlasma_get(PyObject *self, PyObject *args) {
PlasmaClient *client;
PyObject *object_id_list;
long long timeout_ms;
if (!PyArg_ParseTuple(args, "O&OL", PyObjectToPlasmaClient, &client,
&object_id_list, &timeout_ms)) {
return NULL;
}
Py_ssize_t num_object_ids = PyList_Size(object_id_list);
ObjectID *object_ids = (ObjectID *) malloc(sizeof(ObjectID) * num_object_ids);
ObjectBuffer *object_buffers =
(ObjectBuffer *) malloc(sizeof(ObjectBuffer) * num_object_ids);
for (int i = 0; i < num_object_ids; ++i) {
PyStringToUniqueID(PyList_GetItem(object_id_list, i), &object_ids[i]);
}
Py_BEGIN_ALLOW_THREADS;
ARROW_CHECK_OK(
client->Get(object_ids, num_object_ids, timeout_ms, object_buffers));
Py_END_ALLOW_THREADS;
free(object_ids);
PyObject *returns = PyList_New(num_object_ids);
for (int i = 0; i < num_object_ids; ++i) {
if (object_buffers[i].data_size != -1) {
/* The object was retrieved, so return the object. */
PyObject *t = PyTuple_New(2);
#if PY_MAJOR_VERSION >= 3
PyTuple_SetItem(
t, 0, PyMemoryView_FromMemory(
(char *) object_buffers[i].data,
(Py_ssize_t) object_buffers[i].data_size, PyBUF_READ));
PyTuple_SetItem(
t, 1, PyMemoryView_FromMemory(
(char *) object_buffers[i].metadata,
(Py_ssize_t) object_buffers[i].metadata_size, PyBUF_READ));
#else
PyTuple_SetItem(
t, 0, PyBuffer_FromMemory((void *) object_buffers[i].data,
(Py_ssize_t) object_buffers[i].data_size));
PyTuple_SetItem(t, 1, PyBuffer_FromMemory(
(void *) object_buffers[i].metadata,
(Py_ssize_t) object_buffers[i].metadata_size));
#endif
PyList_SetItem(returns, i, t);
} else {
/* The object was not retrieved, so just add None to the list of return
* values. */
Py_XINCREF(Py_None);
PyList_SetItem(returns, i, Py_None);
}
}
free(object_buffers);
return returns;
}
PyObject *PyPlasma_contains(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
if (!PyArg_ParseTuple(args, "O&O&", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id)) {
return NULL;
}
int has_object;
ARROW_CHECK_OK(client->Contains(object_id, &has_object));
if (has_object)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
PyObject *PyPlasma_fetch(PyObject *self, PyObject *args) {
PlasmaClient *client;
PyObject *object_id_list;
if (!PyArg_ParseTuple(args, "O&O", PyObjectToPlasmaClient, &client,
&object_id_list)) {
return NULL;
}
if (!plasma_manager_is_connected(client)) {
PyErr_SetString(PyExc_RuntimeError, "Not connected to the plasma manager");
return NULL;
}
Py_ssize_t n = PyList_Size(object_id_list);
ObjectID *object_ids = (ObjectID *) malloc(sizeof(ObjectID) * n);
for (int i = 0; i < n; ++i) {
PyStringToUniqueID(PyList_GetItem(object_id_list, i), &object_ids[i]);
}
ARROW_CHECK_OK(client->Fetch((int) n, object_ids));
free(object_ids);
Py_RETURN_NONE;
}
PyObject *PyPlasma_wait(PyObject *self, PyObject *args) {
PlasmaClient *client;
PyObject *object_id_list;
long long timeout;
int num_returns;
if (!PyArg_ParseTuple(args, "O&OLi", PyObjectToPlasmaClient, &client,
&object_id_list, &timeout, &num_returns)) {
return NULL;
}
Py_ssize_t n = PyList_Size(object_id_list);
if (!plasma_manager_is_connected(client)) {
PyErr_SetString(PyExc_RuntimeError, "Not connected to the plasma manager");
return NULL;
}
if (num_returns < 0) {
PyErr_SetString(PyExc_RuntimeError,
"The argument num_returns cannot be less than zero.");
return NULL;
}
if (num_returns > n) {
PyErr_SetString(
PyExc_RuntimeError,
"The argument num_returns cannot be greater than len(object_ids)");
return NULL;
}
int64_t threshold = 1 << 30;
if (timeout > threshold) {
PyErr_SetString(PyExc_RuntimeError,
"The argument timeout cannot be greater than 2 ** 30.");
return NULL;
}
ObjectRequest *object_requests =
(ObjectRequest *) malloc(sizeof(ObjectRequest) * n);
for (int i = 0; i < n; ++i) {
ARROW_CHECK(PyStringToUniqueID(PyList_GetItem(object_id_list, i),
&object_requests[i].object_id) == 1);
object_requests[i].type = PLASMA_QUERY_ANYWHERE;
}
/* Drop the global interpreter lock while we are waiting, so other threads can
* run. */
int num_return_objects;
Py_BEGIN_ALLOW_THREADS;
ARROW_CHECK_OK(client->Wait((int) n, object_requests, num_returns,
(uint64_t) timeout, num_return_objects));
Py_END_ALLOW_THREADS;
int num_to_return = std::min(num_return_objects, num_returns);
PyObject *ready_ids = PyList_New(num_to_return);
PyObject *waiting_ids = PySet_New(object_id_list);
int num_returned = 0;
for (int i = 0; i < n; ++i) {
if (num_returned == num_to_return) {
break;
}
if (object_requests[i].status == ObjectStatus_Local ||
object_requests[i].status == ObjectStatus_Remote) {
PyObject *ready =
PyBytes_FromStringAndSize((char *) &object_requests[i].object_id,
sizeof(object_requests[i].object_id));
PyList_SetItem(ready_ids, num_returned, ready);
PySet_Discard(waiting_ids, ready);
num_returned += 1;
} else {
ARROW_CHECK(object_requests[i].status == ObjectStatus_Nonexistent);
}
}
ARROW_CHECK(num_returned == num_to_return);
/* Return both the ready IDs and the remaining IDs. */
PyObject *t = PyTuple_New(2);
PyTuple_SetItem(t, 0, ready_ids);
PyTuple_SetItem(t, 1, waiting_ids);
return t;
}
PyObject *PyPlasma_evict(PyObject *self, PyObject *args) {
PlasmaClient *client;
long long num_bytes;
if (!PyArg_ParseTuple(args, "O&L", PyObjectToPlasmaClient, &client,
&num_bytes)) {
return NULL;
}
int64_t evicted_bytes;
ARROW_CHECK_OK(client->Evict((int64_t) num_bytes, evicted_bytes));
return PyLong_FromLong((long) evicted_bytes);
}
PyObject *PyPlasma_delete(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
if (!PyArg_ParseTuple(args, "O&O&", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id)) {
return NULL;
}
ARROW_CHECK_OK(client->Delete(object_id));
Py_RETURN_NONE;
}
PyObject *PyPlasma_transfer(PyObject *self, PyObject *args) {
PlasmaClient *client;
ObjectID object_id;
const char *addr;
int port;
if (!PyArg_ParseTuple(args, "O&O&si", PyObjectToPlasmaClient, &client,
PyStringToUniqueID, &object_id, &addr, &port)) {
return NULL;
}
if (!plasma_manager_is_connected(client)) {
PyErr_SetString(PyExc_RuntimeError, "Not connected to the plasma manager");
return NULL;
}
ARROW_CHECK_OK(client->Transfer(addr, port, object_id));
Py_RETURN_NONE;
}
PyObject *PyPlasma_subscribe(PyObject *self, PyObject *args) {
PlasmaClient *client;
if (!PyArg_ParseTuple(args, "O&", PyObjectToPlasmaClient, &client)) {
return NULL;
}
int sock;
ARROW_CHECK_OK(client->Subscribe(sock));
return PyLong_FromLong(sock);
}
PyObject *PyPlasma_receive_notification(PyObject *self, PyObject *args) {
int plasma_sock;
if (!PyArg_ParseTuple(args, "i", &plasma_sock)) {
return NULL;
}
/* Receive object notification from the plasma connection socket. If the
* object was added, return a tuple of its fields: ObjectID, data_size,
* metadata_size. If the object was deleted, data_size and metadata_size will
* be set to -1. */
uint8_t *notification = read_message_async(plasma_sock);
if (notification == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"Failed to read object notification from Plasma socket");
return NULL;
}
auto object_info = flatbuffers::GetRoot<ObjectInfo>(notification);
/* Construct a tuple from object_info and return. */
PyObject *t = PyTuple_New(3);
PyTuple_SetItem(t, 0,
PyBytes_FromStringAndSize(object_info->object_id()->data(),
object_info->object_id()->size()));
if (object_info->is_deletion()) {
PyTuple_SetItem(t, 1, PyLong_FromLong(-1));
PyTuple_SetItem(t, 2, PyLong_FromLong(-1));
} else {
PyTuple_SetItem(t, 1, PyLong_FromLong(object_info->data_size()));
PyTuple_SetItem(t, 2, PyLong_FromLong(object_info->metadata_size()));
}
free(notification);
return t;
}
static PyMethodDef plasma_methods[] = {
{"connect", PyPlasma_connect, METH_VARARGS, "Connect to plasma."},
{"disconnect", PyPlasma_disconnect, METH_VARARGS,
"Disconnect from plasma."},
{"create", PyPlasma_create, METH_VARARGS, "Create a new plasma object."},
{"hash", PyPlasma_hash, METH_VARARGS,
"Compute the hash of a plasma object."},
{"seal", PyPlasma_seal, METH_VARARGS, "Seal a plasma object."},
{"get", PyPlasma_get, METH_VARARGS, "Get a plasma object."},
{"contains", PyPlasma_contains, METH_VARARGS,
"Does the plasma store contain this plasma object?"},
{"fetch", PyPlasma_fetch, METH_VARARGS,
"Fetch the object from another plasma manager instance."},
{"wait", PyPlasma_wait, METH_VARARGS,
"Wait until num_returns objects in object_ids are ready."},
{"evict", PyPlasma_evict, METH_VARARGS,
"Evict some objects until we recover some number of bytes."},
{"release", PyPlasma_release, METH_VARARGS, "Release the plasma object."},
{"delete", PyPlasma_delete, METH_VARARGS, "Delete a plasma object."},
{"transfer", PyPlasma_transfer, METH_VARARGS,
"Transfer object to another plasma manager."},
{"subscribe", PyPlasma_subscribe, METH_VARARGS,
"Subscribe to the plasma notification socket."},
{"receive_notification", PyPlasma_receive_notification, METH_VARARGS,
"Receive next notification from plasma notification socket."},
{NULL} /* Sentinel */
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"libplasma", /* m_name */
"A Python client library for plasma.", /* m_doc */
0, /* m_size */
plasma_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
#if PY_MAJOR_VERSION >= 3
#define INITERROR return NULL
#else
#define INITERROR return
#endif
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
#if PY_MAJOR_VERSION >= 3
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#else
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
#endif
MOD_INIT(libplasma) {
#if PY_MAJOR_VERSION >= 3
PyObject *m = PyModule_Create(&moduledef);
#else
PyObject *m = Py_InitModule3("libplasma", plasma_methods,
"A Python client library for plasma.");
#endif
/* Create a custom exception for when an object ID is reused. */
char plasma_object_exists_error[] = "plasma_object_exists.error";
PlasmaObjectExistsError =
PyErr_NewException(plasma_object_exists_error, NULL, NULL);
Py_INCREF(PlasmaObjectExistsError);
PyModule_AddObject(m, "plasma_object_exists_error", PlasmaObjectExistsError);
/* Create a custom exception for when the plasma store is out of memory. */
char plasma_out_of_memory_error[] = "plasma_out_of_memory.error";
PlasmaOutOfMemoryError =
PyErr_NewException(plasma_out_of_memory_error, NULL, NULL);
Py_INCREF(PlasmaOutOfMemoryError);
PyModule_AddObject(m, "plasma_out_of_memory_error", PlasmaOutOfMemoryError);
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}
-24
View File
@@ -1,24 +0,0 @@
#ifndef PLASMA_EXTENSION_H
#define PLASMA_EXTENSION_H
static int PyObjectToPlasmaClient(PyObject *object, PlasmaClient **client) {
if (PyCapsule_IsValid(object, "plasma")) {
*client = (PlasmaClient *) PyCapsule_GetPointer(object, "plasma");
return 1;
} else {
PyErr_SetString(PyExc_TypeError, "must be a 'plasma' capsule");
return 0;
}
}
int PyStringToUniqueID(PyObject *object, ObjectID *object_id) {
if (PyBytes_Check(object)) {
memcpy(object_id, PyBytes_AsString(object), sizeof(ObjectID));
return 1;
} else {
PyErr_SetString(PyExc_TypeError, "must be a 20 character string");
return 0;
}
}
#endif /* PLASMA_EXTENSION_H */
-220
View File
@@ -1,220 +0,0 @@
#include "plasma_io.h"
#include "plasma_common.h"
using arrow::Status;
/* Number of times we try binding to a socket. */
#define NUM_BIND_ATTEMPTS 5
#define BIND_TIMEOUT_MS 100
/* Number of times we try connecting to a socket. */
#define NUM_CONNECT_ATTEMPTS 50
#define CONNECT_TIMEOUT_MS 100
Status WriteBytes(int fd, uint8_t *cursor, size_t length) {
ssize_t nbytes = 0;
size_t bytesleft = length;
size_t offset = 0;
while (bytesleft > 0) {
/* While we haven't written the whole message, write to the file descriptor,
* advance the cursor, and decrease the amount left to write. */
nbytes = write(fd, cursor + offset, bytesleft);
if (nbytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
continue;
}
return Status::IOError(std::string(strerror(errno)));
} else if (nbytes == 0) {
return Status::IOError("Encountered unexpected EOF");
}
ARROW_CHECK(nbytes > 0);
bytesleft -= nbytes;
offset += nbytes;
}
return Status::OK();
}
Status WriteMessage(int fd, int64_t type, int64_t length, uint8_t *bytes) {
int64_t version = PLASMA_PROTOCOL_VERSION;
RETURN_NOT_OK(
WriteBytes(fd, reinterpret_cast<uint8_t *>(&version), sizeof(version)));
RETURN_NOT_OK(
WriteBytes(fd, reinterpret_cast<uint8_t *>(&type), sizeof(type)));
RETURN_NOT_OK(
WriteBytes(fd, reinterpret_cast<uint8_t *>(&length), sizeof(length)));
return WriteBytes(fd, bytes, length * sizeof(char));
}
Status ReadBytes(int fd, uint8_t *cursor, size_t length) {
ssize_t nbytes = 0;
/* Termination condition: EOF or read 'length' bytes total. */
size_t bytesleft = length;
size_t offset = 0;
while (bytesleft > 0) {
nbytes = read(fd, cursor + offset, bytesleft);
if (nbytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
continue;
}
return Status::IOError(std::string(strerror(errno)));
} else if (0 == nbytes) {
return Status::IOError("Encountered unexpected EOF");
}
ARROW_CHECK(nbytes > 0);
bytesleft -= nbytes;
offset += nbytes;
}
return Status::OK();
}
Status ReadMessage(int fd, int64_t *type, std::vector<uint8_t> &buffer) {
int64_t version;
RETURN_NOT_OK_ELSE(
ReadBytes(fd, reinterpret_cast<uint8_t *>(&version), sizeof(version)),
*type = DISCONNECT_CLIENT);
ARROW_CHECK(version == PLASMA_PROTOCOL_VERSION) << "version = " << version;
int64_t length;
RETURN_NOT_OK_ELSE(
ReadBytes(fd, reinterpret_cast<uint8_t *>(type), sizeof(*type)),
*type = DISCONNECT_CLIENT);
RETURN_NOT_OK_ELSE(
ReadBytes(fd, reinterpret_cast<uint8_t *>(&length), sizeof(length)),
*type = DISCONNECT_CLIENT);
if (length > buffer.size()) {
buffer.resize(length);
}
RETURN_NOT_OK_ELSE(ReadBytes(fd, buffer.data(), length),
*type = DISCONNECT_CLIENT);
return Status::OK();
}
int bind_ipc_sock(const std::string &pathname, bool shall_listen) {
struct sockaddr_un socket_address;
int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socket_fd < 0) {
ARROW_LOG(ERROR) << "socket() failed for pathname " << pathname;
return -1;
}
/* Tell the system to allow the port to be reused. */
int on = 1;
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
sizeof(on)) < 0) {
ARROW_LOG(ERROR) << "setsockopt failed for pathname " << pathname;
close(socket_fd);
return -1;
}
unlink(pathname.c_str());
memset(&socket_address, 0, sizeof(socket_address));
socket_address.sun_family = AF_UNIX;
if (pathname.size() + 1 > sizeof(socket_address.sun_path)) {
ARROW_LOG(ERROR) << "Socket pathname is too long.";
close(socket_fd);
return -1;
}
strncpy(socket_address.sun_path, pathname.c_str(), pathname.size() + 1);
if (bind(socket_fd, (struct sockaddr *) &socket_address,
sizeof(socket_address)) != 0) {
ARROW_LOG(ERROR) << "Bind failed for pathname " << pathname;
close(socket_fd);
return -1;
}
if (shall_listen && listen(socket_fd, 128) == -1) {
ARROW_LOG(ERROR) << "Could not listen to socket " << pathname;
close(socket_fd);
return -1;
}
return socket_fd;
}
int connect_ipc_sock_retry(const std::string &pathname,
int num_retries,
int64_t timeout) {
/* Pick the default values if the user did not specify. */
if (num_retries < 0) {
num_retries = NUM_CONNECT_ATTEMPTS;
}
if (timeout < 0) {
timeout = CONNECT_TIMEOUT_MS;
}
int fd = -1;
for (int num_attempts = 0; num_attempts < num_retries; ++num_attempts) {
fd = connect_ipc_sock(pathname);
if (fd >= 0) {
break;
}
if (num_attempts == 0) {
ARROW_LOG(ERROR) << "Connection to socket failed for pathname "
<< pathname;
}
/* Sleep for timeout milliseconds. */
usleep(timeout * 1000);
}
/* If we could not connect to the socket, exit. */
if (fd == -1) {
ARROW_LOG(FATAL) << "Could not connect to socket " << pathname;
}
return fd;
}
int connect_ipc_sock(const std::string &pathname) {
struct sockaddr_un socket_address;
int socket_fd;
socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socket_fd < 0) {
ARROW_LOG(ERROR) << "socket() failed for pathname " << pathname;
return -1;
}
memset(&socket_address, 0, sizeof(socket_address));
socket_address.sun_family = AF_UNIX;
if (pathname.size() + 1 > sizeof(socket_address.sun_path)) {
ARROW_LOG(ERROR) << "Socket pathname is too long.";
return -1;
}
strncpy(socket_address.sun_path, pathname.c_str(), pathname.size() + 1);
if (connect(socket_fd, (struct sockaddr *) &socket_address,
sizeof(socket_address)) != 0) {
close(socket_fd);
return -1;
}
return socket_fd;
}
int AcceptClient(int socket_fd) {
int client_fd = accept(socket_fd, NULL, NULL);
if (client_fd < 0) {
ARROW_LOG(ERROR) << "Error reading from socket.";
return -1;
}
return client_fd;
}
uint8_t *read_message_async(int sock) {
int64_t size;
Status s = ReadBytes(sock, (uint8_t *) &size, sizeof(int64_t));
if (!s.ok()) {
/* The other side has closed the socket. */
ARROW_LOG(DEBUG)
<< "Socket has been closed, or some other error has occurred.";
close(sock);
return NULL;
}
uint8_t *message = (uint8_t *) malloc(size);
s = ReadBytes(sock, message, size);
if (!s.ok()) {
/* The other side has closed the socket. */
ARROW_LOG(DEBUG)
<< "Socket has been closed, or some other error has occurred.";
close(sock);
return NULL;
}
return message;
}
-38
View File
@@ -1,38 +0,0 @@
#include <inttypes.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <vector>
#include <string>
#include "status.h"
// TODO(pcm): Replace our own custom message header (message type,
// message length, plasma protocol verion) with one that is serialized
// using flatbuffers.
#define PLASMA_PROTOCOL_VERSION 0x0000000000000000
#define DISCONNECT_CLIENT 0
arrow::Status WriteBytes(int fd, uint8_t *cursor, size_t length);
arrow::Status WriteMessage(int fd,
int64_t type,
int64_t length,
uint8_t *bytes);
arrow::Status ReadBytes(int fd, uint8_t *cursor, size_t length);
arrow::Status ReadMessage(int fd, int64_t *type, std::vector<uint8_t> &buffer);
int bind_ipc_sock(const std::string &pathname, bool shall_listen);
int connect_ipc_sock(const std::string &pathname);
int connect_ipc_sock_retry(const std::string &pathname,
int num_retries,
int64_t timeout);
int AcceptClient(int socket_fd);
uint8_t *read_message_async(int sock);
+67 -52
View File
@@ -28,13 +28,14 @@
#include "utarray.h"
#include "utstring.h"
#include "common_protocol.h"
#include "common.h"
#include "io.h"
#include "net.h"
#include "event_loop.h"
#include "plasma.h"
#include "plasma_protocol.h"
#include "plasma_client.h"
#include "common.h"
#include "plasma/plasma.h"
#include "plasma/events.h"
#include "plasma/protocol.h"
#include "plasma/client.h"
#include "plasma_manager.h"
#include "state/db.h"
#include "state/object_table.h"
@@ -168,7 +169,7 @@ struct WaitRequest {
WaitRequest(ClientConnection *client_conn,
int64_t timer,
int64_t num_object_requests,
ObjectRequestMap &&object_requests,
plasma::ObjectRequestMap &&object_requests,
int64_t num_objects_to_wait_for,
int64_t num_satisfied)
: client_conn(client_conn),
@@ -188,7 +189,7 @@ struct WaitRequest {
/** The object requests for this wait request. Each object request has a
* status field which is either PLASMA_QUERY_LOCAL or PLASMA_QUERY_ANYWHERE.
*/
ObjectRequestMap object_requests;
plasma::ObjectRequestMap object_requests;
/** The minimum number of objects to wait for in this request. */
int64_t num_objects_to_wait_for;
/** The number of object requests in this wait request that are already
@@ -214,7 +215,7 @@ struct PlasmaManagerState {
/** Event loop. */
event_loop *loop;
/** Connection to the local plasma store for reading or writing data. */
PlasmaClient *plasma_conn;
plasma::PlasmaClient *plasma_conn;
/** Hash table of all contexts for active connections to
* other plasma managers. These are used for writing data to
* other plasma stores. */
@@ -341,9 +342,9 @@ ObjectWaitRequests **object_wait_requests_table_ptr_from_type(
PlasmaManagerState *manager_state,
int type) {
/* We use different types of hash tables for different requests. */
if (type == PLASMA_QUERY_LOCAL) {
if (type == plasma::PLASMA_QUERY_LOCAL) {
return &manager_state->object_wait_requests_local;
} else if (type == PLASMA_QUERY_ANYWHERE) {
} else if (type == plasma::PLASMA_QUERY_ANYWHERE) {
return &manager_state->object_wait_requests_remote;
} else {
LOG_FATAL("This code should be unreachable.");
@@ -415,10 +416,10 @@ void remove_wait_request(PlasmaManagerState *manager_state,
void return_from_wait(PlasmaManagerState *manager_state,
WaitRequest *wait_req) {
/* Send the reply to the client. */
handle_sigpipe(
SendWaitReply(wait_req->client_conn->fd, wait_req->object_requests,
wait_req->num_object_requests),
wait_req->client_conn->fd);
handle_sigpipe(plasma::SendWaitReply(wait_req->client_conn->fd,
wait_req->object_requests,
wait_req->num_object_requests),
wait_req->client_conn->fd);
/* Iterate over all object IDs requested as part of this wait request.
* Remove the wait request from each of the relevant object_wait_requests hash
* tables if it is present there. */
@@ -455,7 +456,8 @@ void update_object_wait_requests(PlasmaManagerState *manager_state,
WaitRequest *wait_req = *wait_req_ptr;
wait_req->num_satisfied += 1;
/* Mark the object as present in the wait request. */
auto object_request = wait_req->object_requests.find(obj_id);
auto object_request =
wait_req->object_requests.find(obj_id.to_plasma_id());
/* Check that we found the object. */
CHECK(object_request != wait_req->object_requests.end());
/* Check that the object found was not previously known to us. */
@@ -513,7 +515,7 @@ PlasmaManagerState *PlasmaManagerState_init(const char *store_socket_name,
PlasmaManagerState *state =
(PlasmaManagerState *) malloc(sizeof(PlasmaManagerState));
state->loop = event_loop_create();
state->plasma_conn = new PlasmaClient();
state->plasma_conn = new plasma::PlasmaClient();
ARROW_CHECK_OK(state->plasma_conn->Connect(store_socket_name, "",
PLASMA_DEFAULT_RELEASE_DELAY));
state->manager_connections = NULL;
@@ -551,7 +553,7 @@ PlasmaManagerState *PlasmaManagerState_init(const char *store_socket_name,
state->local_available_objects = NULL;
/* Subscribe to notifications about sealed objects. */
int plasma_fd;
ARROW_CHECK_OK(state->plasma_conn->Subscribe(plasma_fd));
ARROW_CHECK_OK(state->plasma_conn->Subscribe(&plasma_fd));
/* Add the callback that processes the notification to the event loop. */
event_loop_add_file(state->loop, plasma_fd, EVENT_LOOP_READ,
process_object_notification, state);
@@ -642,7 +644,8 @@ int write_object_chunk(ClientConnection *conn, PlasmaRequestBuffer *buf) {
conn->cursor = 0;
/* We are done sending the object, so release it. The corresponding call to
* plasma_get occurred in process_transfer_request. */
ARROW_CHECK_OK(conn->manager_state->plasma_conn->Release(buf->object_id));
ARROW_CHECK_OK(conn->manager_state->plasma_conn->Release(
buf->object_id.to_plasma_id()));
}
return 0;
@@ -668,7 +671,8 @@ void send_queued_request(event_loop *loop,
switch (buf->type) {
case MessageType_PlasmaDataRequest:
err = handle_sigpipe(
SendDataRequest(conn->fd, buf->object_id, state->addr, state->port),
plasma::SendDataRequest(conn->fd, buf->object_id.to_plasma_id(),
state->addr, state->port),
conn->fd);
break;
case MessageType_PlasmaDataReply:
@@ -676,9 +680,10 @@ void send_queued_request(event_loop *loop,
if (conn->cursor == 0) {
/* If the cursor is zero, we haven't sent any requests for this object
* yet, so send the initial data request. */
err = handle_sigpipe(SendDataReply(conn->fd, buf->object_id,
buf->data_size, buf->metadata_size),
conn->fd);
err = handle_sigpipe(
plasma::SendDataReply(conn->fd, buf->object_id.to_plasma_id(),
buf->data_size, buf->metadata_size),
conn->fd);
}
if (err == 0) {
err = write_object_chunk(conn, buf);
@@ -759,8 +764,10 @@ void process_data_chunk(event_loop *loop,
LOG_DEBUG("reading on channel %d finished", data_sock);
/* The following seal also triggers notification of clients for fetch or
* wait requests, see process_object_notification. */
ARROW_CHECK_OK(conn->manager_state->plasma_conn->Seal(buf->object_id));
ARROW_CHECK_OK(conn->manager_state->plasma_conn->Release(buf->object_id));
ARROW_CHECK_OK(
conn->manager_state->plasma_conn->Seal(buf->object_id.to_plasma_id()));
ARROW_CHECK_OK(
conn->manager_state->plasma_conn->Release(buf->object_id.to_plasma_id()));
/* Remove the request buffer used for reading this object's data. */
DL_DELETE(conn->transfer_queue, buf);
free(buf);
@@ -838,10 +845,11 @@ void process_transfer_request(event_loop *loop,
}
/* Allocate and append the request to the transfer queue. */
ObjectBuffer object_buffer;
plasma::ObjectBuffer object_buffer;
plasma::ObjectID object_id = obj_id.to_plasma_id();
/* We pass in 0 to indicate that the command should return immediately. */
ARROW_CHECK_OK(
conn->manager_state->plasma_conn->Get(&obj_id, 1, 0, &object_buffer));
conn->manager_state->plasma_conn->Get(&object_id, 1, 0, &object_buffer));
if (object_buffer.data_size == -1) {
/* If the object wasn't locally available, exit immediately. If the object
* later appears locally, the requesting plasma manager should request the
@@ -908,7 +916,7 @@ void process_data_request(event_loop *loop,
/* The corresponding call to plasma_release should happen in
* process_data_chunk. */
Status s = conn->manager_state->plasma_conn->Create(
object_id, data_size, NULL, metadata_size, &(buf->data));
object_id.to_plasma_id(), data_size, NULL, metadata_size, &(buf->data));
/* If success_create == true, a new object has been created.
* If success_create == false the object creation has failed, possibly
* due to an object with the same ID already existing in the Plasma Store. */
@@ -1118,7 +1126,8 @@ void object_present_callback(ObjectID object_id,
CHECK(manager_count >= 1);
/* Update the in-progress remote wait requests. */
update_object_wait_requests(manager_state, object_id, PLASMA_QUERY_ANYWHERE,
update_object_wait_requests(manager_state, object_id,
plasma::PLASMA_QUERY_ANYWHERE,
ObjectStatus_Remote);
}
@@ -1143,7 +1152,7 @@ void object_table_subscribe_callback(ObjectID object_id,
void process_fetch_requests(ClientConnection *client_conn,
int num_object_ids,
ObjectID object_ids[]) {
plasma::ObjectID object_ids[]) {
PlasmaManagerState *manager_state = client_conn->manager_state;
int num_object_ids_to_request = 0;
@@ -1197,7 +1206,7 @@ int wait_timeout_handler(event_loop *loop, timer_id id, void *context) {
}
void process_wait_request(ClientConnection *client_conn,
ObjectRequestMap &&object_requests,
plasma::ObjectRequestMap &&object_requests,
uint64_t timeout_ms,
int num_ready_objects) {
CHECK(client_conn != NULL);
@@ -1231,10 +1240,10 @@ void process_wait_request(ClientConnection *client_conn,
add_wait_request_for_object(manager_state, obj_id, object_request.type,
wait_req);
if (object_request.type == PLASMA_QUERY_LOCAL) {
if (object_request.type == plasma::PLASMA_QUERY_LOCAL) {
/* TODO(rkn): If desired, we could issue a fetch command here to retrieve
* the object. */
} else if (object_request.type == PLASMA_QUERY_ANYWHERE) {
} else if (object_request.type == plasma::PLASMA_QUERY_ANYWHERE) {
/* Add this object ID to the list of object IDs to request notifications
* for from the object table. */
object_ids_to_request[num_object_ids_to_request] = obj_id;
@@ -1289,8 +1298,10 @@ void request_status_done(ObjectID object_id,
ClientConnection *client_conn = (ClientConnection *) context;
int status =
request_status(object_id, manager_count, manager_vector, context);
handle_sigpipe(SendStatusReply(client_conn->fd, &object_id, &status, 1),
client_conn->fd);
plasma::ObjectID object_id_copy = object_id.to_plasma_id();
handle_sigpipe(
plasma::SendStatusReply(client_conn->fd, &object_id_copy, &status, 1),
client_conn->fd);
}
int request_status(ObjectID object_id,
@@ -1318,19 +1329,22 @@ void object_table_lookup_fail_callback(ObjectID object_id,
CHECK(0);
}
void process_status_request(ClientConnection *client_conn, ObjectID object_id) {
void process_status_request(ClientConnection *client_conn,
plasma::ObjectID object_id) {
/* Return success immediately if we already have this object. */
if (is_object_local(client_conn->manager_state, object_id)) {
int status = ObjectStatus_Local;
handle_sigpipe(SendStatusReply(client_conn->fd, &object_id, &status, 1),
client_conn->fd);
handle_sigpipe(
plasma::SendStatusReply(client_conn->fd, &object_id, &status, 1),
client_conn->fd);
return;
}
if (client_conn->manager_state->db == NULL) {
int status = ObjectStatus_Nonexistent;
handle_sigpipe(SendStatusReply(client_conn->fd, &object_id, &status, 1),
client_conn->fd);
handle_sigpipe(
plasma::SendStatusReply(client_conn->fd, &object_id, &status, 1),
client_conn->fd);
return;
}
@@ -1426,9 +1440,9 @@ void process_add_object_notification(PlasmaManagerState *state,
}
/* Update the in-progress local and remote wait requests. */
update_object_wait_requests(state, object_id, PLASMA_QUERY_LOCAL,
update_object_wait_requests(state, object_id, plasma::PLASMA_QUERY_LOCAL,
ObjectStatus_Local);
update_object_wait_requests(state, object_id, PLASMA_QUERY_ANYWHERE,
update_object_wait_requests(state, object_id, plasma::PLASMA_QUERY_ANYWHERE,
ObjectStatus_Local);
}
@@ -1546,46 +1560,47 @@ void process_message(event_loop *loop,
switch (type) {
case MessageType_PlasmaDataRequest: {
LOG_DEBUG("Processing data request");
ObjectID object_id;
plasma::ObjectID object_id;
char *address;
int port;
ARROW_CHECK_OK(ReadDataRequest(data, &object_id, &address, &port));
ARROW_CHECK_OK(
plasma::ReadDataRequest(data, length, &object_id, &address, &port));
process_transfer_request(loop, object_id, address, port, conn);
free(address);
} break;
case MessageType_PlasmaDataReply: {
LOG_DEBUG("Processing data reply");
ObjectID object_id;
plasma::ObjectID object_id;
int64_t object_size;
int64_t metadata_size;
ARROW_CHECK_OK(
ReadDataReply(data, &object_id, &object_size, &metadata_size));
ARROW_CHECK_OK(plasma::ReadDataReply(data, length, &object_id, &object_size,
&metadata_size));
process_data_request(loop, client_sock, object_id, object_size,
metadata_size, conn);
} break;
case MessageType_PlasmaFetchRequest: {
LOG_DEBUG("Processing fetch remote");
std::vector<ObjectID> object_ids_to_fetch;
std::vector<plasma::ObjectID> object_ids_to_fetch;
/* TODO(pcm): process_fetch_requests allocates an array of num_objects
* object_ids too so these should be shared in the future. */
ARROW_CHECK_OK(ReadFetchRequest(data, object_ids_to_fetch));
ARROW_CHECK_OK(plasma::ReadFetchRequest(data, length, object_ids_to_fetch));
process_fetch_requests(conn, object_ids_to_fetch.size(),
object_ids_to_fetch.data());
} break;
case MessageType_PlasmaWaitRequest: {
LOG_DEBUG("Processing wait");
ObjectRequestMap object_requests;
plasma::ObjectRequestMap object_requests;
int64_t timeout_ms;
int num_ready_objects;
ARROW_CHECK_OK(ReadWaitRequest(data, object_requests, &timeout_ms,
&num_ready_objects));
ARROW_CHECK_OK(plasma::ReadWaitRequest(data, length, object_requests,
&timeout_ms, &num_ready_objects));
process_wait_request(conn, std::move(object_requests), timeout_ms,
num_ready_objects);
} break;
case MessageType_PlasmaStatusRequest: {
LOG_DEBUG("Processing status");
ObjectID object_id;
ARROW_CHECK_OK(ReadStatusRequest(data, &object_id, 1));
plasma::ObjectID object_id;
ARROW_CHECK_OK(plasma::ReadStatusRequest(data, length, &object_id, 1));
process_status_request(conn, object_id);
} break;
case DISCONNECT_CLIENT: {
-194
View File
@@ -1,194 +0,0 @@
#ifndef PLASMA_PROTOCOL_H
#define PLASMA_PROTOCOL_H
#include "status.h"
#include "format/plasma_generated.h"
#include "plasma.h"
using arrow::Status;
/* Plasma receive message. */
Status PlasmaReceive(int sock,
int64_t message_type,
std::vector<uint8_t> &buffer);
/* Plasma Create message functions. */
Status SendCreateRequest(int sock,
ObjectID object_id,
int64_t data_size,
int64_t metadata_size);
Status ReadCreateRequest(uint8_t *data,
ObjectID *object_id,
int64_t *data_size,
int64_t *metadata_size);
Status SendCreateReply(int sock,
ObjectID object_id,
PlasmaObject *object,
int error);
Status ReadCreateReply(uint8_t *data,
ObjectID *object_id,
PlasmaObject *object);
/* Plasma Seal message functions. */
Status SendSealRequest(int sock, ObjectID object_id, unsigned char *digest);
Status ReadSealRequest(uint8_t *data,
ObjectID *object_id,
unsigned char *digest);
Status SendSealReply(int sock, ObjectID object_id, int error);
Status ReadSealReply(uint8_t *data, ObjectID *object_id);
/* Plasma Get message functions. */
Status SendGetRequest(int sock,
ObjectID object_ids[],
int64_t num_objects,
int64_t timeout_ms);
Status ReadGetRequest(uint8_t *data,
std::vector<ObjectID> &object_ids,
int64_t *timeout_ms);
Status SendGetReply(
int sock,
ObjectID object_ids[],
std::unordered_map<ObjectID, PlasmaObject, UniqueIDHasher> &plasma_objects,
int64_t num_objects);
Status ReadGetReply(uint8_t *data,
ObjectID object_ids[],
PlasmaObject plasma_objects[],
int64_t num_objects);
/* Plasma Release message functions. */
Status SendReleaseRequest(int sock, ObjectID object_id);
Status ReadReleaseRequest(uint8_t *data, ObjectID *object_id);
Status SendReleaseReply(int sock, ObjectID object_id, int error);
Status ReadReleaseReply(uint8_t *data, ObjectID *object_id);
/* Plasma Delete message functions. */
Status SendDeleteRequest(int sock, ObjectID object_id);
Status ReadDeleteRequest(uint8_t *data, ObjectID *object_id);
Status SendDeleteReply(int sock, ObjectID object_id, int error);
Status ReadDeleteReply(uint8_t *data, ObjectID *object_id);
/* Satus messages. */
Status SendStatusRequest(int sock, ObjectID object_ids[], int64_t num_objects);
Status ReadStatusRequest(uint8_t *data,
ObjectID object_ids[],
int64_t num_objects);
Status SendStatusReply(int sock,
ObjectID object_ids[],
int object_status[],
int64_t num_objects);
int64_t ReadStatusReply_num_objects(uint8_t *data);
Status ReadStatusReply(uint8_t *data,
ObjectID object_ids[],
int object_status[],
int64_t num_objects);
/* Plasma Constains message functions. */
Status SendContainsRequest(int sock, ObjectID object_id);
Status ReadContainsRequest(uint8_t *data, ObjectID *object_id);
Status SendContainsReply(int sock, ObjectID object_id, int has_object);
Status ReadContainsReply(uint8_t *data, ObjectID *object_id, int *has_object);
/* Plasma Connect message functions. */
Status SendConnectRequest(int sock);
Status ReadConnectRequest(uint8_t *data);
Status SendConnectReply(int sock, int64_t memory_capacity);
Status ReadConnectReply(uint8_t *data, int64_t *memory_capacity);
/* Plasma Evict message functions (no reply so far). */
Status SendEvictRequest(int sock, int64_t num_bytes);
Status ReadEvictRequest(uint8_t *data, int64_t *num_bytes);
Status SendEvictReply(int sock, int64_t num_bytes);
Status ReadEvictReply(uint8_t *data, int64_t &num_bytes);
/* Plasma Fetch Remote message functions. */
Status SendFetchRequest(int sock, ObjectID object_ids[], int64_t num_objects);
Status ReadFetchRequest(uint8_t *data, std::vector<ObjectID> &object_ids);
/* Plasma Wait message functions. */
Status SendWaitRequest(int sock,
ObjectRequest object_requests[],
int num_requests,
int num_ready_objects,
int64_t timeout_ms);
Status ReadWaitRequest(uint8_t *data,
ObjectRequestMap &object_requests,
int64_t *timeout_ms,
int *num_ready_objects);
Status SendWaitReply(int sock,
const ObjectRequestMap &object_requests,
int num_ready_objects);
Status ReadWaitReply(uint8_t *data,
ObjectRequest object_requests[],
int *num_ready_objects);
/* Plasma Subscribe message functions. */
Status SendSubscribeRequest(int sock);
/* Data messages. */
Status SendDataRequest(int sock,
ObjectID object_id,
const char *address,
int port);
Status ReadDataRequest(uint8_t *data,
ObjectID *object_id,
char **address,
int *port);
Status SendDataReply(int sock,
ObjectID object_id,
int64_t object_size,
int64_t metadata_size);
Status ReadDataReply(uint8_t *data,
ObjectID *object_id,
int64_t *object_size,
int64_t *metadata_size);
#endif /* PLASMA_PROTOCOL */
-689
View File
@@ -1,689 +0,0 @@
// PLASMA STORE: This is a simple object store server process
//
// It accepts incoming client connections on a unix domain socket
// (name passed in via the -s option of the executable) and uses a
// single thread to serve the clients. Each client establishes a
// connection and can create objects, wait for objects and seal
// objects through that connection.
//
// It keeps a hash table that maps object_ids (which are 20 byte long,
// just enough to store and SHA1 hash) to memory mapped files.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/un.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <limits.h>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "plasma_common.h"
#include "plasma_store.h"
#include "format/common_generated.h"
#include "plasma_io.h"
#include "malloc.h"
extern "C" {
#include "fling.h"
void *dlmalloc(size_t);
void *dlmemalign(size_t alignment, size_t bytes);
void dlfree(void *);
size_t dlmalloc_set_footprint_limit(size_t bytes);
}
struct GetRequest {
GetRequest(Client *client, const std::vector<ObjectID> &object_ids);
/// The client that called get.
Client *client;
/// The ID of the timer that will time out and cause this wait to return to
/// the client if it hasn't already returned.
int64_t timer;
/// The object IDs involved in this request. This is used in the reply.
std::vector<ObjectID> object_ids;
/// The object information for the objects in this request. This is used in
/// the reply.
std::unordered_map<ObjectID, PlasmaObject, UniqueIDHasher> objects;
/// The minimum number of objects to wait for in this request.
int64_t num_objects_to_wait_for;
/// The number of object requests in this wait request that are already
/// satisfied.
int64_t num_satisfied;
};
GetRequest::GetRequest(Client *client, const std::vector<ObjectID> &object_ids)
: client(client),
timer(-1),
object_ids(object_ids.begin(), object_ids.end()),
objects(object_ids.size()),
num_satisfied(0) {
std::unordered_set<ObjectID, UniqueIDHasher> unique_ids(object_ids.begin(),
object_ids.end());
num_objects_to_wait_for = unique_ids.size();
}
Client::Client(int fd) : fd(fd) {}
PlasmaStore::PlasmaStore(EventLoop *loop, int64_t system_memory)
: loop_(loop), eviction_policy_(&store_info_) {
store_info_.memory_capacity = system_memory;
}
PlasmaStore::~PlasmaStore() {
for (const auto &element : pending_notifications_) {
auto object_notifications = element.second.object_notifications;
for (int i = 0; i < object_notifications.size(); ++i) {
uint8_t *notification = (uint8_t *) object_notifications.at(i);
uint8_t *data = notification;
free(data);
}
}
}
// If this client is not already using the object, add the client to the
// object's list of clients, otherwise do nothing.
void PlasmaStore::add_client_to_object_clients(ObjectTableEntry *entry,
Client *client) {
// Check if this client is already using the object.
if (entry->clients.find(client) != entry->clients.end()) {
return;
}
// If there are no other clients using this object, notify the eviction policy
// that the object is being used.
if (entry->clients.size() == 0) {
// Tell the eviction policy that this object is being used.
std::vector<ObjectID> objects_to_evict;
eviction_policy_.begin_object_access(entry->object_id, objects_to_evict);
delete_objects(objects_to_evict);
}
// Add the client pointer to the list of clients using this object.
entry->clients.insert(client);
}
// Create a new object buffer in the hash table.
int PlasmaStore::create_object(ObjectID object_id,
int64_t data_size,
int64_t metadata_size,
Client *client,
PlasmaObject *result) {
ARROW_LOG(DEBUG) << "creating object " << object_id.hex();
if (store_info_.objects.count(object_id) != 0) {
// There is already an object with the same ID in the Plasma Store, so
// ignore this requst.
return PlasmaError_ObjectExists;
}
// Try to evict objects until there is enough space.
uint8_t *pointer;
do {
// Allocate space for the new object. We use dlmemalign instead of dlmalloc
// in order to align the allocated region to a 64-byte boundary. This is not
// strictly necessary, but it is an optimization that could speed up the
// computation of a hash of the data (see compute_object_hash_parallel in
// plasma_client.cc). Note that even though this pointer is 64-byte aligned,
// it is not guaranteed that the corresponding pointer in the client will be
// 64-byte aligned, but in practice it often will be.
pointer = (uint8_t *) dlmemalign(BLOCK_SIZE, data_size + metadata_size);
if (pointer == NULL) {
// Tell the eviction policy how much space we need to create this object.
std::vector<ObjectID> objects_to_evict;
bool success = eviction_policy_.require_space(data_size + metadata_size,
objects_to_evict);
delete_objects(objects_to_evict);
// Return an error to the client if not enough space could be freed to
// create the object.
if (!success) {
return PlasmaError_OutOfMemory;
}
}
} while (pointer == NULL);
int fd;
int64_t map_size;
ptrdiff_t offset;
get_malloc_mapinfo(pointer, &fd, &map_size, &offset);
assert(fd != -1);
auto entry = std::unique_ptr<ObjectTableEntry>(new ObjectTableEntry());
entry->object_id = object_id;
entry->info.object_id = object_id.binary();
entry->info.data_size = data_size;
entry->info.metadata_size = metadata_size;
entry->pointer = pointer;
// TODO(pcm): Set the other fields.
entry->fd = fd;
entry->map_size = map_size;
entry->offset = offset;
entry->state = PLASMA_CREATED;
store_info_.objects[object_id] = std::move(entry);
result->handle.store_fd = fd;
result->handle.mmap_size = map_size;
result->data_offset = offset;
result->metadata_offset = offset + data_size;
result->data_size = data_size;
result->metadata_size = metadata_size;
// Notify the eviction policy that this object was created. This must be done
// immediately before the call to add_client_to_object_clients so that the
// eviction policy does not have an opportunity to evict the object.
eviction_policy_.object_created(object_id);
// Record that this client is using this object.
add_client_to_object_clients(store_info_.objects[object_id].get(), client);
return PlasmaError_OK;
}
void PlasmaObject_init(PlasmaObject *object, ObjectTableEntry *entry) {
DCHECK(object != NULL);
DCHECK(entry != NULL);
DCHECK(entry->state == PLASMA_SEALED);
object->handle.store_fd = entry->fd;
object->handle.mmap_size = entry->map_size;
object->data_offset = entry->offset;
object->metadata_offset = entry->offset + entry->info.data_size;
object->data_size = entry->info.data_size;
object->metadata_size = entry->info.metadata_size;
}
void PlasmaStore::return_from_get(GetRequest *get_req) {
// Send the get reply to the client.
Status s = SendGetReply(get_req->client->fd, &get_req->object_ids[0],
get_req->objects, get_req->object_ids.size());
warn_if_sigpipe(s.ok() ? 0 : -1, get_req->client->fd);
// If we successfully sent the get reply message to the client, then also send
// the file descriptors.
if (s.ok()) {
// Send all of the file descriptors for the present objects.
for (const auto &object_id : get_req->object_ids) {
PlasmaObject &object = get_req->objects[object_id];
// We use the data size to indicate whether the object is present or not.
if (object.data_size != -1) {
int error_code = send_fd(get_req->client->fd, object.handle.store_fd);
// If we failed to send the file descriptor, loop until we have sent it
// successfully. TODO(rkn): This is problematic for two reasons. First
// of all, sending the file descriptor should just succeed without any
// errors, but sometimes I see a "Message too long" error number.
// Second, looping like this allows a client to potentially block the
// plasma store event loop which should never happen.
while (error_code < 0) {
if (errno == EMSGSIZE) {
ARROW_LOG(WARNING) << "Failed to send file descriptor, retrying.";
error_code = send_fd(get_req->client->fd, object.handle.store_fd);
continue;
}
warn_if_sigpipe(error_code, get_req->client->fd);
break;
}
}
}
}
// Remove the get request from each of the relevant object_get_requests hash
// tables if it is present there. It should only be present there if the get
// request timed out.
for (ObjectID &object_id : get_req->object_ids) {
auto &get_requests = object_get_requests_[object_id];
// Erase get_req from the vector.
auto it = std::find(get_requests.begin(), get_requests.end(), get_req);
if (it != get_requests.end()) {
get_requests.erase(it);
}
}
// Remove the get request.
if (get_req->timer != -1) {
ARROW_CHECK(loop_->remove_timer(get_req->timer) == AE_OK);
}
delete get_req;
}
void PlasmaStore::update_object_get_requests(ObjectID object_id) {
std::vector<GetRequest *> &get_requests = object_get_requests_[object_id];
int index = 0;
int num_requests = get_requests.size();
for (int i = 0; i < num_requests; ++i) {
GetRequest *get_req = get_requests[index];
auto entry = get_object_table_entry(&store_info_, object_id);
ARROW_CHECK(entry != NULL);
PlasmaObject_init(&get_req->objects[object_id], entry);
get_req->num_satisfied += 1;
// Record the fact that this client will be using this object and will
// be responsible for releasing this object.
add_client_to_object_clients(entry, get_req->client);
// If this get request is done, reply to the client.
if (get_req->num_satisfied == get_req->num_objects_to_wait_for) {
return_from_get(get_req);
} else {
// The call to return_from_get will remove the current element in the
// array, so we only increment the counter in the else branch.
index += 1;
}
}
DCHECK(index == get_requests.size());
// Remove the array of get requests for this object, since no one should be
// waiting for this object anymore.
object_get_requests_.erase(object_id);
}
void PlasmaStore::process_get_request(Client *client,
const std::vector<ObjectID> &object_ids,
uint64_t timeout_ms) {
// Create a get request for this object.
GetRequest *get_req = new GetRequest(client, object_ids);
for (auto object_id : object_ids) {
// Check if this object is already present locally. If so, record that the
// object is being used and mark it as accounted for.
auto entry = get_object_table_entry(&store_info_, object_id);
if (entry && entry->state == PLASMA_SEALED) {
// Update the get request to take into account the present object.
PlasmaObject_init(&get_req->objects[object_id], entry);
get_req->num_satisfied += 1;
// If necessary, record that this client is using this object. In the case
// where entry == NULL, this will be called from seal_object.
add_client_to_object_clients(entry, client);
} else {
// Add a placeholder plasma object to the get request to indicate that the
// object is not present. This will be parsed by the client. We set the
// data size to -1 to indicate that the object is not present.
get_req->objects[object_id].data_size = -1;
// Add the get request to the relevant data structures.
object_get_requests_[object_id].push_back(get_req);
}
}
// If all of the objects are present already or if the timeout is 0, return to
// the client.
if (get_req->num_satisfied == get_req->num_objects_to_wait_for ||
timeout_ms == 0) {
return_from_get(get_req);
} else if (timeout_ms != -1) {
// Set a timer that will cause the get request to return to the client. Note
// that a timeout of -1 is used to indicate that no timer should be set.
get_req->timer =
loop_->add_timer(timeout_ms, [this, get_req](int64_t timer_id) {
return_from_get(get_req);
return kEventLoopTimerDone;
});
}
}
int PlasmaStore::remove_client_from_object_clients(ObjectTableEntry *entry,
Client *client) {
auto it = entry->clients.find(client);
if (it != entry->clients.end()) {
entry->clients.erase(it);
// If no more clients are using this object, notify the eviction policy
// that the object is no longer being used.
if (entry->clients.size() == 0) {
// Tell the eviction policy that this object is no longer being used.
std::vector<ObjectID> objects_to_evict;
eviction_policy_.end_object_access(entry->object_id, objects_to_evict);
delete_objects(objects_to_evict);
}
// Return 1 to indicate that the client was removed.
return 1;
} else {
// Return 0 to indicate that the client was not removed.
return 0;
}
}
void PlasmaStore::release_object(ObjectID object_id, Client *client) {
auto entry = get_object_table_entry(&store_info_, object_id);
ARROW_CHECK(entry != NULL);
// Remove the client from the object's array of clients.
ARROW_CHECK(remove_client_from_object_clients(entry, client) == 1);
}
// Check if an object is present.
int PlasmaStore::contains_object(ObjectID object_id) {
auto entry = get_object_table_entry(&store_info_, object_id);
return entry && (entry->state == PLASMA_SEALED) ? OBJECT_FOUND
: OBJECT_NOT_FOUND;
}
// Seal an object that has been created in the hash table.
void PlasmaStore::seal_object(ObjectID object_id, unsigned char digest[]) {
ARROW_LOG(DEBUG) << "sealing object " << object_id.hex();
auto entry = get_object_table_entry(&store_info_, object_id);
ARROW_CHECK(entry != NULL);
ARROW_CHECK(entry->state == PLASMA_CREATED);
// Set the state of object to SEALED.
entry->state = PLASMA_SEALED;
// Set the object digest.
entry->info.digest = std::string((char *) &digest[0], kDigestSize);
// Inform all subscribers that a new object has been sealed.
push_notification(&entry->info);
// Update all get requests that involve this object.
update_object_get_requests(object_id);
}
void PlasmaStore::delete_objects(const std::vector<ObjectID> &object_ids) {
for (const auto &object_id : object_ids) {
ARROW_LOG(DEBUG) << "deleting object " << object_id.hex();
auto entry = get_object_table_entry(&store_info_, object_id);
// TODO(rkn): This should probably not fail, but should instead throw an
// error. Maybe we should also support deleting objects that have been
// created but not sealed.
ARROW_CHECK(entry != NULL)
<< "To delete an object it must be in the object table.";
ARROW_CHECK(entry->state == PLASMA_SEALED)
<< "To delete an object it must have been sealed.";
ARROW_CHECK(entry->clients.size() == 0)
<< "To delete an object, there must be no clients currently using it.";
dlfree(entry->pointer);
store_info_.objects.erase(object_id);
// Inform all subscribers that the object has been deleted.
ObjectInfoT notification;
notification.object_id = object_id.binary();
notification.is_deletion = true;
push_notification(&notification);
}
}
void PlasmaStore::connect_client(int listener_sock) {
int client_fd = AcceptClient(listener_sock);
// This is freed in disconnect_client.
Client *client = new Client(client_fd);
// Add a callback to handle events on this socket.
// TODO(pcm): Check return value.
loop_->add_file_event(client_fd, kEventLoopRead, [this, client](int events) {
process_message(client);
});
ARROW_LOG(DEBUG) << "New connection with fd " << client_fd;
}
void PlasmaStore::disconnect_client(Client *client) {
ARROW_CHECK(client != NULL);
ARROW_CHECK(client->fd > 0);
loop_->remove_file_event(client->fd);
// Close the socket.
close(client->fd);
ARROW_LOG(INFO) << "Disconnecting client on fd " << client->fd;
// If this client was using any objects, remove it from the appropriate
// lists.
for (const auto &entry : store_info_.objects) {
remove_client_from_object_clients(entry.second.get(), client);
}
// Note, the store may still attempt to send a message to the disconnected
// client (for example, when an object ID that the client was waiting for
// is ready). In these cases, the attempt to send the message will fail, but
// the store should just ignore the failure.
delete client;
}
/// Send notifications about sealed objects to the subscribers. This is called
/// in seal_object. If the socket's send buffer is full, the notification will
/// be
/// buffered, and this will be called again when the send buffer has room.
///
/// @param client The client to send the notification to.
/// @return Void.
void PlasmaStore::send_notifications(int client_fd) {
auto it = pending_notifications_.find(client_fd);
int num_processed = 0;
bool closed = false;
// Loop over the array of pending notifications and send as many of them as
// possible.
for (int i = 0; i < it->second.object_notifications.size(); ++i) {
uint8_t *notification = (uint8_t *) it->second.object_notifications.at(i);
// Decode the length, which is the first bytes of the message.
int64_t size = *((int64_t *) notification);
// Attempt to send a notification about this object ID.
int nbytes = send(client_fd, notification, sizeof(int64_t) + size, 0);
if (nbytes >= 0) {
ARROW_CHECK(nbytes == sizeof(int64_t) + size);
} else if (nbytes == -1 &&
(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) {
ARROW_LOG(DEBUG)
<< "The socket's send buffer is full, so we are caching this "
"notification and will send it later.";
// Add a callback to the event loop to send queued notifications whenever
// there is room in the socket's send buffer. Callbacks can be added
// more than once here and will be overwritten. The callback is removed
// at the end of the method.
// TODO(pcm): Introduce status codes and check in case the file descriptor
// is added twice.
loop_->add_file_event(
client_fd, kEventLoopWrite,
[this, client_fd](int events) { send_notifications(client_fd); });
break;
} else {
ARROW_LOG(WARNING) << "Failed to send notification to client on fd "
<< client_fd;
if (errno == EPIPE) {
closed = true;
break;
}
}
num_processed += 1;
// The corresponding malloc happened in create_object_info_buffer
// within push_notification.
free(notification);
}
// Remove the sent notifications from the array.
it->second.object_notifications.erase(
it->second.object_notifications.begin(),
it->second.object_notifications.begin() + num_processed);
// Stop sending notifications if the pipe was broken.
if (closed) {
close(client_fd);
pending_notifications_.erase(client_fd);
}
// If we have sent all notifications, remove the fd from the event loop.
if (it->second.object_notifications.empty()) {
loop_->remove_file_event(client_fd);
}
}
void PlasmaStore::push_notification(ObjectInfoT *object_info) {
for (auto &element : pending_notifications_) {
uint8_t *notification = create_object_info_buffer(object_info);
element.second.object_notifications.push_back(notification);
send_notifications(element.first);
// The notification gets freed in send_notifications when the notification
// is sent over the socket.
}
}
// Subscribe to notifications about sealed objects.
void PlasmaStore::subscribe_to_updates(Client *client) {
ARROW_LOG(DEBUG) << "subscribing to updates on fd " << client->fd;
// TODO(rkn): The store could block here if the client doesn't send a file
// descriptor.
int fd = recv_fd(client->fd);
if (fd < 0) {
// This may mean that the client died before sending the file descriptor.
ARROW_LOG(WARNING) << "Failed to receive file descriptor from client on fd "
<< client->fd << ".";
return;
}
// Create a new array to buffer notifications that can't be sent to the
// subscriber yet because the socket send buffer is full. TODO(rkn): the queue
// never gets freed.
NotificationQueue &queue = pending_notifications_[fd];
// Push notifications to the new subscriber about existing objects.
for (const auto &entry : store_info_.objects) {
push_notification(&entry.second->info);
}
send_notifications(fd);
}
Status PlasmaStore::process_message(Client *client) {
int64_t type;
Status s = ReadMessage(client->fd, &type, input_buffer_);
ARROW_CHECK(s.ok() || s.IsIOError());
uint8_t *input = input_buffer_.data();
ObjectID object_id;
PlasmaObject object;
// TODO(pcm): Get rid of the following.
memset(&object, 0, sizeof(object));
// Process the different types of requests.
switch (type) {
case MessageType_PlasmaCreateRequest: {
int64_t data_size;
int64_t metadata_size;
RETURN_NOT_OK(
ReadCreateRequest(input, &object_id, &data_size, &metadata_size));
int error_code =
create_object(object_id, data_size, metadata_size, client, &object);
HANDLE_SIGPIPE(SendCreateReply(client->fd, object_id, &object, error_code),
client->fd);
if (error_code == PlasmaError_OK) {
warn_if_sigpipe(send_fd(client->fd, object.handle.store_fd), client->fd);
}
} break;
case MessageType_PlasmaGetRequest: {
std::vector<ObjectID> object_ids_to_get;
int64_t timeout_ms;
RETURN_NOT_OK(ReadGetRequest(input, object_ids_to_get, &timeout_ms));
process_get_request(client, object_ids_to_get, timeout_ms);
} break;
case MessageType_PlasmaReleaseRequest:
RETURN_NOT_OK(ReadReleaseRequest(input, &object_id));
release_object(object_id, client);
break;
case MessageType_PlasmaContainsRequest:
RETURN_NOT_OK(ReadContainsRequest(input, &object_id));
if (contains_object(object_id) == OBJECT_FOUND) {
HANDLE_SIGPIPE(SendContainsReply(client->fd, object_id, 1), client->fd);
} else {
HANDLE_SIGPIPE(SendContainsReply(client->fd, object_id, 0), client->fd);
}
break;
case MessageType_PlasmaSealRequest: {
unsigned char digest[kDigestSize];
RETURN_NOT_OK(ReadSealRequest(input, &object_id, &digest[0]));
seal_object(object_id, &digest[0]);
} break;
case MessageType_PlasmaEvictRequest: {
// This code path should only be used for testing.
int64_t num_bytes;
RETURN_NOT_OK(ReadEvictRequest(input, &num_bytes));
std::vector<ObjectID> objects_to_evict;
int64_t num_bytes_evicted =
eviction_policy_.choose_objects_to_evict(num_bytes, objects_to_evict);
delete_objects(objects_to_evict);
HANDLE_SIGPIPE(SendEvictReply(client->fd, num_bytes_evicted), client->fd);
} break;
case MessageType_PlasmaSubscribeRequest:
subscribe_to_updates(client);
break;
case MessageType_PlasmaConnectRequest: {
HANDLE_SIGPIPE(SendConnectReply(client->fd, store_info_.memory_capacity),
client->fd);
} break;
case DISCONNECT_CLIENT:
ARROW_LOG(DEBUG) << "Disconnecting client on fd " << client->fd;
disconnect_client(client);
break;
default:
// This code should be unreachable.
ARROW_CHECK(0);
}
return Status::OK();
}
// Report "success" to valgrind.
void signal_handler(int signal) {
if (signal == SIGTERM) {
exit(0);
}
}
void start_server(char *socket_name, int64_t system_memory) {
// Ignore SIGPIPE signals. If we don't do this, then when we attempt to write
// to a client that has already died, the store could die.
signal(SIGPIPE, SIG_IGN);
// Create the event loop.
EventLoop loop;
PlasmaStore store(&loop, system_memory);
int socket = bind_ipc_sock(socket_name, true);
ARROW_CHECK(socket >= 0);
// TODO(pcm): Check return value.
loop.add_file_event(socket, kEventLoopRead, [&store, socket](int events) {
store.connect_client(socket);
});
loop.run();
}
int main(int argc, char *argv[]) {
signal(SIGTERM, signal_handler);
char *socket_name = NULL;
int64_t system_memory = -1;
int c;
while ((c = getopt(argc, argv, "s:m:")) != -1) {
switch (c) {
case 's':
socket_name = optarg;
break;
case 'm': {
char extra;
int scanned = sscanf(optarg, "%" SCNd64 "%c", &system_memory, &extra);
ARROW_CHECK(scanned == 1);
ARROW_LOG(INFO) << "Allowing the Plasma store to use up to "
<< ((double) system_memory) / 1000000000
<< "GB of memory.";
break;
}
default:
exit(-1);
}
}
if (!socket_name) {
ARROW_LOG(FATAL)
<< "please specify socket for incoming connections with -s switch";
}
if (system_memory == -1) {
ARROW_LOG(FATAL)
<< "please specify the amount of system memory with -m switch";
}
#ifdef __linux__
// On Linux, check that the amount of memory available in /dev/shm is large
// enough to accommodate the request. If it isn't, then fail.
int shm_fd = open("/dev/shm", O_RDONLY);
struct statvfs shm_vfs_stats;
fstatvfs(shm_fd, &shm_vfs_stats);
// The value shm_vfs_stats.f_bsize is the block size, and the value
// shm_vfs_stats.f_bavail is the number of available blocks.
int64_t shm_mem_avail = shm_vfs_stats.f_bsize * shm_vfs_stats.f_bavail;
close(shm_fd);
if (system_memory > shm_mem_avail) {
ARROW_LOG(FATAL)
<< "System memory request exceeds memory available in /dev/shm. The "
"request is for "
<< system_memory << " bytes, and the amount available is "
<< shm_mem_avail
<< " bytes. You may be able to free up space by deleting files in "
"/dev/shm. If you are inside a Docker container, you may need to "
"pass "
"an argument with the flag '--shm-size' to 'docker run'.";
}
#endif
// Make it so dlmalloc fails if we try to request more memory than is
// available.
dlmalloc_set_footprint_limit((size_t) system_memory);
ARROW_LOG(DEBUG) << "starting server listening on " << socket_name;
start_server(socket_name, system_memory);
}
-154
View File
@@ -1,154 +0,0 @@
#ifndef PLASMA_STORE_H
#define PLASMA_STORE_H
#include "eviction_policy.h"
#include "plasma.h"
#include "plasma_common.h"
#include "plasma_events.h"
#include "plasma_protocol.h"
class GetRequest;
struct NotificationQueue {
/// The object notifications for clients. We notify the client about the
/// objects in the order that the objects were sealed or deleted.
std::deque<uint8_t *> object_notifications;
};
/// Contains all information that is associated with a Plasma store client.
struct Client {
Client(int fd);
/// The file descriptor used to communicate with the client.
int fd;
};
class PlasmaStore {
public:
PlasmaStore(EventLoop *loop, int64_t system_memory);
~PlasmaStore();
/// Create a new object. The client must do a call to release_object to tell
/// the store when it is done with the object.
///
/// @param object_id Object ID of the object to be created.
/// @param data_size Size in bytes of the object to be created.
/// @param metadata_size Size in bytes of the object metadata.
/// @return One of the following error codes:
/// - PlasmaError_OK, if the object was created successfully.
/// - PlasmaError_ObjectExists, if an object with this ID is already
/// present in the store. In this case, the client should not call
/// plasma_release.
/// - PlasmaError_OutOfMemory, if the store is out of memory and
/// cannot create the object. In this case, the client should not call
/// plasma_release.
int create_object(ObjectID object_id,
int64_t data_size,
int64_t metadata_size,
Client *client,
PlasmaObject *result);
/// Delete objects that have been created in the hash table. This should only
/// be called on objects that are returned by the eviction policy to evict.
///
/// @param object_ids Object IDs of the objects to be deleted.
/// @return Void.
void delete_objects(const std::vector<ObjectID> &object_ids);
/// Process a get request from a client. This method assumes that we will
/// eventually have these objects sealed. If one of the objects has not yet
/// been sealed, the client that requested the object will be notified when it
/// is sealed.
///
/// For each object, the client must do a call to release_object to tell the
/// store when it is done with the object.
///
/// @param client The client making this request.
/// @param object_ids Object IDs of the objects to be gotten.
/// @param timeout_ms The timeout for the get request in milliseconds.
/// @return Void.
void process_get_request(Client *client,
const std::vector<ObjectID> &object_ids,
uint64_t timeout_ms);
/// Seal an object. The object is now immutable and can be accessed with get.
///
/// @param object_id Object ID of the object to be sealed.
/// @param digest The digest of the object. This is used to tell if two
/// objects
/// with the same object ID are the same.
/// @return Void.
void seal_object(ObjectID object_id, unsigned char digest[]);
/// Check if the plasma store contains an object:
///
/// @param object_id Object ID that will be checked.
/// @return OBJECT_FOUND if the object is in the store, OBJECT_NOT_FOUND if
/// not
int contains_object(ObjectID object_id);
/// Record the fact that a particular client is no longer using an object.
///
/// @param object_id The object ID of the object that is being released.
/// @param client The client making this request.
/// @param Void.
void release_object(ObjectID object_id, Client *client);
/// Subscribe a file descriptor to updates about new sealed objects.
///
/// @param client The client making this request.
/// @return Void.
void subscribe_to_updates(Client *client);
/// Connect a new client to the PlasmaStore.
///
/// @param listener_sock The socket that is listening to incoming connections.
/// @return Void.
void connect_client(int listener_sock);
/// Disconnect a client from the PlasmaStore.
///
/// @param client The client that is disconnected.
/// @return Void.
void disconnect_client(Client *client);
void send_notifications(int client_fd);
Status process_message(Client *client);
private:
void push_notification(ObjectInfoT *object_notification);
void add_client_to_object_clients(ObjectTableEntry *entry, Client *client);
void return_from_get(GetRequest *get_req);
void update_object_get_requests(ObjectID object_id);
int remove_client_from_object_clients(ObjectTableEntry *entry,
Client *client);
/// Event loop of the plasma store.
EventLoop *loop_;
/// The plasma store information, including the object tables, that is exposed
/// to the eviction policy.
PlasmaStoreInfo store_info_;
/// The state that is managed by the eviction policy.
EvictionPolicy eviction_policy_;
/// Input buffer. This is allocated only once to avoid mallocs for every
/// call to process_message.
std::vector<uint8_t> input_buffer_;
/// A hash table mapping object IDs to a vector of the get requests that are
/// waiting for the object to arrive.
std::unordered_map<ObjectID, std::vector<GetRequest *>, UniqueIDHasher>
object_get_requests_;
/// The pending notifications that have not been sent to subscribers because
/// the socket send buffers were full. This is a hash table from client file
/// descriptor to an array of object_ids to send to that client.
/// TODO(pcm): Consider putting this into the Client data structure and
/// reorganize the code slightly.
std::unordered_map<int, NotificationQueue> pending_notifications_;
};
#endif // PLASMA_STORE_H
-90
View File
@@ -1,90 +0,0 @@
// 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.
#include "status.h"
#include <assert.h>
namespace arrow {
Status::Status(StatusCode code, const std::string &msg, int16_t posix_code) {
assert(code != StatusCode::OK);
const uint32_t size = static_cast<uint32_t>(msg.size());
char *result = new char[size + 7];
memcpy(result, &size, sizeof(size));
result[4] = static_cast<char>(code);
memcpy(result + 5, &posix_code, sizeof(posix_code));
memcpy(result + 7, msg.c_str(), msg.size());
state_ = result;
}
const char *Status::CopyState(const char *state) {
uint32_t size;
memcpy(&size, state, sizeof(size));
char *result = new char[size + 7];
memcpy(result, state, size + 7);
return result;
}
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;
default:
type = "Unknown";
break;
}
return std::string(type);
}
std::string Status::ToString() const {
std::string result(CodeAsString());
if (state_ == NULL) {
return result;
}
result.append(": ");
uint32_t length;
memcpy(&length, state_, sizeof(length));
result.append(reinterpret_cast<const char *>(state_ + 7), length);
return result;
}
} // namespace arrow
-226
View File
@@ -1,226 +0,0 @@
// 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 Kudu github.com/cloudera/kudu
#ifndef ARROW_STATUS_H_
#define ARROW_STATUS_H_
#include <cstdint>
#include <cstring>
#include <string>
// Return the given status if it is not OK.
#define ARROW_RETURN_NOT_OK(s) \
do { \
::arrow::Status _s = (s); \
if (!_s.ok()) { \
return _s; \
} \
} while (0);
// Return the given status if it is not OK, but first clone it and
// prepend the given message.
#define ARROW_RETURN_NOT_OK_PREPEND(s, msg) \
do { \
::arrow::Status _s = (s); \
if (::gutil::PREDICT_FALSE(!_s.ok())) \
return _s.CloneAndPrepend(msg); \
} while (0);
// Return 'to_return' if 'to_call' returns a bad status.
// The substitution for 'to_return' may reference the variable
// 's' for the bad status.
#define ARROW_RETURN_NOT_OK_RET(to_call, to_return) \
do { \
::arrow::Status s = (to_call); \
if (::gutil::PREDICT_FALSE(!s.ok())) \
return (to_return); \
} while (0);
// If 'to_call' returns a bad status, CHECK immediately with a logged message
// of 'msg' followed by the status.
#define ARROW_CHECK_OK_PREPEND(to_call, msg) \
do { \
::arrow::Status _s = (to_call); \
ARROW_CHECK(_s.ok()) << (msg) << ": " << _s.ToString(); \
} while (0);
// If the status is bad, CHECK immediately, appending the status to the
// logged message.
#define ARROW_CHECK_OK(s) ARROW_CHECK_OK_PREPEND(s, "Bad status")
namespace arrow {
#define RETURN_NOT_OK(s) \
do { \
Status _s = (s); \
if (!_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,
PlasmaObjectExists = 20,
PlasmaObjectNonexistent = 21,
PlasmaStoreFull = 22
};
class Status {
public:
// Create a success status.
Status() : state_(NULL) {}
~Status() { delete[] state_; }
Status(StatusCode code, const std::string &msg) : Status(code, msg, -1) {}
// 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, int16_t posix_code = -1) {
return Status(StatusCode::OutOfMemory, msg, posix_code);
}
static Status KeyError(const std::string &msg) {
return Status(StatusCode::KeyError, msg, -1);
}
static Status TypeError(const std::string &msg) {
return Status(StatusCode::TypeError, msg, -1);
}
static Status UnknownError(const std::string &msg) {
return Status(StatusCode::UnknownError, msg, -1);
}
static Status NotImplemented(const std::string &msg) {
return Status(StatusCode::NotImplemented, msg, -1);
}
static Status Invalid(const std::string &msg) {
return Status(StatusCode::Invalid, msg, -1);
}
static Status IOError(const std::string &msg) {
return Status(StatusCode::IOError, msg, -1);
}
static Status PlasmaObjectExists(const std::string &msg) {
return Status(StatusCode::PlasmaObjectExists, msg, -1);
}
static Status PlasmaObjectNonexistent(const std::string &msg) {
return Status(StatusCode::PlasmaObjectNonexistent, msg, -1);
}
static Status PlasmaStoreFull(const std::string &msg) {
return Status(StatusCode::PlasmaStoreFull, msg, -1);
}
// 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; }
// An object with this object ID already exists in the plasma store.
bool IsPlasmaObjectExists() const {
return code() == StatusCode::PlasmaObjectExists;
}
// An object was requested that doesn't exist in the plasma store.
bool IsPlasmaObjectNonexistent() const {
return code() == StatusCode::PlasmaObjectNonexistent;
}
// An object is too large to fit into the plasma store.
bool IsPlasmaStoreFull() const {
return code() == StatusCode::PlasmaStoreFull;
}
// 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;
// Get the POSIX code associated with this Status, or -1 if there is none.
int16_t posix_code() const;
StatusCode code() const {
return ((state_ == NULL) ? StatusCode::OK
: static_cast<StatusCode>(state_[4]));
}
std::string message() const {
uint32_t length;
memcpy(&length, state_, sizeof(length));
std::string msg;
msg.append((state_ + 7), length);
return msg;
}
private:
// OK status has a NULL state_. Otherwise, state_ is a new[] array
// of the following form:
// state_[0..3] == length of message
// state_[4] == code
// state_[5..6] == posix_code
// state_[7..] == message
const char *state_;
Status(StatusCode code, const std::string &msg, int16_t posix_code);
static const char *CopyState(const char *s);
};
inline Status::Status(const Status &s) {
state_ = (s.state_ == NULL) ? NULL : CopyState(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_) {
delete[] state_;
state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_);
}
}
} // namespace arrow
#endif // ARROW_STATUS_H_
+12 -10
View File
@@ -4,10 +4,12 @@
#include <unistd.h>
#include <sys/time.h>
#include "plasma_common.h"
#include "plasma.h"
#include "plasma_protocol.h"
#include "plasma_client.h"
#include "plasma/common.h"
#include "plasma/plasma.h"
#include "plasma/protocol.h"
#include "plasma/client.h"
using namespace plasma;
SUITE(plasma_client_tests);
@@ -177,7 +179,7 @@ TEST plasma_wait_for_objects_tests(void) {
gettimeofday(&start, NULL);
int n;
ARROW_CHECK_OK(client1.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 0);
gettimeofday(&end, NULL);
float diff_ms = (end.tv_sec - start.tv_sec);
@@ -195,7 +197,7 @@ TEST plasma_wait_for_objects_tests(void) {
ARROW_CHECK_OK(client1.Seal(oid1));
ARROW_CHECK_OK(client1.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 1);
/* Create and insert an object in client2. */
@@ -204,21 +206,21 @@ TEST plasma_wait_for_objects_tests(void) {
ARROW_CHECK_OK(client2.Seal(oid2));
ARROW_CHECK_OK(client1.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 2);
ARROW_CHECK_OK(client2.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 2);
obj_requests[0].type = PLASMA_QUERY_LOCAL;
obj_requests[1].type = PLASMA_QUERY_LOCAL;
ARROW_CHECK_OK(client1.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 1);
ARROW_CHECK_OK(client2.Wait(NUM_OBJ_REQUEST, obj_requests, NUM_OBJ_REQUEST,
WAIT_TIMEOUT_MS, n));
WAIT_TIMEOUT_MS, &n));
ASSERT(n == 1);
ARROW_CHECK_OK(client1.Disconnect());
+20 -20
View File
@@ -13,10 +13,10 @@
#include "io.h"
#include "utstring.h"
#include "plasma.h"
#include "plasma_client.h"
#include "plasma_manager.h"
#include "plasma_protocol.h"
#include "plasma/plasma.h"
#include "plasma/client.h"
#include "../plasma_manager.h"
#include "plasma/protocol.h"
SUITE(plasma_manager_tests);
@@ -53,7 +53,7 @@ typedef struct {
ClientConnection *read_conn;
/* Connect a new client to the local plasma manager and mock a request to an
* object. */
PlasmaClient *plasma_client;
plasma::PlasmaClient *plasma_client;
ClientConnection *client_conn;
} plasma_mock;
@@ -85,7 +85,7 @@ plasma_mock *init_plasma_mock(plasma_mock *remote_mock) {
}
/* Connect a new client to the local plasma manager and mock a request to an
* object. */
mock->plasma_client = new PlasmaClient();
mock->plasma_client = new plasma::PlasmaClient();
ARROW_CHECK_OK(mock->plasma_client->Connect(
plasma_store_socket_name, utstring_body(manager_socket_name), 0));
wait_for_pollin(mock->manager_local_fd);
@@ -130,13 +130,13 @@ TEST request_transfer_test(void) {
event_loop_run(local_mock->loop);
int read_fd = get_client_sock(remote_mock->read_conn);
std::vector<uint8_t> request_data;
ARROW_CHECK_OK(
PlasmaReceive(read_fd, MessageType_PlasmaDataRequest, request_data));
ObjectID object_id2;
ARROW_CHECK_OK(plasma::PlasmaReceive(read_fd, MessageType_PlasmaDataRequest,
&request_data));
plasma::ObjectID object_id2;
char *address;
int port;
ARROW_CHECK_OK(
ReadDataRequest(request_data.data(), &object_id2, &address, &port));
ARROW_CHECK_OK(plasma::ReadDataRequest(
request_data.data(), request_data.size(), &object_id2, &address, &port));
ASSERT(ObjectID_equal(object_id, object_id2));
free(address);
/* Clean up. */
@@ -184,13 +184,13 @@ TEST request_transfer_retry_test(void) {
int read_fd = get_client_sock(remote_mock2->read_conn);
std::vector<uint8_t> request_data;
ARROW_CHECK_OK(
PlasmaReceive(read_fd, MessageType_PlasmaDataRequest, request_data));
ObjectID object_id2;
ARROW_CHECK_OK(plasma::PlasmaReceive(read_fd, MessageType_PlasmaDataRequest,
&request_data));
plasma::ObjectID object_id2;
char *address;
int port;
ARROW_CHECK_OK(
ReadDataRequest(request_data.data(), &object_id2, &address, &port));
ARROW_CHECK_OK(plasma::ReadDataRequest(
request_data.data(), request_data.size(), &object_id2, &address, &port));
free(address);
ASSERT(ObjectID_equal(object_id, object_id2));
/* Clean up. */
@@ -271,24 +271,24 @@ TEST object_notifications_test(void) {
ASSERT(!is_local);
/* Check that the object is local after receiving an object notification. */
uint8_t *notification = create_object_info_buffer(&info);
uint8_t *notification = plasma::create_object_info_buffer(&info);
int64_t size = *((int64_t *) notification);
send(fd[1], notification, sizeof(int64_t) + size, 0);
process_object_notification(local_mock->loop, fd[0], local_mock->state, 0);
is_local = is_object_local(local_mock->state, object_id);
ASSERT(is_local);
free(notification);
delete[] notification;
/* Check that the object is not local after receiving a notification about
* the object deletion. */
info.is_deletion = true;
notification = create_object_info_buffer(&info);
notification = plasma::create_object_info_buffer(&info);
size = *((int64_t *) notification);
send(fd[1], notification, sizeof(int64_t) + size, 0);
process_object_notification(local_mock->loop, fd[0], local_mock->state, 0);
is_local = is_object_local(local_mock->state, object_id);
ASSERT(!is_local);
free(notification);
delete[] notification;
/* Clean up. */
close(fd[0]);
-1
View File
@@ -7,7 +7,6 @@ set -e
sleep 1
./src/plasma/manager_tests
killall plasma_store
./src/plasma/serialization_tests
# Start the Redis shards.
./src/common/thirdparty/redis/src/redis-server --loglevel warning --loadmodule ./src/common/redis_module/libray_redis_module.so --port 6379 &
-1
View File
@@ -7,4 +7,3 @@ set -e
sleep 1
valgrind --leak-check=full --error-exitcode=1 ./src/plasma/manager_tests
killall plasma_store
valgrind --leak-check=full --error-exitcode=1 ./src/plasma/serialization_tests
-439
View File
@@ -1,439 +0,0 @@
#include "greatest.h"
#include <sys/types.h>
#include <unistd.h>
#include "plasma_common.h"
#include "plasma.h"
#include "plasma_io.h"
#include "plasma_protocol.h"
SUITE(plasma_serialization_tests);
/**
* Create a temporary file. Needs to be closed by the caller.
*
* @return File descriptor of the file.
*/
int create_temp_file(void) {
static char temp[] = "/tmp/tempfileXXXXXX";
char file_name[32];
strncpy(file_name, temp, 32);
return mkstemp(file_name);
}
/**
* Seek to the beginning of a file and read a message from it.
*
* @param fd File descriptor of the file.
* @param message type Message type that we expect in the file.
*
* @return Pointer to the content of the message. Needs to be freed by the
* caller.
*/
std::vector<uint8_t> read_message_from_file(int fd, int message_type) {
/* Go to the beginning of the file. */
lseek(fd, 0, SEEK_SET);
int64_t type;
std::vector<uint8_t> data;
ARROW_CHECK_OK(ReadMessage(fd, &type, data));
ARROW_CHECK(type == message_type);
return data;
}
PlasmaObject random_plasma_object(void) {
int random = rand();
PlasmaObject object;
memset(&object, 0, sizeof(object));
object.handle.store_fd = random + 7;
object.handle.mmap_size = random + 42;
object.data_offset = random + 1;
object.metadata_offset = random + 2;
object.data_size = random + 3;
object.metadata_size = random + 4;
return object;
}
TEST plasma_create_request_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
int64_t data_size1 = 42;
int64_t metadata_size1 = 11;
ARROW_CHECK_OK(SendCreateRequest(fd, object_id1, data_size1, metadata_size1));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaCreateRequest);
ObjectID object_id2;
int64_t data_size2;
int64_t metadata_size2;
ARROW_CHECK_OK(ReadCreateRequest(data.data(), &object_id2, &data_size2,
&metadata_size2));
ASSERT_EQ(data_size1, data_size2);
ASSERT_EQ(metadata_size1, metadata_size2);
ASSERT(object_id1 == object_id2);
close(fd);
PASS();
}
TEST plasma_create_reply_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
PlasmaObject object1 = random_plasma_object();
ARROW_CHECK_OK(SendCreateReply(fd, object_id1, &object1, 0));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaCreateReply);
ObjectID object_id2;
PlasmaObject object2;
memset(&object2, 0, sizeof(object2));
ARROW_CHECK_OK(ReadCreateReply(data.data(), &object_id2, &object2));
ASSERT(object_id1 == object_id2);
ASSERT(memcmp(&object1, &object2, sizeof(object1)) == 0);
close(fd);
PASS();
}
TEST plasma_seal_request_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
unsigned char digest1[kDigestSize];
memset(&digest1[0], 7, kDigestSize);
ARROW_CHECK_OK(SendSealRequest(fd, object_id1, &digest1[0]));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaSealRequest);
ObjectID object_id2;
unsigned char digest2[kDigestSize];
ARROW_CHECK_OK(ReadSealRequest(data.data(), &object_id2, &digest2[0]));
ASSERT(object_id1 == object_id2);
ASSERT(memcmp(&digest1[0], &digest2[0], kDigestSize) == 0);
close(fd);
PASS();
}
TEST plasma_seal_reply_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
ARROW_CHECK_OK(SendSealReply(fd, object_id1, PlasmaError_ObjectExists));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaSealReply);
ObjectID object_id2;
Status s = ReadSealReply(data.data(), &object_id2);
ASSERT(object_id1 == object_id2);
ASSERT(s.IsPlasmaObjectExists());
close(fd);
PASS();
}
TEST plasma_get_request_test(void) {
int fd = create_temp_file();
ObjectID object_ids[2];
object_ids[0] = ObjectID::from_random();
object_ids[1] = ObjectID::from_random();
int64_t timeout_ms = 1234;
ARROW_CHECK_OK(SendGetRequest(fd, object_ids, 2, timeout_ms));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaGetRequest);
std::vector<ObjectID> object_ids_return;
int64_t timeout_ms_return;
ARROW_CHECK_OK(
ReadGetRequest(data.data(), object_ids_return, &timeout_ms_return));
ASSERT(object_ids[0] == object_ids_return[0]);
ASSERT(object_ids[1] == object_ids_return[1]);
ASSERT(timeout_ms == timeout_ms_return);
close(fd);
PASS();
}
TEST plasma_get_reply_test(void) {
int fd = create_temp_file();
ObjectID object_ids[2];
object_ids[0] = ObjectID::from_random();
object_ids[1] = ObjectID::from_random();
std::unordered_map<ObjectID, PlasmaObject, UniqueIDHasher> plasma_objects;
plasma_objects[object_ids[0]] = random_plasma_object();
plasma_objects[object_ids[1]] = random_plasma_object();
ARROW_CHECK_OK(SendGetReply(fd, object_ids, plasma_objects, 2));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaGetReply);
ObjectID object_ids_return[2];
PlasmaObject plasma_objects_return[2];
memset(&plasma_objects_return, 0, sizeof(plasma_objects_return));
ARROW_CHECK_OK(ReadGetReply(data.data(), object_ids_return,
&plasma_objects_return[0], 2));
ASSERT(object_ids[0] == object_ids_return[0]);
ASSERT(object_ids[1] == object_ids_return[1]);
ASSERT(memcmp(&plasma_objects[object_ids[0]], &plasma_objects_return[0],
sizeof(PlasmaObject)) == 0);
ASSERT(memcmp(&plasma_objects[object_ids[1]], &plasma_objects_return[1],
sizeof(PlasmaObject)) == 0);
close(fd);
PASS();
}
TEST plasma_release_request_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
ARROW_CHECK_OK(SendReleaseRequest(fd, object_id1));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaReleaseRequest);
ObjectID object_id2;
ARROW_CHECK_OK(ReadReleaseRequest(data.data(), &object_id2));
ASSERT(object_id1 == object_id2);
close(fd);
PASS();
}
TEST plasma_release_reply_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
ARROW_CHECK_OK(SendReleaseReply(fd, object_id1, PlasmaError_ObjectExists));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaReleaseReply);
ObjectID object_id2;
Status s = ReadReleaseReply(data.data(), &object_id2);
ASSERT(object_id1 == object_id2);
ASSERT(s.IsPlasmaObjectExists());
close(fd);
PASS();
}
TEST plasma_delete_request_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
ARROW_CHECK_OK(SendDeleteRequest(fd, object_id1));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaDeleteRequest);
ObjectID object_id2;
ARROW_CHECK_OK(ReadDeleteRequest(data.data(), &object_id2));
ASSERT(object_id1 == object_id2);
close(fd);
PASS();
}
TEST plasma_delete_reply_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
int error1 = PlasmaError_ObjectExists;
ARROW_CHECK_OK(SendDeleteReply(fd, object_id1, error1));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaDeleteReply);
ObjectID object_id2;
Status s = ReadDeleteReply(data.data(), &object_id2);
ASSERT(object_id1 == object_id2);
ASSERT(s.IsPlasmaObjectExists());
close(fd);
PASS();
}
TEST plasma_status_request_test(void) {
int fd = create_temp_file();
int64_t num_objects = 2;
ObjectID object_ids[num_objects];
object_ids[0] = ObjectID::from_random();
object_ids[1] = ObjectID::from_random();
ARROW_CHECK_OK(SendStatusRequest(fd, object_ids, num_objects));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaStatusRequest);
ObjectID object_ids_read[num_objects];
ARROW_CHECK_OK(ReadStatusRequest(data.data(), object_ids_read, num_objects));
ASSERT(object_ids[0] == object_ids_read[0]);
ASSERT(object_ids[1] == object_ids_read[1]);
close(fd);
PASS();
}
TEST plasma_status_reply_test(void) {
int fd = create_temp_file();
ObjectID object_ids[2];
object_ids[0] = ObjectID::from_random();
object_ids[1] = ObjectID::from_random();
int object_statuses[2] = {42, 43};
ARROW_CHECK_OK(SendStatusReply(fd, object_ids, object_statuses, 2));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaStatusReply);
int64_t num_objects = ReadStatusReply_num_objects(data.data());
ObjectID object_ids_read[num_objects];
int object_statuses_read[num_objects];
ARROW_CHECK_OK(ReadStatusReply(data.data(), object_ids_read,
object_statuses_read, num_objects));
ASSERT(object_ids[0] == object_ids_read[0]);
ASSERT(object_ids[1] == object_ids_read[1]);
ASSERT_EQ(object_statuses[0], object_statuses_read[0]);
ASSERT_EQ(object_statuses[1], object_statuses_read[1]);
close(fd);
PASS();
}
TEST plasma_evict_request_test(void) {
int fd = create_temp_file();
int64_t num_bytes = 111;
ARROW_CHECK_OK(SendEvictRequest(fd, num_bytes));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaEvictRequest);
int64_t num_bytes_received;
ARROW_CHECK_OK(ReadEvictRequest(data.data(), &num_bytes_received));
ASSERT_EQ(num_bytes, num_bytes_received);
close(fd);
PASS();
}
TEST plasma_evict_reply_test(void) {
int fd = create_temp_file();
int64_t num_bytes = 111;
ARROW_CHECK_OK(SendEvictReply(fd, num_bytes));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaEvictReply);
int64_t num_bytes_received;
ARROW_CHECK_OK(ReadEvictReply(data.data(), num_bytes_received));
ASSERT_EQ(num_bytes, num_bytes_received);
close(fd);
PASS();
}
TEST plasma_fetch_request_test(void) {
int fd = create_temp_file();
ObjectID object_ids[2];
object_ids[0] = ObjectID::from_random();
object_ids[1] = ObjectID::from_random();
ARROW_CHECK_OK(SendFetchRequest(fd, object_ids, 2));
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaFetchRequest);
std::vector<ObjectID> object_ids_read;
ARROW_CHECK_OK(ReadFetchRequest(data.data(), object_ids_read));
ASSERT(object_ids[0] == object_ids_read[0]);
ASSERT(object_ids[1] == object_ids_read[1]);
close(fd);
PASS();
}
TEST plasma_wait_request_test(void) {
int fd = create_temp_file();
const int num_objects_in = 2;
ObjectRequest object_requests_in[num_objects_in] = {
ObjectRequest({ObjectID::from_random(), PLASMA_QUERY_ANYWHERE, 0}),
ObjectRequest({ObjectID::from_random(), PLASMA_QUERY_LOCAL, 0})};
const int num_ready_objects_in = 1;
int64_t timeout_ms = 1000;
ARROW_CHECK_OK(SendWaitRequest(fd, &object_requests_in[0], num_objects_in,
num_ready_objects_in, timeout_ms));
/* Read message back. */
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaWaitRequest);
int num_ready_objects_out;
int64_t timeout_ms_read;
ObjectRequestMap object_requests_out;
ARROW_CHECK_OK(ReadWaitRequest(data.data(), object_requests_out,
&timeout_ms_read, &num_ready_objects_out));
ASSERT_EQ(num_objects_in, object_requests_out.size());
ASSERT_EQ(num_ready_objects_out, num_ready_objects_in);
for (int i = 0; i < num_objects_in; i++) {
const ObjectID &object_id = object_requests_in[i].object_id;
ASSERT_EQ(1, object_requests_out.count(object_id));
const auto &entry = object_requests_out.find(object_id);
ASSERT(entry != object_requests_out.end());
ASSERT(entry->second.object_id == object_requests_in[i].object_id);
ASSERT_EQ(entry->second.type, object_requests_in[i].type);
}
close(fd);
PASS();
}
TEST plasma_wait_reply_test(void) {
int fd = create_temp_file();
const int num_objects_in = 2;
/* Create a map with two ObjectRequests in it. */
ObjectRequestMap objects_in(num_objects_in);
ObjectID id1 = ObjectID::from_random();
objects_in[id1] = ObjectRequest({id1, 0, ObjectStatus_Local});
ObjectID id2 = ObjectID::from_random();
objects_in[id2] = ObjectRequest({id2, 0, ObjectStatus_Nonexistent});
ARROW_CHECK_OK(SendWaitReply(fd, objects_in, num_objects_in));
/* Read message back. */
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaWaitReply);
ObjectRequest objects_out[2];
int num_objects_out;
ARROW_CHECK_OK(ReadWaitReply(data.data(), &objects_out[0], &num_objects_out));
ASSERT(num_objects_in == num_objects_out);
for (int i = 0; i < num_objects_out; i++) {
/* Each object request must appear exactly once. */
ASSERT(1 == objects_in.count(objects_out[i].object_id));
const auto &entry = objects_in.find(objects_out[i].object_id);
ASSERT(entry != objects_in.end());
ASSERT(entry->second.object_id == objects_out[i].object_id);
ASSERT(entry->second.status == objects_out[i].status);
}
close(fd);
PASS();
}
TEST plasma_data_request_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
const char *address1 = "address1";
int port1 = 12345;
ARROW_CHECK_OK(SendDataRequest(fd, object_id1, address1, port1));
/* Reading message back. */
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaDataRequest);
ObjectID object_id2;
char *address2;
int port2;
ARROW_CHECK_OK(ReadDataRequest(data.data(), &object_id2, &address2, &port2));
ASSERT(object_id1 == object_id2);
ASSERT(strcmp(address1, address2) == 0);
ASSERT(port1 == port2);
free(address2);
close(fd);
PASS();
}
TEST plasma_data_reply_test(void) {
int fd = create_temp_file();
ObjectID object_id1 = ObjectID::from_random();
int64_t object_size1 = 146;
int64_t metadata_size1 = 198;
ARROW_CHECK_OK(SendDataReply(fd, object_id1, object_size1, metadata_size1));
/* Reading message back. */
std::vector<uint8_t> data =
read_message_from_file(fd, MessageType_PlasmaDataReply);
ObjectID object_id2;
int64_t object_size2;
int64_t metadata_size2;
ARROW_CHECK_OK(
ReadDataReply(data.data(), &object_id2, &object_size2, &metadata_size2));
ASSERT(object_id1 == object_id2);
ASSERT(object_size1 == object_size2);
ASSERT(metadata_size1 == metadata_size2);
PASS();
}
SUITE(plasma_serialization_tests) {
RUN_TEST(plasma_create_request_test);
RUN_TEST(plasma_create_reply_test);
RUN_TEST(plasma_seal_request_test);
RUN_TEST(plasma_seal_reply_test);
RUN_TEST(plasma_get_request_test);
RUN_TEST(plasma_get_reply_test);
RUN_TEST(plasma_release_request_test);
RUN_TEST(plasma_release_reply_test);
RUN_TEST(plasma_delete_request_test);
RUN_TEST(plasma_delete_reply_test);
RUN_TEST(plasma_status_request_test);
RUN_TEST(plasma_status_reply_test);
RUN_TEST(plasma_evict_request_test);
RUN_TEST(plasma_evict_reply_test);
RUN_TEST(plasma_fetch_request_test);
RUN_TEST(plasma_wait_request_test);
RUN_TEST(plasma_wait_reply_test);
RUN_TEST(plasma_data_request_test);
RUN_TEST(plasma_data_reply_test);
}
GREATEST_MAIN_DEFS();
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN();
RUN_SUITE(plasma_serialization_tests);
GREATEST_MAIN_END();
}
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
set -x
# Cause the script to exit if a single command fails.
set -e
TP_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd)
if [[ -z "$1" ]]; then
PYTHON_EXECUTABLE=`which python`
else
PYTHON_EXECUTABLE=$1
fi
echo "Using Python executable $PYTHON_EXECUTABLE."
# Determine how many parallel jobs to use for make based on the number of cores
unamestr="$(uname)"
if [[ "$unamestr" == "Linux" ]]; then
PARALLEL=$(nproc)
elif [[ "$unamestr" == "Darwin" ]]; then
PARALLEL=$(sysctl -n hw.ncpu)
echo "Platform is macosx."
else
echo "Unrecognized platform."
exit 1
fi
echo "building arrow"
cd $TP_DIR/arrow/cpp
mkdir -p $TP_DIR/arrow/cpp/build
cd $TP_DIR/arrow/cpp/build
export ARROW_HOME=$TP_DIR/arrow/cpp/build/cpp-install
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS="-g -O3" \
-DCMAKE_CXX_FLAGS="-g -O3" \
-DCMAKE_INSTALL_PREFIX=$ARROW_HOME \
-DARROW_BUILD_TESTS=off \
-DARROW_HDFS=on \
-DARROW_PYTHON=on \
-DARROW_PLASMA=on \
-DPLASMA_PYTHON=on \
-DARROW_JEMALLOC=off \
-DARROW_WITH_BROTLI=off \
-DARROW_WITH_LZ4=off \
-DARROW_WITH_SNAPPY=off \
-DARROW_WITH_ZLIB=off \
-DARROW_WITH_ZSTD=off \
..
make VERBOSE=1 -j$PARALLEL
make install
echo "installing pyarrow"
cd $TP_DIR/arrow/python
# We set PKG_CONFIG_PATH, which is important so that in cmake, pkg-config can
# find plasma.
ARROW_HOME=$TP_DIR/arrow/cpp/build/cpp-install
PKG_CONFIG_PATH=$ARROW_HOME/lib/pkgconfig PYARROW_WITH_PLASMA=1 PYARROW_BUNDLE_ARROW_CPP=1 $PYTHON_EXECUTABLE setup.py install
@@ -11,6 +11,6 @@ if [ ! -d $TP_DIR/arrow ]; then
git clone https://github.com/apache/arrow/ "$TP_DIR/arrow"
fi
cd $TP_DIR/arrow
git pull origin master
git fetch origin master
git checkout 8a700ccdad745c250fe5d91a9104e7c2d6364c1b
git checkout dca5d96c7a029c079183e2903db425e486e2deb9