mirror of
https://github.com/wassname/ray.git
synced 2026-08-17 11:25:34 +08:00
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:
committed by
Robert Nishihara
parent
dfcd399dbb
commit
c3b39b4d86
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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/)
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 doesn’t 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 */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 */
|
||||
@@ -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(¬ification);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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());
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 &
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
+2
-2
@@ -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
|
||||
Reference in New Issue
Block a user