Add global version allocators for C in query modules (#162)

This commit is contained in:
Kostas Kyrimis 2021-06-07 15:45:05 +03:00 committed by GitHub
parent 50f6e348dc
commit 524acb17a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 279 additions and 82 deletions

View File

@ -17,10 +17,11 @@ extern "C" {
/// addition to efficiency, Memgraph can set the limit on allowed allocations
/// thus providing some safety with regards to memory usage. The allocated
/// memory is only valid during the execution of mgp_main. You must not allocate
/// global resources with these functions. None of the functions are
/// global resources with these functions and none of the functions are
/// thread-safe, because we provide a single thread of execution when invoking a
/// custom procedure. This allows Memgraph to be more efficient as stated
/// before.
/// custom procedure. For allocating global resources, you can use the _global
/// variations of the aforementioned allocators. This allows Memgraph to be
/// more efficient as explained before.
///@{
/// Provides memory managament access and state.
@ -39,8 +40,7 @@ void *mgp_alloc(struct mgp_memory *memory, size_t size_in_bytes);
/// `alignment` must be a power of 2 value.
/// The returned pointer must be freed with mgp_free.
/// NULL is returned if unable to serve the requested allocation.
void *mgp_aligned_alloc(struct mgp_memory *memory, size_t size_in_bytes,
size_t alignment);
void *mgp_aligned_alloc(struct mgp_memory *memory, size_t size_in_bytes, size_t alignment);
/// Deallocate an allocation from mgp_alloc or mgp_aligned_alloc.
/// Unlike free, this function is not thread-safe.
@ -48,6 +48,26 @@ void *mgp_aligned_alloc(struct mgp_memory *memory, size_t size_in_bytes,
/// The behavior is undefined if `ptr` is not a value returned from a prior
/// mgp_alloc or mgp_aligned_alloc call with the corresponding `memory`.
void mgp_free(struct mgp_memory *memory, void *ptr);
/// Allocate a global block of memory with given size in bytes.
/// This function can be used to allocate global memory that persists
/// beyond a single invocation of mgp_main.
/// The returned pointer must be freed with mgp_global_free.
/// NULL is returned if unable to serve the requested allocation.
void *mgp_global_alloc(size_t size_in_bytes);
/// Allocate an aligned global block of memory with given size in bytes.
/// This function can be used to allocate global memory that persists
/// beyond a single invocation of mgp_main.
/// The returned pointer must be freed with mgp_global_free.
/// NULL is returned if unable to serve the requested allocation.
void *mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment);
/// Deallocate an allocation from mgp_global_alloc or mgp_global_aligned_alloc.
/// If `ptr` is NULL, this function does nothing.
/// The behavior is undefined if `ptr` is not a value returned from a prior
/// mgp_global_alloc() or mgp_global_aligned_alloc().
void mgp_global_free(void *p);
///@}
/// @name Operations on mgp_value
@ -119,8 +139,7 @@ struct mgp_value *mgp_value_make_double(double val, struct mgp_memory *memory);
/// Construct a character string value from a NULL terminated string.
/// You need to free the instance through mgp_value_destroy.
/// NULL is returned if unable to allocate a mgp_value.
struct mgp_value *mgp_value_make_string(const char *val,
struct mgp_memory *memory);
struct mgp_value *mgp_value_make_string(const char *val, struct mgp_memory *memory);
/// Create a mgp_value storing a mgp_list.
/// You need to free the instance through mgp_value_destroy. The ownership of
@ -238,8 +257,7 @@ const struct mgp_path *mgp_value_get_path(const struct mgp_value *val);
/// of mgp_value, but it will not contain any elements. Therefore,
/// mgp_list_size will return 0.
/// NULL is returned if unable to allocate a new list.
struct mgp_list *mgp_list_make_empty(size_t capacity,
struct mgp_memory *memory);
struct mgp_list *mgp_list_make_empty(size_t capacity, struct mgp_memory *memory);
/// Free the memory used by the given mgp_list and contained elements.
void mgp_list_destroy(struct mgp_list *list);
@ -288,8 +306,7 @@ void mgp_map_destroy(struct mgp_map *map);
/// you still need to free their memory explicitly.
/// Return non-zero on success, or 0 if there's no memory to insert a new
/// mapping or a previous mapping already exists.
int mgp_map_insert(struct mgp_map *map, const char *key,
const struct mgp_value *value);
int mgp_map_insert(struct mgp_map *map, const char *key, const struct mgp_value *value);
/// Return the number of items stored in mgp_map.
size_t mgp_map_size(const struct mgp_map *map);
@ -314,8 +331,7 @@ struct mgp_map_items_iterator;
/// The returned mgp_map_items_iterator needs to be deallocated with
/// mgp_map_items_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_map_items_iterator *mgp_map_iter_items(const struct mgp_map *map,
struct mgp_memory *memory);
struct mgp_map_items_iterator *mgp_map_iter_items(const struct mgp_map *map, struct mgp_memory *memory);
/// Deallocate memory used by mgp_map_items_iterator.
void mgp_map_items_iterator_destroy(struct mgp_map_items_iterator *it);
@ -328,27 +344,23 @@ void mgp_map_items_iterator_destroy(struct mgp_map_items_iterator *it);
/// as the value before, and use them after invoking
/// mgp_map_items_iterator_next.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_map_item *mgp_map_items_iterator_get(
const struct mgp_map_items_iterator *it);
const struct mgp_map_item *mgp_map_items_iterator_get(const struct mgp_map_items_iterator *it);
/// Advance the iterator to the next item stored in map and return it.
/// The previous pointer obtained through mgp_map_items_iterator_get will
/// be invalidated, but the pointers to key and value will remain valid.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_map_item *mgp_map_items_iterator_next(
struct mgp_map_items_iterator *it);
const struct mgp_map_item *mgp_map_items_iterator_next(struct mgp_map_items_iterator *it);
/// Create a path with the copy of the given starting vertex.
/// You need to free the created instance with mgp_path_destroy.
/// NULL is returned if unable to allocate a path.
struct mgp_path *mgp_path_make_with_start(const struct mgp_vertex *vertex,
struct mgp_memory *memory);
struct mgp_path *mgp_path_make_with_start(const struct mgp_vertex *vertex, struct mgp_memory *memory);
/// Copy a mgp_path.
/// Returned pointer must be freed with mgp_path_destroy.
/// NULL is returned if unable to allocate a mgp_path.
struct mgp_path *mgp_path_copy(const struct mgp_path *path,
struct mgp_memory *memory);
struct mgp_path *mgp_path_copy(const struct mgp_path *path, struct mgp_memory *memory);
/// Free the memory used by the given mgp_path and contained vertices and edges.
void mgp_path_destroy(struct mgp_path *path);
@ -370,14 +382,12 @@ size_t mgp_path_size(const struct mgp_path *path);
/// Return the vertex from a path at given index.
/// The valid index range is [0, mgp_path_size].
/// NULL is returned if index is out of range.
const struct mgp_vertex *mgp_path_vertex_at(const struct mgp_path *path,
size_t index);
const struct mgp_vertex *mgp_path_vertex_at(const struct mgp_path *path, size_t index);
/// Return the edge from a path at given index.
/// The valid index range is [0, mgp_path_size - 1].
/// NULL is returned if index is out of range.
const struct mgp_edge *mgp_path_edge_at(const struct mgp_path *path,
size_t index);
const struct mgp_edge *mgp_path_edge_at(const struct mgp_path *path, size_t index);
/// Return non-zero if given paths are equal, otherwise 0.
int mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2);
@ -408,9 +418,7 @@ struct mgp_result_record *mgp_result_new_record(struct mgp_result *res);
/// Return 0 if there's no memory to copy the mgp_value to mgp_result_record or
/// if the combination of `field_name` and `val` does not satisfy the
/// procedure's result signature.
int mgp_result_record_insert(struct mgp_result_record *record,
const char *field_name,
const struct mgp_value *val);
int mgp_result_record_insert(struct mgp_result_record *record, const char *field_name, const struct mgp_value *val);
///@}
/// @name Graph Constructs
@ -446,15 +454,13 @@ struct mgp_property {
/// When the mgp_properties_iterator_next is invoked, the previous
/// mgp_property is invalidated and its value must not be used.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_property *mgp_properties_iterator_get(
const struct mgp_properties_iterator *it);
const struct mgp_property *mgp_properties_iterator_get(const struct mgp_properties_iterator *it);
/// Advance the iterator to the next property and return it.
/// The previous mgp_property obtained through mgp_properties_iterator_get
/// will be invalidated, and you must not use its value.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_property *mgp_properties_iterator_next(
struct mgp_properties_iterator *it);
const struct mgp_property *mgp_properties_iterator_next(struct mgp_properties_iterator *it);
/// Iterator over edges of a vertex.
struct mgp_edges_iterator;
@ -475,8 +481,7 @@ struct mgp_vertex_id mgp_vertex_get_id(const struct mgp_vertex *v);
/// Copy a mgp_vertex.
/// Returned pointer must be freed with mgp_vertex_destroy.
/// NULL is returned if unable to allocate a mgp_vertex.
struct mgp_vertex *mgp_vertex_copy(const struct mgp_vertex *v,
struct mgp_memory *memory);
struct mgp_vertex *mgp_vertex_copy(const struct mgp_vertex *v, struct mgp_memory *memory);
/// Free the memory used by a mgp_vertex.
void mgp_vertex_destroy(struct mgp_vertex *v);
@ -495,43 +500,37 @@ struct mgp_label mgp_vertex_label_at(const struct mgp_vertex *v, size_t index);
int mgp_vertex_has_label(const struct mgp_vertex *v, struct mgp_label label);
/// Return non-zero if the given vertex has a label with given name.
int mgp_vertex_has_label_named(const struct mgp_vertex *v,
const char *label_name);
int mgp_vertex_has_label_named(const struct mgp_vertex *v, const char *label_name);
/// Get a copy of a vertex property mapped to a given name.
/// Returned value must be freed with mgp_value_destroy.
/// NULL is returned if unable to allocate a mgp_value.
struct mgp_value *mgp_vertex_get_property(const struct mgp_vertex *v,
const char *property_name,
struct mgp_value *mgp_vertex_get_property(const struct mgp_vertex *v, const char *property_name,
struct mgp_memory *memory);
/// Start iterating over properties stored in the given vertex.
/// The returned mgp_properties_iterator needs to be deallocated with
/// mgp_properties_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_properties_iterator *mgp_vertex_iter_properties(
const struct mgp_vertex *v, struct mgp_memory *memory);
struct mgp_properties_iterator *mgp_vertex_iter_properties(const struct mgp_vertex *v, struct mgp_memory *memory);
/// Start iterating over inbound edges of the given vertex.
/// The returned mgp_edges_iterator needs to be deallocated with
/// mgp_edges_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_edges_iterator *mgp_vertex_iter_in_edges(const struct mgp_vertex *v,
struct mgp_memory *memory);
struct mgp_edges_iterator *mgp_vertex_iter_in_edges(const struct mgp_vertex *v, struct mgp_memory *memory);
/// Start iterating over outbound edges of the given vertex.
/// The returned mgp_edges_iterator needs to be deallocated with
/// mgp_edges_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_edges_iterator *mgp_vertex_iter_out_edges(const struct mgp_vertex *v,
struct mgp_memory *memory);
struct mgp_edges_iterator *mgp_vertex_iter_out_edges(const struct mgp_vertex *v, struct mgp_memory *memory);
/// Get the current edge pointed to by the iterator.
/// When the mgp_edges_iterator_next is invoked, the previous
/// mgp_edge is invalidated and its value must not be used.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_edge *mgp_edges_iterator_get(
const struct mgp_edges_iterator *it);
const struct mgp_edge *mgp_edges_iterator_get(const struct mgp_edges_iterator *it);
/// Advance the iterator to the next edge and return it.
/// The previous mgp_edge obtained through mgp_edges_iterator_get
@ -552,8 +551,7 @@ struct mgp_edge_id mgp_edge_get_id(const struct mgp_edge *e);
/// Copy a mgp_edge.
/// Returned pointer must be freed with mgp_edge_destroy.
/// NULL is returned if unable to allocate a mgp_edge.
struct mgp_edge *mgp_edge_copy(const struct mgp_edge *e,
struct mgp_memory *memory);
struct mgp_edge *mgp_edge_copy(const struct mgp_edge *e, struct mgp_memory *memory);
/// Free the memory used by a mgp_edge.
void mgp_edge_destroy(struct mgp_edge *e);
@ -573,16 +571,13 @@ const struct mgp_vertex *mgp_edge_get_to(const struct mgp_edge *e);
/// Get a copy of a edge property mapped to a given name.
/// Returned value must be freed with mgp_value_destroy.
/// NULL is returned if unable to allocate a mgp_value.
struct mgp_value *mgp_edge_get_property(const struct mgp_edge *e,
const char *property_name,
struct mgp_memory *memory);
struct mgp_value *mgp_edge_get_property(const struct mgp_edge *e, const char *property_name, struct mgp_memory *memory);
/// Start iterating over properties stored in the given edge.
/// The returned mgp_properties_iterator needs to be deallocated with
/// mgp_properties_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_properties_iterator *mgp_edge_iter_properties(
const struct mgp_edge *e, struct mgp_memory *memory);
struct mgp_properties_iterator *mgp_edge_iter_properties(const struct mgp_edge *e, struct mgp_memory *memory);
/// State of the graph database.
struct mgp_graph;
@ -590,8 +585,7 @@ struct mgp_graph;
/// Return the vertex corresponding to given ID.
/// The returned vertex must be freed using mgp_vertex_destroy.
/// NULL is returned if unable to allocate the vertex or if ID is not valid.
struct mgp_vertex *mgp_graph_get_vertex_by_id(const struct mgp_graph *g,
struct mgp_vertex_id id,
struct mgp_vertex *mgp_graph_get_vertex_by_id(const struct mgp_graph *g, struct mgp_vertex_id id,
struct mgp_memory *memory);
/// Iterator over vertices.
@ -604,22 +598,19 @@ void mgp_vertices_iterator_destroy(struct mgp_vertices_iterator *it);
/// The returned mgp_vertices_iterator needs to be deallocated with
/// mgp_vertices_iterator_destroy.
/// NULL is returned if unable to allocate a new iterator.
struct mgp_vertices_iterator *mgp_graph_iter_vertices(
const struct mgp_graph *g, struct mgp_memory *memory);
struct mgp_vertices_iterator *mgp_graph_iter_vertices(const struct mgp_graph *g, struct mgp_memory *memory);
/// Get the current vertex pointed to by the iterator.
/// When the mgp_vertices_iterator_next is invoked, the previous
/// mgp_vertex is invalidated and its value must not be used.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_vertex *mgp_vertices_iterator_get(
const struct mgp_vertices_iterator *it);
const struct mgp_vertex *mgp_vertices_iterator_get(const struct mgp_vertices_iterator *it);
/// Advance the iterator to the next vertex and return it.
/// The previous mgp_vertex obtained through mgp_vertices_iterator_get
/// will be invalidated, and you must not use its value.
/// NULL is returned if the end of the iteration has been reached.
const struct mgp_vertex *mgp_vertices_iterator_next(
struct mgp_vertices_iterator *it);
const struct mgp_vertex *mgp_vertices_iterator_next(struct mgp_vertices_iterator *it);
///@}
/// @name Type System
@ -718,8 +709,8 @@ struct mgp_proc;
/// Passed in arguments will not live longer than the callback's execution.
/// Therefore, you must not store them globally or use the passed in mgp_memory
/// to allocate global resources.
typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *,
struct mgp_result *, struct mgp_memory *);
typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *, struct mgp_result *,
struct mgp_memory *);
/// Register a read-only procedure with a module.
///
@ -730,9 +721,7 @@ typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *,
///
/// NULL is returned if unable to allocate memory for mgp_proc; if `name` is
/// not valid or a procedure with the same name was already registered.
struct mgp_proc *mgp_module_add_read_procedure(struct mgp_module *module,
const char *name,
mgp_proc_cb cb);
struct mgp_proc *mgp_module_add_read_procedure(struct mgp_module *module, const char *name, mgp_proc_cb cb);
/// Add a required argument to a procedure.
///
@ -748,8 +737,7 @@ struct mgp_proc *mgp_module_add_read_procedure(struct mgp_module *module,
/// 0 is returned if unable to allocate memory for an argument; if invoking this
/// function after setting an optional argument or if `name` is not valid.
/// Non-zero is returned on success.
int mgp_proc_add_arg(struct mgp_proc *proc, const char *name,
const struct mgp_type *type);
int mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
/// Add an optional argument with a default value to a procedure.
///
@ -772,8 +760,7 @@ int mgp_proc_add_arg(struct mgp_proc *proc, const char *name,
/// 0 is returned if unable to allocate memory for an argument; if `name` is
/// not valid or `default_value` does not satisfy `type`. Non-zero is returned
/// on success.
int mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name,
const struct mgp_type *type,
int mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type,
const struct mgp_value *default_value);
/// Add a result field to a procedure.
@ -787,15 +774,13 @@ int mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name,
/// 0 is returned if unable to allocate memory for a result field; if
/// `name` is not valid or if a result field with the same name was already
/// added. Non-zero is returned on success.
int mgp_proc_add_result(struct mgp_proc *proc, const char *name,
const struct mgp_type *type);
int mgp_proc_add_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
/// Add a result field to a procedure and mark it as deprecated.
///
/// This is the same as mgp_proc_add_result, but the result field will be marked
/// as deprecated.
int mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name,
const struct mgp_type *type);
int mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
///@}
/// @name Execution

View File

@ -6,21 +6,20 @@
#include <regex>
#include <type_traits>
#include "module.hpp"
#include "utils/algorithm.hpp"
#include "utils/logging.hpp"
#include "utils/math.hpp"
#include "utils/memory.hpp"
#include "utils/string.hpp"
// This file contains implementation of top level C API functions, but this is
// all actually part of query::procedure. So use that namespace for simplicity.
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace query::procedure;
void *mgp_alloc(mgp_memory *memory, size_t size_in_bytes) {
return mgp_aligned_alloc(memory, size_in_bytes, alignof(std::max_align_t));
}
namespace {
void *mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const size_t alignment) {
void *MgpAlignedAllocImpl(utils::MemoryResource &memory, const size_t size_in_bytes, const size_t alignment) {
if (size_in_bytes == 0U || !utils::IsPow2(alignment)) return nullptr;
// Simplify alignment by always using values greater or equal to max_align.
const size_t alloc_align = std::max(alignment, alignof(std::max_align_t));
@ -37,7 +36,7 @@ void *mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const si
const size_t alloc_size = bytes_for_header + size_in_bytes;
if (alloc_size < size_in_bytes) return nullptr;
try {
void *ptr = memory->impl->Allocate(alloc_size, alloc_align);
void *ptr = memory.Allocate(alloc_size, alloc_align);
char *data = reinterpret_cast<char *>(ptr) + bytes_for_header;
std::memcpy(data - sizeof(size_in_bytes), &size_in_bytes, sizeof(size_in_bytes));
std::memcpy(data - sizeof(size_in_bytes) - sizeof(alloc_align), &alloc_align, sizeof(alloc_align));
@ -47,7 +46,7 @@ void *mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const si
}
}
void mgp_free(mgp_memory *memory, void *const p) {
void MgpFreeImpl(utils::MemoryResource &memory, void *const p) {
if (!p) return;
char *const data = reinterpret_cast<char *>(p);
// Read the header containing size & alignment info.
@ -63,9 +62,31 @@ void mgp_free(mgp_memory *memory, void *const p) {
const size_t alloc_size = bytes_for_header + size_in_bytes;
// Get the original ptr we allocated.
void *const original_ptr = data - bytes_for_header;
memory->impl->Deallocate(original_ptr, alloc_size, alloc_align);
memory.Deallocate(original_ptr, alloc_size, alloc_align);
}
} // namespace
void *mgp_alloc(mgp_memory *memory, size_t size_in_bytes) {
return mgp_aligned_alloc(memory, size_in_bytes, alignof(std::max_align_t));
}
void *mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const size_t alignment) {
return MgpAlignedAllocImpl(*memory->impl, size_in_bytes, alignment);
}
void mgp_free(mgp_memory *memory, void *const p) { MgpFreeImpl(*memory->impl, p); }
void *mgp_global_alloc(size_t size_in_bytes) {
return mgp_global_aligned_alloc(size_in_bytes, alignof(std::max_align_t));
}
void *mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment) {
return MgpAlignedAllocImpl(gModuleRegistry.GetSharedMemoryResource(), size_in_bytes, alignment);
}
void mgp_global_free(void *const p) { MgpFreeImpl(gModuleRegistry.GetSharedMemoryResource(), p); }
namespace {
// May throw whatever the constructor of U throws. `std::bad_alloc` is handled

View File

@ -1,4 +1,5 @@
#include "query/procedure/module.hpp"
#include "utils/memory.hpp"
extern "C" {
#include <dlfcn.h>
@ -478,6 +479,8 @@ void ModuleRegistry::UnloadAllModules() {
DoUnloadAllModules();
}
utils::MemoryResource &ModuleRegistry::GetSharedMemoryResource() { return *shared_; }
std::optional<std::pair<procedure::ModulePtr, const mgp_proc *>> FindProcedure(
const ModuleRegistry &module_registry, const std::string_view &fully_qualified_procedure_name,
utils::MemoryResource *memory) {

View File

@ -52,6 +52,7 @@ class ModulePtr final {
class ModuleRegistry final {
std::map<std::string, std::unique_ptr<Module>, std::less<>> modules_;
mutable utils::RWLock lock_{utils::RWLock::Priority::WRITE};
std::unique_ptr<utils::MemoryResource> shared_{std::make_unique<utils::ResourceWithOutOfMemoryException>()};
bool RegisterModule(const std::string_view &name, std::unique_ptr<Module> module);
@ -96,6 +97,9 @@ class ModuleRegistry final {
/// Takes a write lock.
void UnloadAllModules();
/// Returns the shared memory allocator used by modules
utils::MemoryResource &GetSharedMemoryResource();
private:
std::vector<std::filesystem::path> modules_dirs_;
};

View File

@ -1,2 +1,11 @@
add_subdirectory(procedures)
add_executable(memgraph__e2e__memory__control memory_control.cpp)
target_link_libraries(memgraph__e2e__memory__control gflags mgclient mg-utils mg-io Threads::Threads)
add_executable(memgraph__e2e__memory__limit_global_alloc memory_limit_global_alloc.cpp)
target_link_libraries(memgraph__e2e__memory__limit_global_alloc gflags mgclient mg-utils mg-io Threads::Threads)
add_executable(memgraph__e2e__memory__limit_global_alloc_proc memory_limit_global_alloc_proc.cpp)
target_link_libraries(memgraph__e2e__memory__limit_global_alloc_proc gflags mgclient mg-utils mg-io Threads::Threads)

View File

@ -0,0 +1,26 @@
#include <gflags/gflags.h>
#include <mgclient.hpp>
#include "utils/logging.hpp"
#include "utils/timer.hpp"
DEFINE_uint64(bolt_port, 7687, "Bolt port");
DEFINE_uint64(timeout, 120, "Timeout seconds");
int main(int argc, char **argv) {
google::SetUsageMessage("Memgraph E2E Memory Limit For Global Allocators");
gflags::ParseCommandLineFlags(&argc, &argv, true);
logging::RedirectToStderr();
mg::Client::Init();
auto client =
mg::Client::Connect({.host = "127.0.0.1", .port = static_cast<uint16_t>(FLAGS_bolt_port), .use_ssl = false});
if (!client) {
LOG_FATAL("Failed to connect!");
}
bool result = client->Execute("CALL libglobal_memory_limit.procedure() YIELD *");
MG_ASSERT(result == false);
return 0;
}

View File

@ -0,0 +1,31 @@
#include <gflags/gflags.h>
#include <mgclient.hpp>
#include <algorithm>
#include "utils/logging.hpp"
#include "utils/timer.hpp"
DEFINE_uint64(bolt_port, 7687, "Bolt port");
DEFINE_uint64(timeout, 120, "Timeout seconds");
int main(int argc, char **argv) {
google::SetUsageMessage("Memgraph E2E Memory Limit For Global Allocators");
gflags::ParseCommandLineFlags(&argc, &argv, true);
logging::RedirectToStderr();
mg::Client::Init();
auto client =
mg::Client::Connect({.host = "127.0.0.1", .port = static_cast<uint16_t>(FLAGS_bolt_port), .use_ssl = false});
if (!client) {
LOG_FATAL("Failed to connect!");
}
bool result = client->Execute("CALL libglobal_memory_limit_proc.error() YIELD *");
auto result1 = client->FetchAll();
MG_ASSERT(result1 != std::nullopt && result1->size() == 0);
result = client->Execute("CALL libglobal_memory_limit_proc.success() YIELD *");
auto result2 = client->FetchAll();
MG_ASSERT(result2 != std::nullopt && result2->size() > 0);
return 0;
}

View File

@ -0,0 +1,5 @@
add_library(global_memory_limit SHARED global_memory_limit.c)
target_include_directories(global_memory_limit PRIVATE ${CMAKE_SOURCE_DIR}/include)
add_library(global_memory_limit_proc SHARED global_memory_limit_proc.c)
target_include_directories(global_memory_limit_proc PRIVATE ${CMAKE_SOURCE_DIR}/include)

View File

@ -0,0 +1,36 @@
#include "mg_procedure.h"
int *gVal = NULL;
void set_error(struct mgp_result *result) { mgp_result_set_error_msg(result, "Something went wrong"); }
static void procedure(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory) {
struct mgp_result_record *record = mgp_result_new_record(result);
if (record == NULL) return set_error(result);
struct mgp_value *result_msg = mgp_value_make_string("mgp_init_module allocation works", memory);
if (result_msg == NULL) return set_error(result);
int result_inserted = mgp_result_record_insert(record, "result", result_msg);
mgp_value_destroy(result_msg);
if (!result_inserted) return set_error(result);
}
int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
const size_t one_gb = 1 << 30;
gVal = mgp_global_alloc(one_gb);
if (!gVal) return 1;
struct mgp_proc *proc = mgp_module_add_read_procedure(module, "procedure", procedure);
if (!proc) return 1;
if (!mgp_proc_add_result(proc, "result", mgp_type_string())) return 1;
return 0;
}
int mgp_shutdown_module() {
if (gVal) mgp_global_free(gVal);
return 0;
}

View File

@ -0,0 +1,63 @@
#include "mg_procedure.h"
int *gVal = NULL;
void set_error(struct mgp_result *result) { mgp_result_set_error_msg(result, "Something went wrong"); }
void set_out_of_memory_error(struct mgp_result *result) { mgp_result_set_error_msg(result, "Out of memory"); }
static void error(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory) {
const size_t one_gb = 1 << 30;
if (gVal) {
mgp_global_free(gVal);
gVal = NULL;
}
if (!gVal) {
gVal = mgp_global_alloc(one_gb);
if (!gVal) return set_out_of_memory_error(result);
}
struct mgp_result_record *record = mgp_result_new_record(result);
if (record == NULL) return set_error(result);
struct mgp_value *error_value = mgp_value_make_string("ERROR", memory);
if (error_value == NULL) return set_error(result);
int result_inserted = mgp_result_record_insert(record, "error_result", error_value);
mgp_value_destroy(error_value);
if (!result_inserted) return set_error(result);
}
static void success(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory) {
const size_t bytes = 1024;
if (!gVal) {
gVal = mgp_global_alloc(bytes);
if (!gVal) set_out_of_memory_error(result);
}
struct mgp_result_record *record = mgp_result_new_record(result);
if (record == NULL) return set_error(result);
struct mgp_value *success_value = mgp_value_make_string("sucess", memory);
if (success_value == NULL) return set_error(result);
int result_inserted = mgp_result_record_insert(record, "success_result", success_value);
mgp_value_destroy(success_value);
if (!result_inserted) return set_error(result);
}
int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
struct mgp_proc *error_proc = mgp_module_add_read_procedure(module, "error", error);
if (!error_proc) return 1;
if (!mgp_proc_add_result(error_proc, "error_result", mgp_type_string())) return 1;
struct mgp_proc *succ_proc = mgp_module_add_read_procedure(module, "success", success);
if (!succ_proc) return 1;
if (!mgp_proc_add_result(succ_proc, "success_result", mgp_type_string())) return 1;
return 0;
}
int mgp_shutdown_module() {
if (gVal) mgp_global_free(gVal);
return 0;
}

View File

@ -13,4 +13,14 @@ workloads:
args: ["--bolt-port", *bolt_port, "--timeout", "180"]
<<: *template_cluster
- name: "Memory limit for modules upon loading"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/"
<<: *template_cluster
- name: "Memory limit for modules inside a procedure"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/"
<<: *template_cluster

View File

@ -51,6 +51,10 @@ def run(args):
mg_instances[name] = mg_instance
log_file_path = os.path.join(BUILD_DIR, 'logs', config['log_file'])
binary_args = config['args'] + ["--log-file", log_file_path]
if 'proc' in workload:
procdir = "--query-modules-directory=" + os.path.join(BUILD_DIR, workload['proc'])
binary_args.append(procdir)
mg_instance.start(args=binary_args)
for query in config['setup_queries']:
mg_instance.query(query)