From e0a73ea417c758ceae69b6947eb8429fe50a5847 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Wed, 31 Jan 2024 13:19:34 +0100 Subject: [PATCH] new c api --- example/c_api.c | 43 ++++++--- include/metall/c_api/metall.h | 176 +++++++++++++++++++++------------- src/metall_c.cpp | 117 +++++++++++++++------- 3 files changed, 225 insertions(+), 111 deletions(-) diff --git a/example/c_api.c b/example/c_api.c index 1db9c3a3..78345071 100644 --- a/example/c_api.c +++ b/example/c_api.c @@ -4,48 +4,65 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) #include +#include #include int main(void) { // Basic allocation { - metall_open(METALL_CREATE_ONLY, "/tmp/metall1"); + metall_manager* manager = metall_create("/tmp/metall1"); - uint64_t *x = metall_malloc(sizeof(uint64_t)); + uint64_t* x = metall_malloc(manager, sizeof(uint64_t)); x[0] = 1; - metall_free(x); - - metall_close(); + metall_free(manager, x); + metall_close(manager); + metall_remove("/tmp/metall1"); } // Allocate named object { - metall_open(METALL_CREATE_ONLY, "/tmp/metall2"); + metall_manager* manager = metall_create("/tmp/metall2"); - uint64_t *array = metall_named_malloc("array", sizeof(uint64_t) * 10); + uint64_t* array = metall_named_malloc(manager, "array", + sizeof(uint64_t) * 10); array[0] = 0; - metall_flush(); + metall_flush(manager); array[1] = 1; - metall_close(); + metall_snapshot(manager, "/tmp/metall2-snap"); + metall_close(manager); } // Retrieve named object { - metall_open(METALL_OPEN_ONLY, "/tmp/metall2"); + metall_manager* manager = metall_open("/tmp/metall2"); - uint64_t *array = metall_find("array"); + uint64_t* array = metall_find(manager, "array"); assert(array[0] == 0); assert(array[1] == 1); - metall_named_free("array"); + metall_named_free(manager, "array"); + metall_close(manager); + metall_remove("/tmp/metall2"); + } + + // Retrieve object snapshot + { + metall_manager* manager = metall_open("/tmp/metall2-snap"); + + uint64_t* array = metall_find(manager, "array"); + + assert(array[0] == 0); + assert(array[1] == 1); - metall_close(); + metall_named_free(manager, "array"); + metall_close(manager); + metall_remove("/tmp/metall2-snap"); } return 0; diff --git a/include/metall/c_api/metall.h b/include/metall/c_api/metall.h index 27a979c1..c9ea7387 100644 --- a/include/metall/c_api/metall.h +++ b/include/metall/c_api/metall.h @@ -1,4 +1,4 @@ -// Copyright 2019 Lawrence Livermore National Security, LLC and other Metall +// Copyright 2024 Lawrence Livermore National Security, LLC and other Metall // Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (Apache-2.0 OR MIT) @@ -6,75 +6,121 @@ #ifndef METALL_C_API_METALL_H #define METALL_C_API_METALL_H -#include +#include +#include #ifdef __cplusplus extern "C" { #endif -/// \brief Tag to create the segment always. -/// The existing segment with the same name is over written. -#define METALL_CREATE_ONLY 1 - -/// \brief Tag to open an already created segment. -#define METALL_OPEN_ONLY 2 - -/// \brief Tag to open an already created segment as read only. -#define METALL_OPEN_READ_ONLY 3 - -/// \brief Constructs a Metall manager object -/// \param mode Open mode -/// \param path A path to the backing data store -/// \return On success, returns 0. On error, returns -1. -extern int metall_open(int mode, const char *path); - -/// \brief Destructs Metall manager object -extern void metall_close(); - -/// \brief Flush data to persistent memory -extern void metall_flush(); - -/// \brief Allocates nbytes bytes. -/// \param nbytes The Number of bytes to allocate -/// \return Returns a pointer to the allocated memory -extern void *metall_malloc(uint64_t nbytes); - -/// \brief Frees the allocated memory -/// \param ptr A pointer to the allocated memory to be free -extern void metall_free(void *ptr); - -/// \brief Allocates nbytes bytes and save the address of the allocated memory -/// with name \param name A name of the allocated memory \param nbytes A size to -/// allocate \return Returns a pointer to the allocated memory -extern void *metall_named_malloc(const char *name, uint64_t nbytes); - -/// \brief Finds a saved memory -/// \param name A name of the allocated memory to find -/// \return Returns a pointer to the allocated memory if it exist. Otherwise, -/// returns NULL. -extern void *metall_find(char *name); - -/// \brief Frees memory with the name -/// \param name A name of the allocated memory to free -extern void metall_named_free(const char *name); - -/// \brief Snapshot the entire data. -/// \param destination_path The path to store a snapshot. -/// \return On success, returns 0. On error, returns -1. -extern int snapshot(const char *destination_path); - -/// \brief Copies backing files synchronously. -/// \param source_path Source data store path. -/// \param destination_path Destination data store path. -/// \return On success, returns 0. On error, returns -1. -extern int copy(const char *source_path, const char *destination_path); - -/// \brief Check if the backing data store is consistent, -/// i.e. it was closed properly. -/// \param path A path to the backing data store. -/// \return Returns a oon-zero integer if the data store is consistent; -/// otherwise, returns 0. -extern int consistent(const char *path); +/** + * \brief Opaque struct representing a metall manager + * \note this type is internally represented by `::metall::manager` therefore pointers + * to `::metall_manager` may be reinterpret-casted to pointers to `::metall::manager` + */ +typedef struct metall_manager metall_manager; + +/** + * \brief Attempts to open the metall datastore at path + * \param path path to datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - ENOTRECOVERABLE if the given metall datastore is inconsistent + */ +metall_manager* metall_open(const char* path); + +/** + * \brief Attempts to open the metall datastore at path in read only mode + * \param path path to datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - ENOTRECOVERABLE if the given metall datastore is inconsistent + */ +metall_manager* metall_open_read_only(const char* path); + +/** + * \brief Attempts to create a metall datastore at path + * \param path path at which to create a datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - EEXIST if the given path already exists + * - ENOTRECOVERABLE if the datastore could not be created for some other reason + */ +metall_manager* metall_create(const char* path); + +/** + * \brief Creates a snapshot of the metall datastore of manager and places it at dst_path + * \param manager manager to perform snapshot + * \param dst_path path where to place the snapshot + * \return true if the snapshot was successfully created otherwise false. + */ +bool metall_snapshot(metall_manager* manager, const char* dst_path); + +/** + * \brief Flushes the given manager + * \param manager manager to flush + */ +void metall_flush(metall_manager* manager); + +/** + * \brief Closes a metall manager + */ +void metall_close(metall_manager* manager); + +/** + * \brief Removes the metall datastore at path + * \param path path to datastore to remove + * \return true on successful removal, false otherwise. On failure, sets errno to one of the following values: + * - EADDRINUSE if there is a metall manager open for the given path + * + * \warning Behaviour is undefined if there is still a metall manager for path open + */ +bool metall_remove(const char* path); + +/** + * \brief Allocates size bytes + * \param manager manager to allocate with + * \param size number of bytes to allocate + * \return pointer to allocated memory if successful otherwise returns NULL and sets errno to one of the following values + * - EINVAL + */ +void* metall_malloc(metall_manager* manager, size_t size); + +/** + * \brief Frees memory previously allocated by metall_malloc + * \param manager manager from which to free + * \param ptr memory to free + */ +void metall_free(metall_manager* manager, void* ptr); + +/** + * \brief Allocates size bytes and associates the allocated memory with a name + * \param manager manager to allocate with + * \param name A name of the allocated memory + * \param size number of bytes to allocate + * \return pointer to the allocated memory if sucessful otherwise returns NULL and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOMEM if the memory could not be allocated + */ +void* metall_named_malloc(metall_manager* manager, const char* name, + size_t size); + +/** + * \brief Finds memory that was previously allocated using metall_named_alloc + * \param manager manager to find the object in + * \param name name of the allocated memory to find + * \return pointer to the allocated memory if found. Otherwise, returns NULL and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOTENT if the object could not be found + */ +void* metall_find(metall_manager* manager, const char* name); + +/** + * \brief Frees memory previously allocated by metall_named_malloc + * \param manager manager from which to free + * \param name name of the allocated memory to free + * \return true if sucessfully freed, otherwise returns false and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOENT if the referred to object does not exist + */ +bool metall_named_free(metall_manager* manager, const char* name); #ifdef __cplusplus } diff --git a/src/metall_c.cpp b/src/metall_c.cpp index 5c920481..7efc98ca 100644 --- a/src/metall_c.cpp +++ b/src/metall_c.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 Lawrence Livermore National Security, LLC and other Metall +// Copyright 2024 Lawrence Livermore National Security, LLC and other Metall // Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (Apache-2.0 OR MIT) @@ -6,55 +6,106 @@ #include #include -metall::manager *g_manager = nullptr; +template +metall_manager* open_impl(const char* path) { + if (!metall::manager::consistent(path)) { + // prevents opening the same datastore twice + // (because opening removes the properly_closed_mark and this checks for it) + errno = ENOTRECOVERABLE; + return nullptr; + } -int metall_open(const int mode, const char *const path) { - if (mode == METALL_CREATE_ONLY) { - g_manager = new metall::manager(metall::create_only, path); - } else if (mode == METALL_OPEN_ONLY) { - g_manager = new metall::manager(metall::open_only, path); - } else if (mode == METALL_OPEN_READ_ONLY) { - g_manager = new metall::manager(metall::open_read_only, path); - } else { - g_manager = nullptr; + auto* manager = new metall::manager{open_mode{}, path}; + if (!manager->check_sanity()) { + delete manager; + errno = ENOTRECOVERABLE; + return nullptr; } - if (g_manager) { - return 0; - } else { - return -1; // error + return reinterpret_cast(manager); +} + +metall_manager* metall_open(const char* path) { + return open_impl(path); +} + +metall_manager* metall_open_read_only(const char* path) { + return open_impl(path); +} + +metall_manager* metall_create(const char* path) { + if (std::filesystem::exists(path)) { + // prevent accidental overwrite + errno = EEXIST; + return nullptr; + } + + auto* manager = new metall::manager{metall::create_only, path}; + if (!manager->check_sanity()) { + delete manager; + errno = ENOTRECOVERABLE; + return nullptr; } + + return reinterpret_cast(manager); } -void metall_close() { delete g_manager; } +bool metall_snapshot(metall_manager* manager, const char* dst_path) { + return reinterpret_cast(manager)->snapshot(dst_path); +} -void metall_flush() { g_manager->flush(); } +void metall_flush(metall_manager* manager) { + reinterpret_cast(manager)->flush(); +} -void *metall_malloc(const uint64_t nbytes) { - return g_manager->allocate(nbytes); +void metall_close(metall_manager* manager) { + delete reinterpret_cast(manager); } -void metall_free(void *const ptr) { g_manager->deallocate(ptr); } +bool metall_remove(const char* path) { + return metall::manager::remove(path); +} -void *metall_named_malloc(const char *name, const uint64_t nbytes) { - return g_manager->construct(name)[nbytes](); +void* metall_malloc(metall_manager* manager, size_t size) { + auto* ptr = reinterpret_cast(manager)->allocate(size); + if (ptr == nullptr) { + errno = ENOMEM; + } + + return ptr; } -void *metall_find(char *name) { return g_manager->find(name).first; } +void metall_free(metall_manager* manager, void* ptr) { + reinterpret_cast(manager)->deallocate(ptr); +} -void metall_named_free(const char *name) { g_manager->destroy(name); } +void* metall_named_malloc(metall_manager* manager, const char* name, + size_t size) { + auto* ptr = reinterpret_cast(manager)->construct(name)[size](); + if (ptr == nullptr) { + errno = ENOMEM; + } -int snapshot(const char *destination_path) { - if (g_manager->snapshot(destination_path)) return 0; - return -1; // Error + return ptr; } -int copy(const char *source_path, const char *destination_path) { - if (metall::manager::copy(source_path, destination_path)) return 0; - return -1; // Error +void* metall_find(metall_manager* manager, const char* name) { + auto* ptr = reinterpret_cast(manager)->find(name).first; + if (ptr == nullptr) { + errno = ENOENT; + } + + return ptr; } -int consistent(const char *path) { - if (metall::manager::consistent(path)) return 1; - return 0; +bool metall_named_free(metall_manager* manager, const char* name) { + auto const res = reinterpret_cast(manager)->destroy(name); + if (!res) { + errno = ENOENT; + } + + return res; } \ No newline at end of file