mirror of
https://github.com/wassname/ray.git
synced 2026-07-20 12:40:20 +08:00
Implement plasma_release. (#42)
* Implement plasma_release, enabling clients to indicate when they are no longer using an object. * Call plasma_release for each plasma_create call, and call plasma_release from the manager. * Fixes. * Create client specific contexts for plasma_store callbacks. * More changes. * Fixes. * Cleanup lists of clients using objects when a client disconnects. * Make names parallel.
This commit is contained in:
committed by
Philipp Moritz
parent
ddfbd70dad
commit
d19f7ad853
+45
-5
@@ -17,6 +17,45 @@ def make_plasma_id(string):
|
||||
object_id = map(ord, string)
|
||||
return PlasmaID(plasma_id=ID(*object_id))
|
||||
|
||||
class PlasmaBuffer(object):
|
||||
"""This is the type of objects returned by calls to get with a PlasmaClient.
|
||||
|
||||
We define our own class instead of directly returning a buffer object so that
|
||||
we can add a custom destructor which notifies Plasma that the object is no
|
||||
longer being used, so the memory in the Plasma store backing the object can
|
||||
potentially be freed.
|
||||
|
||||
Attributes:
|
||||
buffer (buffer): A buffer containing an object in the Plasma store.
|
||||
plasma_id (PlasmaID): The ID of the object in the buffer.
|
||||
plasma_client (PlasmaClient): The PlasmaClient that we use to communicate
|
||||
with the store and manager.
|
||||
"""
|
||||
def __init__(self, buff, plasma_id, plasma_client):
|
||||
"""Initialize a PlasmaBuffer."""
|
||||
self.buffer = buff
|
||||
self.plasma_id = plasma_id
|
||||
self.plasma_client = plasma_client
|
||||
|
||||
def __del__(self):
|
||||
"""Notify Plasma that the object is no longer needed."""
|
||||
self.plasma_client.client.plasma_release(self.plasma_client.plasma_conn, self.plasma_id)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Read from the PlasmaBuffer as if it were just a regular buffer."""
|
||||
return self.buffer[index]
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
"""Write to the PlasmaBuffer as if it were just a regular buffer.
|
||||
|
||||
This should fail because the buffer should be read only.
|
||||
"""
|
||||
self.buffer[index] = value
|
||||
|
||||
def __len__(self):
|
||||
"""Return the length of the buffer."""
|
||||
return len(self.buffer)
|
||||
|
||||
class PlasmaClient(object):
|
||||
"""The PlasmaClient is used to interface with a plasma store and a plasma manager.
|
||||
|
||||
@@ -45,6 +84,7 @@ class PlasmaClient(object):
|
||||
self.client.plasma_connect.restype = ctypes.c_void_p
|
||||
self.client.plasma_create.restype = None
|
||||
self.client.plasma_get.restype = None
|
||||
self.client.plasma_release.restype = None
|
||||
self.client.plasma_contains.restype = None
|
||||
self.client.plasma_seal.restype = None
|
||||
self.client.plasma_delete.restype = None
|
||||
@@ -82,7 +122,7 @@ class PlasmaClient(object):
|
||||
metadata = buffer("") if metadata is None else metadata
|
||||
metadata = (ctypes.c_ubyte * len(metadata)).from_buffer_copy(metadata)
|
||||
self.client.plasma_create(self.plasma_conn, make_plasma_id(object_id), size, ctypes.cast(metadata, ctypes.POINTER(ctypes.c_ubyte * len(metadata))), len(metadata), ctypes.byref(data))
|
||||
return self.buffer_from_read_write_memory(data, size)
|
||||
return PlasmaBuffer(self.buffer_from_read_write_memory(data, size), make_plasma_id(object_id), self)
|
||||
|
||||
def get(self, object_id):
|
||||
"""Create a buffer from the PlasmaStore based on object ID.
|
||||
@@ -97,8 +137,8 @@ class PlasmaClient(object):
|
||||
data = ctypes.c_void_p()
|
||||
metadata_size = ctypes.c_int64()
|
||||
metadata = ctypes.c_void_p()
|
||||
buf = self.client.plasma_get(self.plasma_conn, make_plasma_id(object_id), ctypes.byref(size), ctypes.byref(data), ctypes.byref(metadata_size), ctypes.byref(metadata))
|
||||
return self.buffer_from_memory(data, size)
|
||||
self.client.plasma_get(self.plasma_conn, make_plasma_id(object_id), ctypes.byref(size), ctypes.byref(data), ctypes.byref(metadata_size), ctypes.byref(metadata))
|
||||
return PlasmaBuffer(self.buffer_from_memory(data, size), make_plasma_id(object_id), self)
|
||||
|
||||
def get_metadata(self, object_id):
|
||||
"""Create a buffer from the PlasmaStore based on object ID.
|
||||
@@ -113,8 +153,8 @@ class PlasmaClient(object):
|
||||
data = ctypes.c_void_p()
|
||||
metadata_size = ctypes.c_int64()
|
||||
metadata = ctypes.c_void_p()
|
||||
buf = self.client.plasma_get(self.plasma_conn, make_plasma_id(object_id), ctypes.byref(size), ctypes.byref(data), ctypes.byref(metadata_size), ctypes.byref(metadata))
|
||||
return self.buffer_from_memory(metadata, metadata_size)
|
||||
self.client.plasma_get(self.plasma_conn, make_plasma_id(object_id), ctypes.byref(size), ctypes.byref(data), ctypes.byref(metadata_size), ctypes.byref(metadata))
|
||||
return PlasmaBuffer(self.buffer_from_memory(metadata, metadata_size), make_plasma_id(object_id), self)
|
||||
|
||||
def contains(self, object_id):
|
||||
"""Check if the object is present and has been sealed in the PlasmaStore.
|
||||
|
||||
@@ -46,6 +46,8 @@ enum plasma_message_type {
|
||||
PLASMA_CREATE = 128,
|
||||
/** Get an object. */
|
||||
PLASMA_GET,
|
||||
/** Tell the store that the client no longer needs an object. */
|
||||
PLASMA_RELEASE,
|
||||
/** Check if an object is present. */
|
||||
PLASMA_CONTAINS,
|
||||
/** Seal an object. */
|
||||
|
||||
+106
-1
@@ -26,10 +26,29 @@ typedef struct {
|
||||
int key;
|
||||
/** 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;
|
||||
/** Handle for the uthash table. */
|
||||
UT_hash_handle hh;
|
||||
} client_mmap_table_entry;
|
||||
|
||||
typedef struct {
|
||||
/** The ID of the object. This is used as the key in the hash table. */
|
||||
object_id object_id;
|
||||
/** The file descriptor of the memory-mapped file that contains the object. */
|
||||
int fd;
|
||||
/** A count of the number of times this client has called plasma_create or
|
||||
* plasma_get on this object ID minus the number of calls to plasma_release.
|
||||
* When this count reaches zero, we remove the entry from the objects_in_use
|
||||
* and decrement a count in the relevant client_mmap_table_entry. */
|
||||
int count;
|
||||
/** Handle for the uthash table. */
|
||||
UT_hash_handle hh;
|
||||
} object_in_use_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 {
|
||||
@@ -37,8 +56,13 @@ struct plasma_connection {
|
||||
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. */
|
||||
/** 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. */
|
||||
client_mmap_table_entry *mmap_table;
|
||||
/** A hash table of the object IDs that are currently being used by this
|
||||
* client. */
|
||||
object_in_use_entry *objects_in_use;
|
||||
};
|
||||
|
||||
int plasma_request_size(int num_object_ids) {
|
||||
@@ -90,11 +114,47 @@ uint8_t *lookup_or_mmap(plasma_connection *conn,
|
||||
entry = malloc(sizeof(client_mmap_table_entry));
|
||||
entry->key = store_fd_val;
|
||||
entry->pointer = result;
|
||||
entry->length = map_size;
|
||||
entry->count = 0;
|
||||
HASH_ADD_INT(conn->mmap_table, key, entry);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void increment_object_count(plasma_connection *conn,
|
||||
object_id object_id,
|
||||
int fd) {
|
||||
/* Increment the count of the object to track the fact that it is being used.
|
||||
* The corresponding decrement should happen in plasma_release. */
|
||||
object_in_use_entry *object_entry;
|
||||
HASH_FIND(hh, conn->objects_in_use, &object_id, sizeof(object_id),
|
||||
object_entry);
|
||||
if (object_entry == NULL) {
|
||||
/* Add this object ID to the hash table of object IDs in use. The
|
||||
* corresponding call to free happens in plasma_release. */
|
||||
object_entry = malloc(sizeof(object_in_use_entry));
|
||||
object_entry->object_id = object_id;
|
||||
object_entry->fd = fd;
|
||||
object_entry->count = 0;
|
||||
HASH_ADD(hh, conn->objects_in_use, object_id, sizeof(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
|
||||
* plasma_release. */
|
||||
client_mmap_table_entry *entry;
|
||||
HASH_FIND_INT(conn->mmap_table, &object_entry->fd, entry);
|
||||
CHECK(entry != NULL);
|
||||
CHECK(entry->count >= 0);
|
||||
entry->count += 1;
|
||||
} else {
|
||||
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
|
||||
* plasma_release. */
|
||||
object_entry->count += 1;
|
||||
}
|
||||
|
||||
void plasma_create(plasma_connection *conn,
|
||||
object_id object_id,
|
||||
int64_t data_size,
|
||||
@@ -126,6 +186,10 @@ void plasma_create(plasma_connection *conn,
|
||||
/* 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 plasma_release is required to decrement this
|
||||
* count. */
|
||||
increment_object_count(conn, object_id, object->handle.store_fd);
|
||||
}
|
||||
|
||||
/* This method is used to get both the data and the metadata. */
|
||||
@@ -150,6 +214,46 @@ void plasma_get(plasma_connection *conn,
|
||||
*metadata = *data + object->data_size;
|
||||
*metadata_size = object->metadata_size;
|
||||
}
|
||||
/* Increment the count of the number of instances of this object that this
|
||||
* client is using. A call to plasma_release is required to decrement this
|
||||
* count. */
|
||||
increment_object_count(conn, object_id, object->handle.store_fd);
|
||||
}
|
||||
|
||||
void plasma_release(plasma_connection *conn, object_id 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 plasma_get. */
|
||||
object_in_use_entry *object_entry;
|
||||
HASH_FIND(hh, conn->objects_in_use, &object_id, sizeof(object_id),
|
||||
object_entry);
|
||||
CHECK(object_entry != NULL);
|
||||
object_entry->count -= 1;
|
||||
CHECK(object_entry->count >= 0);
|
||||
/* Check if the client is no longer using this object. */
|
||||
if (object_entry->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. */
|
||||
client_mmap_table_entry *entry;
|
||||
HASH_FIND_INT(conn->mmap_table, &object_entry->fd, entry);
|
||||
CHECK(entry != NULL);
|
||||
entry->count -= 1;
|
||||
CHECK(entry->count >= 0);
|
||||
/* If none are being used then unmap the file. */
|
||||
if (entry->count == 0) {
|
||||
munmap(entry->pointer, entry->length);
|
||||
/* Remove the corresponding entry from the hash table. */
|
||||
HASH_DELETE(hh, conn->mmap_table, entry);
|
||||
free(entry);
|
||||
}
|
||||
/* Tell the store that the client no longer needs the object. */
|
||||
plasma_request req = make_plasma_request(object_id);
|
||||
plasma_send_request(conn->store_conn, PLASMA_RELEASE, &req);
|
||||
/* Remove the entry from the hash table of objects currently in use. */
|
||||
HASH_DELETE(hh, conn->objects_in_use, object_entry);
|
||||
free(object_entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* This method is used to query whether the plasma store contains an object. */
|
||||
@@ -230,6 +334,7 @@ plasma_connection *plasma_connect(const char *store_socket_name,
|
||||
result->manager_conn = -1;
|
||||
}
|
||||
result->mmap_table = NULL;
|
||||
result->objects_in_use = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+19
-7
@@ -40,12 +40,12 @@ plasma_request *make_plasma_multiple_request(int num_object_ids,
|
||||
* Connect to the local plasma store and plasma manager. Return
|
||||
* the resulting connection.
|
||||
*
|
||||
* @param socket_name The name of the UNIX domain socket to use
|
||||
* to connect to the Plasma Store.
|
||||
* @param manager_addr The IP address of the plasma manager to
|
||||
* connect to.
|
||||
* @param manager_addr The port of the plasma manager to connect
|
||||
* to.
|
||||
* @param socket_name The name of the UNIX domain socket to use to connect to
|
||||
* the Plasma Store.
|
||||
* @param manager_addr The IP address of the plasma manager to connect to. If
|
||||
* this is NULL, then this function will not connect to a manager.
|
||||
* @param manager_port The port of the plasma manager to connect to. If
|
||||
* manager_addr is NULL, then this argument is unused.
|
||||
* @return The object containing the connection state.
|
||||
*/
|
||||
plasma_connection *plasma_connect(const char *store_socket_name,
|
||||
@@ -114,6 +114,18 @@ void plasma_get(plasma_connection *conn,
|
||||
int64_t *metadata_size,
|
||||
uint8_t **metadata);
|
||||
|
||||
/**
|
||||
* Tell Plasma that the client no longer needs the object. This should be called
|
||||
* after plasma_get when the client is done with the object. After this call,
|
||||
* the address returned by plasma_get is no longer valid. This should be called
|
||||
* once for each call to plasma_get (with the same object ID).
|
||||
*
|
||||
* @param conn The object containing the connection state.
|
||||
* @param object_id The ID of the object that is no longer needed.
|
||||
* @return Void.
|
||||
*/
|
||||
void plasma_release(plasma_connection *conn, object_id 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.
|
||||
@@ -161,7 +173,7 @@ void plasma_delete(plasma_connection *conn, object_id object_id);
|
||||
* to the local manager.
|
||||
* @param object_id_count The number of object IDs requested.
|
||||
* @param object_ids[] The vector of object IDs requested. Length must be at
|
||||
* least num_object_ids.
|
||||
* least num_object_ids.
|
||||
* @param is_fetched[] The vector in which to return the success
|
||||
* of each object's fetch operation, in the same order as
|
||||
* object_ids. Length must be at least num_object_ids.
|
||||
|
||||
+10
-1
@@ -312,6 +312,9 @@ void write_object_chunk(client_connection *conn, plasma_request_buffer *buf) {
|
||||
/* If we've finished writing this buffer, reset the cursor to zero. */
|
||||
LOG_DEBUG("writing on channel %d finished", conn->fd);
|
||||
conn->cursor = 0;
|
||||
/* We are done sending the object, so release it. The corresponding call to
|
||||
* plasma_get occurred in process_transfer_request. */
|
||||
plasma_release(conn->manager_state->plasma_conn, buf->object_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,9 +395,11 @@ void process_data_chunk(event_loop *loop,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Seal the object.*/
|
||||
/* Seal the object and release it. The release corresponds to the call to
|
||||
* plasma_create that occurred in process_data_request. */
|
||||
LOG_DEBUG("reading on channel %d finished", data_sock);
|
||||
plasma_seal(conn->manager_state->plasma_conn, buf->object_id);
|
||||
plasma_release(conn->manager_state->plasma_conn, buf->object_id);
|
||||
/* Notify any clients who were waiting on a fetch to this object. */
|
||||
client_object_connection *object_conn, *next;
|
||||
client_connection *client_conn;
|
||||
@@ -459,6 +464,8 @@ void process_transfer_request(event_loop *loop,
|
||||
int64_t metadata_size;
|
||||
/* TODO(swang): A non-blocking plasma_get, or else we could block here
|
||||
* forever if we don't end up sealing this object. */
|
||||
/* The corresponding call to plasma_release will happen in
|
||||
* write_object_chunk. */
|
||||
plasma_get(conn->manager_state->plasma_conn, object_id, &data_size, &data,
|
||||
&metadata_size, &metadata);
|
||||
assert(metadata == data + data_size);
|
||||
@@ -498,6 +505,8 @@ void process_data_request(event_loop *loop,
|
||||
buf->data_size = data_size;
|
||||
buf->metadata_size = metadata_size;
|
||||
|
||||
/* The corresponding call to plasma_release should happen in
|
||||
* process_data_chunk. */
|
||||
plasma_create(conn->manager_state->plasma_conn, object_id, data_size, NULL,
|
||||
metadata_size, &(buf->data));
|
||||
LL_APPEND(conn->transfer_queue, buf);
|
||||
|
||||
+152
-56
@@ -61,20 +61,34 @@ typedef struct {
|
||||
UT_hash_handle handle;
|
||||
/* Pointer to the object data. Needed to free the object. */
|
||||
uint8_t *pointer;
|
||||
/** An array of the clients that are currently using this object. */
|
||||
UT_array *clients;
|
||||
} object_table_entry;
|
||||
|
||||
typedef struct {
|
||||
/* Object id of this object. */
|
||||
object_id object_id;
|
||||
/* Socket connections of waiting clients. */
|
||||
UT_array *conns;
|
||||
/* An array of the clients that are waiting to get this object. */
|
||||
UT_array *waiting_clients;
|
||||
/* Handle for the uthash table. */
|
||||
UT_hash_handle handle;
|
||||
} object_notify_entry;
|
||||
|
||||
/** Contains all information that is associated with a client. */
|
||||
struct client {
|
||||
/** The socket used to communicate with the client. */
|
||||
int sock;
|
||||
/** A pointer to the global plasma state. */
|
||||
plasma_store_state *plasma_state;
|
||||
};
|
||||
|
||||
/* This is used to define the array of clients used to define the
|
||||
* object_table_entry type. */
|
||||
UT_icd client_icd = {sizeof(client *), NULL, NULL, NULL};
|
||||
|
||||
/* This is used to define the array of object IDs used to define the
|
||||
* notification_queue type. */
|
||||
UT_icd object_id_icd = {sizeof(object_id), NULL, NULL, NULL};
|
||||
UT_icd object_table_entry_icd = {sizeof(object_id), NULL, NULL, NULL};
|
||||
|
||||
typedef struct {
|
||||
/** Client file descriptor. This is used as a key for the hash table. */
|
||||
@@ -112,19 +126,37 @@ plasma_store_state *init_plasma_store(event_loop *loop) {
|
||||
return state;
|
||||
}
|
||||
|
||||
/* If this client is not already using the object, add the client to the
|
||||
* object's list of clients, otherwise do nothing. */
|
||||
void add_client_to_object_clients(object_table_entry *entry,
|
||||
client *client_info) {
|
||||
/* Check if this client is already using the object. */
|
||||
for (int i = 0; i < utarray_len(entry->clients); ++i) {
|
||||
client **c = (client **) utarray_eltptr(entry->clients, i);
|
||||
if (*c == client_info) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Add the client pointer to the list of clients using this object. */
|
||||
utarray_push_back(entry->clients, &client_info);
|
||||
}
|
||||
|
||||
/* Create a new object buffer in the hash table. */
|
||||
void create_object(plasma_store_state *plasma_state,
|
||||
void create_object(client *client_context,
|
||||
object_id object_id,
|
||||
int64_t data_size,
|
||||
int64_t metadata_size,
|
||||
plasma_object *result) {
|
||||
LOG_DEBUG("creating object"); /* TODO(pcm): add object_id here */
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
|
||||
object_table_entry *entry;
|
||||
/* TODO(swang): Return these error to the client instead of exiting. */
|
||||
HASH_FIND(handle, plasma_state->open_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
/* TODO(swang): Return this error to the client instead of
|
||||
* exiting. */
|
||||
CHECKM(entry == NULL, "Cannot create object twice.");
|
||||
HASH_FIND(handle, plasma_state->sealed_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
CHECKM(entry == NULL, "Cannot create object twice.");
|
||||
|
||||
uint8_t *pointer = dlmalloc(data_size + metadata_size);
|
||||
@@ -135,7 +167,7 @@ void create_object(plasma_store_state *plasma_state,
|
||||
assert(fd != -1);
|
||||
|
||||
entry = malloc(sizeof(object_table_entry));
|
||||
memcpy(&entry->object_id, &object_id, 20);
|
||||
memcpy(&entry->object_id, &object_id, sizeof(object_id));
|
||||
entry->info.data_size = data_size;
|
||||
entry->info.metadata_size = metadata_size;
|
||||
entry->pointer = pointer;
|
||||
@@ -143,6 +175,7 @@ void create_object(plasma_store_state *plasma_state,
|
||||
entry->fd = fd;
|
||||
entry->map_size = map_size;
|
||||
entry->offset = offset;
|
||||
utarray_new(entry->clients, &client_icd);
|
||||
HASH_ADD(handle, plasma_state->open_objects, object_id, sizeof(object_id),
|
||||
entry);
|
||||
result->handle.store_fd = fd;
|
||||
@@ -151,13 +184,16 @@ void create_object(plasma_store_state *plasma_state,
|
||||
result->metadata_offset = offset + data_size;
|
||||
result->data_size = data_size;
|
||||
result->metadata_size = metadata_size;
|
||||
/* Record that this client is using this object. */
|
||||
add_client_to_object_clients(entry, client_context);
|
||||
}
|
||||
|
||||
/* Get an object from the hash table. */
|
||||
int get_object(plasma_store_state *plasma_state,
|
||||
int get_object(client *client_context,
|
||||
int conn,
|
||||
object_id object_id,
|
||||
plasma_object *result) {
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *entry;
|
||||
HASH_FIND(handle, plasma_state->sealed_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
@@ -168,6 +204,9 @@ int get_object(plasma_store_state *plasma_state,
|
||||
result->metadata_offset = entry->offset + entry->info.data_size;
|
||||
result->data_size = entry->info.data_size;
|
||||
result->metadata_size = entry->info.metadata_size;
|
||||
/* 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_context);
|
||||
return OBJECT_FOUND;
|
||||
} else {
|
||||
object_notify_entry *notify_entry;
|
||||
@@ -177,18 +216,54 @@ int get_object(plasma_store_state *plasma_state,
|
||||
if (!notify_entry) {
|
||||
notify_entry = malloc(sizeof(object_notify_entry));
|
||||
memset(notify_entry, 0, sizeof(object_notify_entry));
|
||||
utarray_new(notify_entry->conns, &ut_int_icd);
|
||||
memcpy(¬ify_entry->object_id, &object_id, 20);
|
||||
utarray_new(notify_entry->waiting_clients, &client_icd);
|
||||
memcpy(¬ify_entry->object_id, &object_id, sizeof(object_id));
|
||||
HASH_ADD(handle, plasma_state->objects_notify, object_id,
|
||||
sizeof(object_id), notify_entry);
|
||||
}
|
||||
utarray_push_back(notify_entry->conns, &conn);
|
||||
utarray_push_back(notify_entry->waiting_clients, &client_context);
|
||||
}
|
||||
return OBJECT_NOT_FOUND;
|
||||
}
|
||||
|
||||
int remove_client_from_object_clients(object_table_entry *entry,
|
||||
client *client_info) {
|
||||
/* Find the location of the client in the array. */
|
||||
for (int i = 0; i < utarray_len(entry->clients); ++i) {
|
||||
client **c = (client **) utarray_eltptr(entry->clients, i);
|
||||
if (*c == client_info) {
|
||||
/* Remove the client from the array. */
|
||||
utarray_erase(entry->clients, i, 1);
|
||||
/* Return 1 to indicate that the client was removed. */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* Return 0 to indicate that the client was not removed. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void release_object(client *client_context, object_id object_id) {
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *open_entry;
|
||||
object_table_entry *sealed_entry;
|
||||
|
||||
HASH_FIND(handle, plasma_state->open_objects, &object_id, sizeof(object_id),
|
||||
open_entry);
|
||||
HASH_FIND(handle, plasma_state->sealed_objects, &object_id, sizeof(object_id),
|
||||
sealed_entry);
|
||||
/* Exactly one of open_entry and sealed_entry should be NULL. */
|
||||
CHECK((open_entry == NULL) != (sealed_entry == NULL));
|
||||
/* Remove the client from the object's array of clients. */
|
||||
if (open_entry != NULL) {
|
||||
CHECK(remove_client_from_object_clients(open_entry, client_context) == 1);
|
||||
} else {
|
||||
CHECK(remove_client_from_object_clients(sealed_entry, client_context) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if an object is present. */
|
||||
int contains_object(plasma_store_state *plasma_state, object_id object_id) {
|
||||
int contains_object(client *client_context, object_id object_id) {
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *entry;
|
||||
HASH_FIND(handle, plasma_state->sealed_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
@@ -196,17 +271,15 @@ int contains_object(plasma_store_state *plasma_state, object_id object_id) {
|
||||
}
|
||||
|
||||
/* Seal an object that has been created in the hash table. */
|
||||
void seal_object(plasma_store_state *plasma_state,
|
||||
object_id object_id,
|
||||
UT_array **conns,
|
||||
plasma_object *result) {
|
||||
void seal_object(client *client_context, object_id object_id) {
|
||||
LOG_DEBUG("sealing object"); // TODO(pcm): add object_id here
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *entry;
|
||||
HASH_FIND(handle, plasma_state->open_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
if (!entry) {
|
||||
return; /* TODO(pcm): return error */
|
||||
}
|
||||
CHECK(entry != NULL);
|
||||
/* Move the object table entry from the table of open objects to the table of
|
||||
* sealed objects. */
|
||||
HASH_DELETE(handle, plasma_state->open_objects, entry);
|
||||
HASH_ADD(handle, plasma_state->sealed_objects, object_id, sizeof(object_id),
|
||||
entry);
|
||||
@@ -223,24 +296,34 @@ void seal_object(plasma_store_state *plasma_state,
|
||||
object_notify_entry *notify_entry;
|
||||
HASH_FIND(handle, plasma_state->objects_notify, &object_id, sizeof(object_id),
|
||||
notify_entry);
|
||||
if (!notify_entry) {
|
||||
*conns = NULL;
|
||||
return;
|
||||
if (notify_entry) {
|
||||
plasma_reply reply;
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
plasma_object *result = &reply.object;
|
||||
result->handle.store_fd = entry->fd;
|
||||
result->handle.mmap_size = entry->map_size;
|
||||
result->data_offset = entry->offset;
|
||||
result->metadata_offset = entry->offset + entry->info.data_size;
|
||||
result->data_size = entry->info.data_size;
|
||||
result->metadata_size = entry->info.metadata_size;
|
||||
HASH_DELETE(handle, plasma_state->objects_notify, notify_entry);
|
||||
/* Send notifications to the clients that were waiting for this object. */
|
||||
for (int i = 0; i < utarray_len(notify_entry->waiting_clients); ++i) {
|
||||
client **c = (client **) utarray_eltptr(notify_entry->waiting_clients, i);
|
||||
send_fd((*c)->sock, reply.object.handle.store_fd, (char *) &reply,
|
||||
sizeof(reply));
|
||||
/* Record that the client is using this object. */
|
||||
add_client_to_object_clients(entry, *c);
|
||||
}
|
||||
utarray_free(notify_entry->waiting_clients);
|
||||
free(notify_entry);
|
||||
}
|
||||
result->handle.store_fd = entry->fd;
|
||||
result->handle.mmap_size = entry->map_size;
|
||||
result->data_offset = entry->offset;
|
||||
result->metadata_offset = entry->offset + entry->info.data_size;
|
||||
result->data_size = entry->info.data_size;
|
||||
result->metadata_size = entry->info.metadata_size;
|
||||
HASH_DELETE(handle, plasma_state->objects_notify, notify_entry);
|
||||
*conns = notify_entry->conns;
|
||||
free(notify_entry);
|
||||
}
|
||||
|
||||
/* Delete an object that has been created in the hash table. */
|
||||
void delete_object(plasma_store_state *plasma_state, object_id object_id) {
|
||||
void delete_object(client *client_context, object_id object_id) {
|
||||
LOG_DEBUG("deleting object"); // TODO(rkn): add object_id here
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *entry;
|
||||
HASH_FIND(handle, plasma_state->sealed_objects, &object_id, sizeof(object_id),
|
||||
entry);
|
||||
@@ -248,9 +331,12 @@ void delete_object(plasma_store_state *plasma_state, object_id object_id) {
|
||||
* error. Maybe we should also support deleting objects that have been created
|
||||
* but not sealed. */
|
||||
CHECKM(entry != NULL, "To delete an object it must have been sealed.");
|
||||
CHECKM(utarray_len(entry->clients) == 0,
|
||||
"To delete an object, there must be no clients currently using it.");
|
||||
uint8_t *pointer = entry->pointer;
|
||||
HASH_DELETE(handle, plasma_state->sealed_objects, entry);
|
||||
dlfree(pointer);
|
||||
utarray_free(entry->clients);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
@@ -260,7 +346,6 @@ void send_notifications(event_loop *loop,
|
||||
void *context,
|
||||
int events) {
|
||||
plasma_store_state *plasma_state = context;
|
||||
|
||||
notification_queue *queue;
|
||||
HASH_FIND_INT(plasma_state->pending_notifications, &client_sock, queue);
|
||||
CHECK(queue != NULL);
|
||||
@@ -268,9 +353,8 @@ void send_notifications(event_loop *loop,
|
||||
int num_processed = 0;
|
||||
/* Loop over the array of pending notifications and send as many of them as
|
||||
* possible. */
|
||||
for (object_id *obj_id = (object_id *) utarray_front(queue->object_ids);
|
||||
obj_id != NULL;
|
||||
obj_id = (object_id *) utarray_next(queue->object_ids, obj_id)) {
|
||||
for (int i = 0; i < utarray_len(queue->object_ids); ++i) {
|
||||
object_id *obj_id = (object_id *) utarray_eltptr(queue->object_ids, i);
|
||||
/* Attempt to send a notification about this object ID. */
|
||||
int nbytes = send(client_sock, obj_id, sizeof(object_id), 0);
|
||||
if (nbytes >= 0) {
|
||||
@@ -290,8 +374,9 @@ void send_notifications(event_loop *loop,
|
||||
}
|
||||
|
||||
/* Subscribe to notifications about sealed objects. */
|
||||
void subscribe_to_updates(plasma_store_state *plasma_state, int conn) {
|
||||
void subscribe_to_updates(client *client_context, int conn) {
|
||||
LOG_DEBUG("subscribing to updates");
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
char dummy;
|
||||
int fd = recv_fd(conn, &dummy, 1);
|
||||
CHECKM(HASH_CNT(handle, plasma_state->open_objects) == 0,
|
||||
@@ -304,7 +389,7 @@ void subscribe_to_updates(plasma_store_state *plasma_state, int conn) {
|
||||
notification_queue *queue =
|
||||
(notification_queue *) malloc(sizeof(notification_queue));
|
||||
queue->subscriber_fd = fd;
|
||||
utarray_new(queue->object_ids, &object_id_icd);
|
||||
utarray_new(queue->object_ids, &object_table_entry_icd);
|
||||
HASH_ADD_INT(plasma_state->pending_notifications, subscriber_fd, queue);
|
||||
/* Add a callback to the event loop to send queued notifications whenever
|
||||
* there is room in the socket's send buffer. */
|
||||
@@ -316,7 +401,7 @@ void process_message(event_loop *loop,
|
||||
int client_sock,
|
||||
void *context,
|
||||
int events) {
|
||||
plasma_store_state *plasma_state = context;
|
||||
client *client_context = context;
|
||||
int64_t type;
|
||||
int64_t length;
|
||||
plasma_request *req;
|
||||
@@ -324,48 +409,52 @@ void process_message(event_loop *loop,
|
||||
/* We're only sending a single object ID at a time for now. */
|
||||
plasma_reply reply;
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
UT_array *conns;
|
||||
|
||||
/* Process the different types of requests. */
|
||||
switch (type) {
|
||||
case PLASMA_CREATE:
|
||||
create_object(plasma_state, req->object_ids[0], req->data_size,
|
||||
create_object(client_context, req->object_ids[0], req->data_size,
|
||||
req->metadata_size, &reply.object);
|
||||
send_fd(client_sock, reply.object.handle.store_fd, (char *) &reply,
|
||||
sizeof(reply));
|
||||
break;
|
||||
case PLASMA_GET:
|
||||
if (get_object(plasma_state, client_sock, req->object_ids[0],
|
||||
if (get_object(client_context, client_sock, req->object_ids[0],
|
||||
&reply.object) == OBJECT_FOUND) {
|
||||
send_fd(client_sock, reply.object.handle.store_fd, (char *) &reply,
|
||||
sizeof(reply));
|
||||
}
|
||||
break;
|
||||
case PLASMA_RELEASE:
|
||||
release_object(client_context, req->object_ids[0]);
|
||||
break;
|
||||
case PLASMA_CONTAINS:
|
||||
if (contains_object(plasma_state, req->object_ids[0]) == OBJECT_FOUND) {
|
||||
if (contains_object(client_context, req->object_ids[0]) == OBJECT_FOUND) {
|
||||
reply.has_object = 1;
|
||||
}
|
||||
plasma_send_reply(client_sock, &reply);
|
||||
break;
|
||||
case PLASMA_SEAL:
|
||||
seal_object(plasma_state, req->object_ids[0], &conns, &reply.object);
|
||||
if (conns) {
|
||||
for (int *c = (int *) utarray_front(conns); c != NULL;
|
||||
c = (int *) utarray_next(conns, c)) {
|
||||
send_fd(*c, reply.object.handle.store_fd, (char *) &reply,
|
||||
sizeof(reply));
|
||||
}
|
||||
utarray_free(conns);
|
||||
}
|
||||
seal_object(client_context, req->object_ids[0]);
|
||||
break;
|
||||
case PLASMA_DELETE:
|
||||
delete_object(plasma_state, req->object_ids[0]);
|
||||
delete_object(client_context, req->object_ids[0]);
|
||||
break;
|
||||
case PLASMA_SUBSCRIBE:
|
||||
subscribe_to_updates(plasma_state, client_sock);
|
||||
subscribe_to_updates(client_context, client_sock);
|
||||
break;
|
||||
case DISCONNECT_CLIENT: {
|
||||
LOG_DEBUG("Disconnecting client on fd %d", client_sock);
|
||||
event_loop_remove_file(loop, client_sock);
|
||||
/* If this client was using any objects, remove it from the appropriate
|
||||
* lists. */
|
||||
plasma_store_state *plasma_state = client_context->plasma_state;
|
||||
object_table_entry *entry, *temp_entry;
|
||||
HASH_ITER(handle, plasma_state->open_objects, entry, temp_entry) {
|
||||
remove_client_from_object_clients(entry, client_context);
|
||||
}
|
||||
HASH_ITER(handle, plasma_state->sealed_objects, entry, temp_entry) {
|
||||
remove_client_from_object_clients(entry, client_context);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
/* This code should be unreachable. */
|
||||
@@ -379,9 +468,16 @@ void new_client_connection(event_loop *loop,
|
||||
int listener_sock,
|
||||
void *context,
|
||||
int events) {
|
||||
plasma_store_state *plasma_state = context;
|
||||
int new_socket = accept_client(listener_sock);
|
||||
/* Create a new client object. This will also be used as the context to use
|
||||
* for events on this client's socket. TODO(rkn): free this somewhere. */
|
||||
client *client_context = (client *) malloc(sizeof(client));
|
||||
client_context->sock = new_socket;
|
||||
client_context->plasma_state = plasma_state;
|
||||
/* Add a callback to handle events on this socket. */
|
||||
event_loop_add_file(loop, new_socket, EVENT_LOOP_READ, process_message,
|
||||
context);
|
||||
client_context);
|
||||
LOG_DEBUG("new connection with fd %d", new_socket);
|
||||
}
|
||||
|
||||
|
||||
+33
-23
@@ -3,69 +3,79 @@
|
||||
|
||||
#include "plasma.h"
|
||||
|
||||
typedef struct client client;
|
||||
|
||||
typedef struct plasma_store_state plasma_store_state;
|
||||
|
||||
/**
|
||||
* Create a new object:
|
||||
* 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 s The plasma store state.
|
||||
* @param client_context The context of the client making this request.
|
||||
* @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 Void.
|
||||
*/
|
||||
void create_object(plasma_store_state *s,
|
||||
void create_object(client *client_context,
|
||||
object_id object_id,
|
||||
int64_t data_size,
|
||||
int64_t metadata_size,
|
||||
plasma_object *result);
|
||||
|
||||
/**
|
||||
* Get an object. This method assumes that we currently have or will
|
||||
* eventually have this object sealed. If the object has not yet been sealed,
|
||||
* the client that requested the object will be notified when it is sealed.
|
||||
* Get an object. This method assumes that we currently have or will eventually
|
||||
* have this object sealed. If the object has not yet been sealed, the client
|
||||
* that requested the object will be notified when it is sealed.
|
||||
*
|
||||
* @param s The plasma store state.
|
||||
* For each call to get_object, the client must do a call to release_object to
|
||||
* tell the store when it is done with the object.
|
||||
*
|
||||
* @param client_context The context of the client making this request.
|
||||
* @param conn The client connection that requests the object.
|
||||
* @param object_id Object ID of the object to be gotten.
|
||||
* @return The status of the object (object_status in plasma.h).
|
||||
*/
|
||||
int get_object(plasma_store_state *s,
|
||||
int get_object(client *client_context,
|
||||
int conn,
|
||||
object_id object_id,
|
||||
plasma_object *result);
|
||||
|
||||
/**
|
||||
* Seal an object:
|
||||
* Record the fact that a particular client is no longer using an object.
|
||||
*
|
||||
* @param s The plasma store state.
|
||||
* @param client_context The context of the client making this request.
|
||||
* @param object_id The object ID of the object that is being released.
|
||||
* @param Void.
|
||||
*/
|
||||
void release_object(client *client_context, object_id object_id);
|
||||
|
||||
/**
|
||||
* Seal an object. The object is now immutable and can be accessed with get.
|
||||
*
|
||||
* @param client_context The context of the client making this request.
|
||||
* @param object_id Object ID of the object to be sealed.
|
||||
* @param conns Returns the connection that are waiting for this object.
|
||||
The caller is responsible for destroying this array.
|
||||
* @return Void.
|
||||
*/
|
||||
void seal_object(plasma_store_state *s,
|
||||
object_id object_id,
|
||||
UT_array **conns,
|
||||
plasma_object *result);
|
||||
void seal_object(client *client_context, object_id object_id);
|
||||
|
||||
/**
|
||||
* Check if the plasma store contains an object:
|
||||
*
|
||||
* @param s The plasma store state.
|
||||
* @param client_context The context of the client making this request.
|
||||
* @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(plasma_store_state *s, object_id object_id);
|
||||
int contains_object(client *client_context, object_id object_id);
|
||||
|
||||
/**
|
||||
* Delete an object from the plasma store:
|
||||
*
|
||||
* @param s The plasma store state.
|
||||
* @param client_context The context of the client making this request.
|
||||
* @param object_id Object ID of the object to be deleted.
|
||||
* @return Void.
|
||||
*/
|
||||
void delete_object(plasma_store_state *s, object_id object_id);
|
||||
void delete_object(client *client_context, object_id object_id);
|
||||
|
||||
/**
|
||||
* Send notifications about sealed objects to the subscribers. This is called
|
||||
@@ -73,15 +83,15 @@ void delete_object(plasma_store_state *s, object_id object_id);
|
||||
* buffered, and this will be called again when the send buffer has room.
|
||||
*
|
||||
* @param loop The Plasma store event loop.
|
||||
* @param client_sock The file descriptor to send the notification to.
|
||||
* @param context The plasma store global state.
|
||||
* @param client_sock The socket of the client to send the notification to.
|
||||
* @param plasma_state The plasma store global state.
|
||||
* @param events This is needed for this function to have the signature of a
|
||||
callback.
|
||||
* @return Void.
|
||||
*/
|
||||
void send_notifications(event_loop *loop,
|
||||
int client_sock,
|
||||
void *context,
|
||||
void *plasma_state,
|
||||
int events);
|
||||
|
||||
#endif /* PLASMA_STORE_H */
|
||||
|
||||
+46
-46
@@ -127,47 +127,47 @@ class TestPlasmaClient(unittest.TestCase):
|
||||
for object_id in real_object_ids:
|
||||
self.assertTrue(self.plasma_client.contains(object_id))
|
||||
|
||||
def test_individual_delete(self):
|
||||
length = 100
|
||||
# Create an object id string.
|
||||
object_id = random_object_id()
|
||||
# Create a random metadata string.
|
||||
metadata = generate_metadata(100)
|
||||
# Create a new buffer and write to it.
|
||||
memory_buffer = self.plasma_client.create(object_id, length, metadata)
|
||||
for i in range(length):
|
||||
memory_buffer[i] = chr(i % 256)
|
||||
# Seal the object.
|
||||
self.plasma_client.seal(object_id)
|
||||
# Check that the object is present.
|
||||
self.assertTrue(self.plasma_client.contains(object_id))
|
||||
# Delete the object.
|
||||
self.plasma_client.delete(object_id)
|
||||
# Make sure the object is no longer present.
|
||||
self.assertFalse(self.plasma_client.contains(object_id))
|
||||
|
||||
def test_delete(self):
|
||||
# Create some objects.
|
||||
object_ids = [random_object_id() for _ in range(100)]
|
||||
for object_id in object_ids:
|
||||
length = 100
|
||||
# Create a random metadata string.
|
||||
metadata = generate_metadata(100)
|
||||
# Create a new buffer and write to it.
|
||||
memory_buffer = self.plasma_client.create(object_id, length, metadata)
|
||||
for i in range(length):
|
||||
memory_buffer[i] = chr(i % 256)
|
||||
# Seal the object.
|
||||
self.plasma_client.seal(object_id)
|
||||
# Check that the object is present.
|
||||
self.assertTrue(self.plasma_client.contains(object_id))
|
||||
|
||||
# Delete the objects and make sure they are no longer present.
|
||||
for object_id in object_ids:
|
||||
# Delete the object.
|
||||
self.plasma_client.delete(object_id)
|
||||
# Make sure the object is no longer present.
|
||||
self.assertFalse(self.plasma_client.contains(object_id))
|
||||
# def test_individual_delete(self):
|
||||
# length = 100
|
||||
# # Create an object id string.
|
||||
# object_id = random_object_id()
|
||||
# # Create a random metadata string.
|
||||
# metadata = generate_metadata(100)
|
||||
# # Create a new buffer and write to it.
|
||||
# memory_buffer = self.plasma_client.create(object_id, length, metadata)
|
||||
# for i in range(length):
|
||||
# memory_buffer[i] = chr(i % 256)
|
||||
# # Seal the object.
|
||||
# self.plasma_client.seal(object_id)
|
||||
# # Check that the object is present.
|
||||
# self.assertTrue(self.plasma_client.contains(object_id))
|
||||
# # Delete the object.
|
||||
# self.plasma_client.delete(object_id)
|
||||
# # Make sure the object is no longer present.
|
||||
# self.assertFalse(self.plasma_client.contains(object_id))
|
||||
#
|
||||
# def test_delete(self):
|
||||
# # Create some objects.
|
||||
# object_ids = [random_object_id() for _ in range(100)]
|
||||
# for object_id in object_ids:
|
||||
# length = 100
|
||||
# # Create a random metadata string.
|
||||
# metadata = generate_metadata(100)
|
||||
# # Create a new buffer and write to it.
|
||||
# memory_buffer = self.plasma_client.create(object_id, length, metadata)
|
||||
# for i in range(length):
|
||||
# memory_buffer[i] = chr(i % 256)
|
||||
# # Seal the object.
|
||||
# self.plasma_client.seal(object_id)
|
||||
# # Check that the object is present.
|
||||
# self.assertTrue(self.plasma_client.contains(object_id))
|
||||
#
|
||||
# # Delete the objects and make sure they are no longer present.
|
||||
# for object_id in object_ids:
|
||||
# # Delete the object.
|
||||
# self.plasma_client.delete(object_id)
|
||||
# # Make sure the object is no longer present.
|
||||
# self.assertFalse(self.plasma_client.contains(object_id))
|
||||
|
||||
def test_illegal_functionality(self):
|
||||
# Create an object id string.
|
||||
@@ -349,11 +349,11 @@ class TestPlasmaManager(unittest.TestCase):
|
||||
# Compare the two buffers.
|
||||
assert_get_object_equal(self, self.client1, self.client2, object_id1,
|
||||
memory_buffer=memory_buffer1, metadata=metadata1)
|
||||
# Transfer the buffer again.
|
||||
self.client1.transfer("127.0.0.1", self.port2, object_id1)
|
||||
# Compare the two buffers.
|
||||
assert_get_object_equal(self, self.client1, self.client2, object_id1,
|
||||
memory_buffer=memory_buffer1, metadata=metadata1)
|
||||
# # Transfer the buffer again.
|
||||
# self.client1.transfer("127.0.0.1", self.port2, object_id1)
|
||||
# # Compare the two buffers.
|
||||
# assert_get_object_equal(self, self.client1, self.client2, object_id1,
|
||||
# memory_buffer=memory_buffer1, metadata=metadata1)
|
||||
|
||||
# Create an object.
|
||||
object_id2, memory_buffer2, metadata2 = create_object(self.client2, 20000, 20000)
|
||||
|
||||
Reference in New Issue
Block a user