ray/src/plasma_client.c

330 lines
11 KiB
C
Raw Normal View History

/* PLASMA CLIENT: Client library for using the plasma store and manager */
2016-08-13 17:11:11 -07:00
#include <assert.h>
#include <fcntl.h>
2016-08-13 17:11:11 -07:00
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <netinet/in.h>
#include <netdb.h>
2016-08-13 17:11:11 -07:00
#include "common.h"
#include "io.h"
2016-08-13 17:11:11 -07:00
#include "plasma.h"
#include "plasma_client.h"
2016-08-13 17:11:11 -07:00
#include "fling.h"
#include "uthash.h"
typedef struct {
/** Key that uniquely identifies the memory mapped file. In practice, we
* take the numerical value of the file descriptor in the object store. */
int key;
/** The result of mmap for this file descriptor. */
uint8_t *pointer;
/** Handle for the uthash table. */
UT_hash_handle hh;
} client_mmap_table_entry;
/** Information about a connection between a Plasma Client and Plasma Store.
* This is used to avoid mapping the same files into memory multiple times. */
struct plasma_connection {
/** 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;
/** Table of dlmalloc buffer files that have been memory mapped so far. */
client_mmap_table_entry *mmap_table;
};
int plasma_request_size(int num_object_ids) {
int object_ids_size = (num_object_ids - 1) * sizeof(object_id);
return sizeof(plasma_request) + object_ids_size;
}
void plasma_send_request(int fd, int type, plasma_request *req) {
int req_size = plasma_request_size(req->num_object_ids);
int error = write_message(fd, type, req_size, (uint8_t *) req);
/* TODO(swang): Actually handle the write error. */
CHECK(!error);
}
plasma_request make_plasma_request(object_id object_id) {
plasma_request req = {.num_object_ids = 1, .object_ids = {object_id}};
return req;
}
plasma_request *make_plasma_multiple_request(int num_object_ids,
object_id object_ids[]) {
int req_size = plasma_request_size(num_object_ids);
plasma_request *req = malloc(req_size);
req->num_object_ids = num_object_ids;
memcpy(&req->object_ids, object_ids, num_object_ids * sizeof(object_id));
return req;
2016-08-13 17:11:11 -07:00
}
/* 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(plasma_connection *conn,
int fd,
int store_fd_val,
int64_t map_size) {
client_mmap_table_entry *entry;
HASH_FIND_INT(conn->mmap_table, &store_fd_val, entry);
if (entry) {
close(fd);
return entry->pointer;
} else {
uint8_t *result =
mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (result == MAP_FAILED) {
LOG_ERR("mmap failed");
exit(-1);
}
close(fd);
entry = malloc(sizeof(client_mmap_table_entry));
entry->key = store_fd_val;
entry->pointer = result;
HASH_ADD_INT(conn->mmap_table, key, entry);
return result;
}
}
void plasma_create(plasma_connection *conn,
object_id object_id,
int64_t data_size,
uint8_t *metadata,
int64_t metadata_size,
uint8_t **data) {
LOG_DEBUG("called plasma_create on conn %d with size %" PRId64
" and metadata size "
"%" PRId64,
conn->store_conn, data_size, metadata_size);
plasma_request req = make_plasma_request(object_id);
req.data_size = data_size;
req.metadata_size = metadata_size;
plasma_send_request(conn->store_conn, PLASMA_CREATE, &req);
2016-08-13 17:11:11 -07:00
plasma_reply reply;
int fd = recv_fd(conn->store_conn, (char *) &reply, sizeof(plasma_reply));
plasma_object *object = &reply.object;
CHECK(object->data_size == data_size);
CHECK(object->metadata_size == metadata_size);
/* The metadata should come right after the data. */
CHECK(object->metadata_offset == object->data_offset + data_size);
*data = lookup_or_mmap(conn, 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);
}
2016-08-13 17:11:11 -07:00
}
/* This method is used to get both the data and the metadata. */
void plasma_get(plasma_connection *conn,
object_id object_id,
int64_t *size,
uint8_t **data,
int64_t *metadata_size,
uint8_t **metadata) {
plasma_request req = make_plasma_request(object_id);
plasma_send_request(conn->store_conn, PLASMA_GET, &req);
2016-08-13 17:11:11 -07:00
plasma_reply reply;
int fd = recv_fd(conn->store_conn, (char *) &reply, sizeof(plasma_reply));
CHECKM(fd != -1, "recv not successful");
plasma_object *object = &reply.object;
*data = lookup_or_mmap(conn, fd, object->handle.store_fd,
object->handle.mmap_size) +
object->data_offset;
*size = object->data_size;
/* If requested, return the metadata as well. */
if (metadata != NULL) {
*metadata = *data + object->data_size;
*metadata_size = object->metadata_size;
}
2016-08-13 17:11:11 -07:00
}
/* This method is used to query whether the plasma store contains an object. */
void plasma_contains(plasma_connection *conn,
object_id object_id,
int *has_object) {
plasma_request req = make_plasma_request(object_id);
plasma_send_request(conn->store_conn, PLASMA_CONTAINS, &req);
plasma_reply reply;
int r = read(conn->store_conn, &reply, sizeof(plasma_reply));
CHECKM(r != -1, "read error");
CHECKM(r != 0, "connection disconnected");
*has_object = reply.has_object;
}
void plasma_seal(plasma_connection *conn, object_id object_id) {
plasma_request req = make_plasma_request(object_id);
plasma_send_request(conn->store_conn, PLASMA_SEAL, &req);
if (conn->manager_conn >= 0) {
plasma_send_request(conn->manager_conn, PLASMA_SEAL, &req);
}
}
void plasma_delete(plasma_connection *conn, object_id object_id) {
plasma_request req = make_plasma_request(object_id);
plasma_send_request(conn->store_conn, PLASMA_DELETE, &req);
2016-08-13 17:11:11 -07:00
}
int plasma_subscribe(plasma_connection *conn) {
int fd[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, fd);
/* Make the socket non-blocking. */
int flags = fcntl(fd[1], F_GETFL, 0);
CHECK(fcntl(fd[1], F_SETFL, flags | O_NONBLOCK) == 0);
/* Tell the Plasma store about the subscription. */
plasma_request req = {};
plasma_send_request(conn->store_conn, PLASMA_SUBSCRIBE, &req);
/* Send the file descriptor that the Plasma store should use to push
* notifications about sealed objects to this client. We include a one byte
* message because otherwise it seems to hang on Linux. */
char dummy = '\0';
send_fd(conn->store_conn, fd[1], &dummy, 1);
/* Return the file descriptor that the client should use to read notifications
* about sealed objects. */
return fd[0];
}
plasma_connection *plasma_connect(const char *store_socket_name,
const char *manager_addr,
int manager_port) {
CHECK(store_socket_name);
/* Try to connect to the Plasma store. If unsuccessful, retry several times.
*/
int fd = -1;
int connected_successfully = 0;
for (int num_attempts = 0; num_attempts < 50; ++num_attempts) {
fd = connect_ipc_sock(store_socket_name);
if (fd >= 0) {
connected_successfully = 1;
break;
}
/* Sleep for 100 milliseconds. */
usleep(100000);
}
/* If we could not connect to the Plasma store, exit. */
if (!connected_successfully) {
LOG_ERR("could not connect to store %s", store_socket_name);
2016-08-13 17:11:11 -07:00
exit(-1);
}
/* Initialize the store connection struct */
plasma_connection *result = malloc(sizeof(plasma_connection));
result->store_conn = fd;
if (manager_addr != NULL) {
result->manager_conn = plasma_manager_connect(manager_addr, manager_port);
} else {
result->manager_conn = -1;
}
result->mmap_table = NULL;
return result;
2016-08-13 17:11:11 -07:00
}
void plasma_disconnect(plasma_connection *conn) {
close(conn->store_conn);
if (conn->manager_conn >= 0) {
close(conn->manager_conn);
}
free(conn);
}
#define h_addr h_addr_list[0]
/* TODO(swang): Return the error to the caller. */
int plasma_manager_connect(const char *ip_addr, int port) {
int fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
LOG_ERR("could not create socket");
exit(-1);
}
struct hostent *manager = gethostbyname(ip_addr); /* TODO(pcm): cache this */
if (!manager) {
LOG_ERR("plasma manager %s not found", ip_addr);
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
2016-09-10 17:47:37 -07:00
memcpy(&addr.sin_addr.s_addr, manager->h_addr, manager->h_length);
addr.sin_port = htons(port);
int r = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
if (r < 0) {
LOG_ERR(
"could not establish connection to manager with id %s:%d (probably ran "
"out of ports)",
&ip_addr[0], port);
exit(-1);
}
return fd;
}
void plasma_transfer(plasma_connection *conn,
const char *addr,
int port,
object_id object_id) {
plasma_request req = make_plasma_request(object_id);
req.port = port;
char *end = NULL;
for (int i = 0; i < 4; ++i) {
req.addr[i] = strtol(end ? end : addr, &end, 10);
/* skip the '.' */
end += 1;
}
plasma_send_request(conn->manager_conn, PLASMA_TRANSFER, &req);
}
void plasma_fetch(plasma_connection *conn,
int num_object_ids,
object_id object_ids[],
int is_fetched[]) {
CHECK(conn->manager_conn >= 0);
plasma_request *req =
make_plasma_multiple_request(num_object_ids, object_ids);
LOG_DEBUG("Requesting fetch");
plasma_send_request(conn->manager_conn, PLASMA_FETCH, req);
free(req);
plasma_reply reply;
int nbytes, success;
for (int received = 0; received < num_object_ids; ++received) {
nbytes = recv(conn->manager_conn, (uint8_t *) &reply, sizeof(reply),
MSG_WAITALL);
if (nbytes < 0) {
LOG_ERR("Error while waiting for manager response in fetch");
success = 0;
} else if (nbytes == 0) {
success = 0;
} else {
CHECK(nbytes == sizeof(reply));
success = reply.has_object;
}
/* Update the correct index in is_fetched. */
int i = 0;
for (; i < num_object_ids; i++) {
if (memcmp(&object_ids[i], &reply.object_id, sizeof(object_id)) == 0) {
/* Check that this isn't a duplicate response. */
CHECK(!is_fetched[i]);
is_fetched[i] = success;
break;
}
}
CHECKM(i != num_object_ids,
"Received unexpected object ID from manager during fetch.");
}
}