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:
Robert Nishihara
2016-10-21 00:47:34 -07:00
committed by Philipp Moritz
parent ddfbd70dad
commit d19f7ad853
8 changed files with 413 additions and 139 deletions
+45 -5
View File
@@ -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.
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&notify_entry->object_id, &object_id, 20);
utarray_new(notify_entry->waiting_clients, &client_icd);
memcpy(&notify_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
View File
@@ -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
View File
@@ -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)