diff --git a/.gitignore b/.gitignore index adf23fad2..39dbb9219 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ doc/latex .idea build/ *.ccls-cache +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a281ae6f..26c9f1b10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,20 @@ if( NOT TARGET OpenMP::OpenMP_CXX ) set( eckit_HAVE_OMP 0 ) endif() +### OpenFAM Support + +ecbuild_add_option( FEATURE OPENFAM + DEFAULT OFF + REQUIRED_PACKAGES "LibUUID REQUIRED" "protobuf REQUIRED" "gRPC REQUIRED" "OpenFAM REQUIRED" + DESCRIPTION "Enables OpenFAM support" ) + +if( eckit_HAVE_OPENFAM ) + find_package( LibUUID REQUIRED ) + find_package( protobuf CONFIG REQUIRED ) + find_package( gRPC CONFIG REQUIRED ) + find_package( OpenFAM 3.0.1 CONFIG REQUIRED ) +endif() + ### RADOS ecbuild_add_option( FEATURE RADOS diff --git a/cmake/FindLibUUID.cmake b/cmake/FindLibUUID.cmake new file mode 100644 index 000000000..b2c83cf6c --- /dev/null +++ b/cmake/FindLibUUID.cmake @@ -0,0 +1,108 @@ +# Copyright 2024- European Centre for Medium-Range Weather Forecasts (ECMWF) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation nor +# does it submit to any jurisdiction. +# +# Requires: +# FindPackageHandleStandardArgs (CMake standard module) +# + +#[=======================================================================[.rst: +FindLibUUID +-------- + +This module finds the libuuid (from util-linux) library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``LibUUID`` + The libuuid library + +Result variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project: + +``LIB_UUID_FOUND`` + True if the libuuid library is found. +``LIB_UUID_INCLUDE_DIRS`` + Include directories needed to use libuuid. +``LIB_UUID_LIBRARIES`` + Libraries needed to link to libuuid. + +Cache variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set to help find libuuid library: + +``LIB_UUID_INCLUDE_DIR`` + where to find the libuuid headers. +``LIB_UUID_LIBRARY`` + where to find the libuuid library. + +Hints +^^^^^ + +The environment variables ``LIB_UUID_ROOT``, ``LIB_UUID_DIR``, and ``LIB_UUID_PATH`` +may also be set to help find libuuid library. + +#]=======================================================================] + +find_path(LIB_UUID_INCLUDE_DIR uuid.h + HINTS + ${LIB_UUID_ROOT} + ${LIB_UUID_DIR} + ${LIB_UUID_PATH} + ENV LIB_UUID_ROOT + ENV LIB_UUID_DIR + ENV LIB_UUID_PATH + PATH_SUFFIXES uuid +) + +find_library(LIB_UUID_LIBRARY + NAMES uuid + HINTS + ${LIB_UUID_ROOT} + ${LIB_UUID_DIR} + ${LIB_UUID_PATH} + ENV LIB_UUID_ROOT + ENV LIB_UUID_DIR + ENV LIB_UUID_PATH + PATH_SUFFIXES lib lib64 +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(LibUUID REQUIRED_VARS + LIB_UUID_LIBRARY + LIB_UUID_INCLUDE_DIR) + +if (LibUUID_FOUND) + set(LIB_UUID_LIBRARIES ${LIB_UUID_LIBRARY}) + set(LIB_UUID_INCLUDE_DIRS ${LIB_UUID_INCLUDE_DIR}) + if(NOT TARGET LibUUID) + add_library(LibUUID UNKNOWN IMPORTED) + set_target_properties(LibUUID PROPERTIES + IMPORTED_LOCATION "${LIB_UUID_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIB_UUID_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "${LIB_UUID_LIBRARY}") + endif() +endif() + +mark_as_advanced(LIB_UUID_INCLUDE_DIR LIB_UUID_LIBRARY) diff --git a/src/eckit/CMakeLists.txt b/src/eckit/CMakeLists.txt index 8ef1c1d37..8a727c053 100644 --- a/src/eckit/CMakeLists.txt +++ b/src/eckit/CMakeLists.txt @@ -266,6 +266,41 @@ list(APPEND eckit_message_srcs message/Splitter.h ) +if(HAVE_OPENFAM) +list(APPEND eckit_io_srcs +io/fam/detail/FamSessionDetail.cc +io/fam/FamConfig.h +io/fam/FamHandle.cc +io/fam/FamHandle.h +io/fam/FamList.cc +io/fam/FamList.h +io/fam/FamListIterator.cc +io/fam/FamListIterator.h +io/fam/FamMap.cc +io/fam/FamMap.h +io/fam/FamMapIterator.cc +io/fam/FamMapIterator.h +io/fam/FamName.cc +io/fam/FamName.h +io/fam/FamObject.cc +io/fam/FamObject.h +io/fam/FamObjectName.cc +io/fam/FamObjectName.h +io/fam/FamPath.cc +io/fam/FamPath.h +io/fam/FamProperty.cc +io/fam/FamProperty.h +io/fam/FamRegion.cc +io/fam/FamRegion.h +io/fam/FamRegionName.cc +io/fam/FamRegionName.h +io/fam/FamSession.cc +io/fam/FamSession.h +io/fam/FamURIManager.cc +io/fam/FamURIManager.h +) +endif(HAVE_OPENFAM) + if(HAVE_RADOS) list( APPEND eckit_io_srcs io/rados/RadosHandle.cc @@ -932,6 +967,7 @@ ecbuild_add_library( "${RADOS_INCLUDE_DIRS}" "${OPENSSL_INCLUDE_DIR}" "${AIO_INCLUDE_DIRS}" + "${LIB_UUID_INCLUDE_DIRS}" PRIVATE_LIBS "${SNAPPY_LIBRARIES}" @@ -943,6 +979,8 @@ ecbuild_add_library( "${CURL_LIBRARIES}" "${AIO_LIBRARIES}" "${RADOS_LIBRARIES}" + "${LIB_UUID_LIBRARIES}" + $<${HAVE_OPENFAM}:OpenFAM::openfam> PUBLIC_LIBS ${CMATH_LIBRARIES} @@ -978,4 +1016,3 @@ endif() if( eckit_HAVE_ECKIT_GEO ) add_subdirectory( geo ) endif() - diff --git a/src/eckit/exception/Exceptions.cc b/src/eckit/exception/Exceptions.cc index 431d9896c..e0d93680a 100644 --- a/src/eckit/exception/Exceptions.cc +++ b/src/eckit/exception/Exceptions.cc @@ -290,6 +290,14 @@ Abort::Abort(const std::string& r, const CodeLocation& loc) : Retry::Retry(const std::string& r) : Exception(std::string("Retry: ") + r) {} +PermissionDenied::PermissionDenied(const std::string& r): Exception(std::string("Permission denied: ") + r) { } + +NotFound::NotFound(const std::string& r): Exception(std::string("Not found: ") + r) { } + +AlreadyExists::AlreadyExists(const std::string& r): Exception(std::string("Already exists: ") + r) { } + +OutOfStorage::OutOfStorage(const std::string& r): Exception(std::string("Out of storage: ") + r) { } + Cancel::Cancel(const std::string& r) : Exception(std::string("Cancel: ") + r) {} diff --git a/src/eckit/exception/Exceptions.h b/src/eckit/exception/Exceptions.h index 578a1a39b..f699d2c9a 100644 --- a/src/eckit/exception/Exceptions.h +++ b/src/eckit/exception/Exceptions.h @@ -170,6 +170,26 @@ class Retry : public Exception { Retry(const std::string&); }; +class PermissionDenied: public Exception { +public: + PermissionDenied(const std::string&); +}; + +class NotFound: public Exception { +public: + NotFound(const std::string&); +}; + +class AlreadyExists: public Exception { +public: + AlreadyExists(const std::string&); +}; + +class OutOfStorage: public Exception { +public: + OutOfStorage(const std::string&); +}; + class UserError : public Exception { public: UserError(const std::string&, const CodeLocation&); diff --git a/src/eckit/filesystem/URI.cc b/src/eckit/filesystem/URI.cc index 0dec1d4e0..dc747aa30 100644 --- a/src/eckit/filesystem/URI.cc +++ b/src/eckit/filesystem/URI.cc @@ -67,6 +67,9 @@ URI::URI(const std::string& scheme, const URI& uri, const std::string& hostname, fragment_(uri.fragment_), queryValues_(uri.queryValues_) {} +URI::URI(std::string scheme, const net::Endpoint& endpoint, std::string name) noexcept: + name_(std::move(name)), scheme_(std::move(scheme)), host_(endpoint.host()), port_(endpoint.port()) { } + URI::URI(Stream& s) { s >> scheme_; s >> user_; diff --git a/src/eckit/filesystem/URI.h b/src/eckit/filesystem/URI.h index 523d3e3f8..87205a9c8 100644 --- a/src/eckit/filesystem/URI.h +++ b/src/eckit/filesystem/URI.h @@ -24,6 +24,7 @@ #include "eckit/io/Offset.h" #include "eckit/net/Endpoint.h" +#include namespace eckit { @@ -48,6 +49,7 @@ class URI { URI(const std::string& scheme, const URI& uri); URI(const std::string& scheme, const std::string& hostname, int port); URI(const std::string& scheme, const URI& uri, const std::string& hostname, int port); + URI(std::string scheme, const net::Endpoint& endpoint, std::string name) noexcept; URI(Stream& s); // Destructor @@ -62,7 +64,9 @@ class URI { DataHandle* newReadHandle(const OffsetList&, const LengthList&) const; DataHandle* newReadHandle() const; - void endpoint(const eckit::net::Endpoint& endpoint) { + net::Endpoint endpoint() const { return {host_, port_}; } + + void endpoint(const net::Endpoint& endpoint) { host_ = endpoint.host(); port_ = endpoint.port(); } diff --git a/src/eckit/io/Buffer.h b/src/eckit/io/Buffer.h index 707e8803b..e623fbf9c 100644 --- a/src/eckit/io/Buffer.h +++ b/src/eckit/io/Buffer.h @@ -40,6 +40,8 @@ class Buffer : private NonCopyable { ~Buffer(); + std::string_view view() const noexcept { return std::string_view(buffer_, size_); } + operator char*() { return buffer_; } operator const char*() const { return buffer_; } diff --git a/src/eckit/io/fam/FamConfig.h b/src/eckit/io/fam/FamConfig.h new file mode 100644 index 000000000..2d46a181f --- /dev/null +++ b/src/eckit/io/fam/FamConfig.h @@ -0,0 +1,47 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamConfig.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/net/Endpoint.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamConfig { + net::Endpoint endpoint {"127.0.0.1", -1}; + std::string sessionName {"EckitFamSession"}; + + bool operator==(const FamConfig& other) const { + return (endpoint == other.endpoint && sessionName == other.sessionName); + } + + friend std::ostream& operator<<(std::ostream& out, const FamConfig& config) { + out << "endpoint=" << config.endpoint << ", sessionName=" << config.sessionName; + return out; + } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamHandle.cc b/src/eckit/io/fam/FamHandle.cc new file mode 100644 index 000000000..79e93385f --- /dev/null +++ b/src/eckit/io/fam/FamHandle.cc @@ -0,0 +1,125 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the EC H2020 funded project IO-SEA + * (Project ID: 955811) iosea-project.eu + */ + +#include "eckit/io/fam/FamHandle.h" + +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamObject.h" +#include "eckit/log/Log.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamHandle::FamHandle(const FamObjectName& name, const Offset& position, const Length& length, const bool overwrite): + name_ {name}, overwrite_ {overwrite}, pos_ {position}, len_ {length} { } + +FamHandle::FamHandle(const FamObjectName& name, const bool overwrite): FamHandle(name, 0, 0, overwrite) { } + +//---------------------------------------------------------------------------------------------------------------------- + +void FamHandle::open(const Mode mode) { + ASSERT(!handle_ && mode_ == Mode::CLOSED); + pos_ = 0; + mode_ = mode; +} + +void FamHandle::close() { + if (mode_ == Mode::WRITE) { flush(); } + pos_ = 0; + mode_ = Mode::CLOSED; + handle_.reset(); +} + +void FamHandle::flush() { + Log::debug() << "FamHandle::flush() ?! noop\n"; +} + +Offset FamHandle::seek(const Offset& offset) { + pos_ = pos_ + offset; + + ASSERT(0 <= pos_ && size() >= pos_); + + return pos_; +} + +Length FamHandle::size() { + return handle_ ? Length(handle_->size()) : estimate(); +} + +//---------------------------------------------------------------------------------------------------------------------- + +Length FamHandle::openForRead() { + open(Mode::READ); + handle_ = name_.lookup().clone(); + return estimate(); +} + +void FamHandle::openForWrite(const Length& length) { + open(Mode::WRITE); + + try { + handle_ = name_.lookup().clone(); + if (overwrite_ && length > 0) { ASSERT(size() >= length); } + } catch (const NotFound& e) { + Log::debug() << "FamHandle::openForWrite() " << e.what() << '\n'; + ASSERT(length > 0); + handle_ = name_.allocate(length).clone(); + } + + len_ = size(); +} + +//---------------------------------------------------------------------------------------------------------------------- + +long FamHandle::read(void* buffer, const long length) { + ASSERT(mode_ == Mode::READ); + ASSERT(0 <= pos_); + + if (size() <= pos_) { return 0; } + + handle_->get(buffer, pos_, length); + + pos_ += length; + + return length; +} + +long FamHandle::write(const void* buffer, const long length) { + ASSERT(mode_ == Mode::WRITE); + + handle_->put(buffer, pos_, length); + + pos_ += length; + + return length; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamHandle::print(std::ostream& out) const { + out << "FamHandle[name=" << name_ << ", position=" << pos_ << ", mode="; + switch (mode_) { + case Mode::CLOSED: out << "closed"; break; + case Mode::READ: out << "read"; break; + case Mode::WRITE: out << "write"; break; + } + out << "]"; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamHandle.h b/src/eckit/io/fam/FamHandle.h new file mode 100644 index 000000000..1f3fea44d --- /dev/null +++ b/src/eckit/io/fam/FamHandle.h @@ -0,0 +1,81 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamHandle.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/io/DataHandle.h" +#include "eckit/io/fam/FamObjectName.h" + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +class FamHandle: public DataHandle { +public: // methods + enum class Mode { CLOSED, READ, WRITE }; + + FamHandle(const FamObjectName& name, const Offset& position, const Length& length, bool overwrite); + + FamHandle(const FamObjectName& name, bool overwrite = false); + + Length openForRead() override; + + void openForWrite(const Length& length) override; + + long read(void* buffer, long length) override; + + long write(const void* buffer, long length) override; + + void flush() override; + + void close() override; + + Offset seek(const Offset& offset) override; + + auto canSeek() const -> bool override { return true; } + + Offset position() override { return pos_; } + + Length estimate() override { return len_; } + + Length size() override; + +private: // methods + void open(Mode mode); + + void print(std::ostream& out) const override; + +private: // members + const FamObjectName name_; + + const bool overwrite_ {false}; + + Offset pos_ {0}; + Length len_ {0}; + + Mode mode_ {Mode::CLOSED}; + + std::unique_ptr handle_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamHashTable.cc b/src/eckit/io/fam/FamHashTable.cc new file mode 100644 index 000000000..f1873caa5 --- /dev/null +++ b/src/eckit/io/fam/FamHashTable.cc @@ -0,0 +1,47 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamHashTable.h" + +#include "detail/FamHashNode.h" +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamObjectName.h" +#include "eckit/io/fam/FamRegionName.h" + +// #include "detail/FamSessionDetail.h" +// #include "eckit/exception/Exceptions.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamHashTable::FamHashTable(const FamRegionName& regionName, const std::string& tableName): + region_ {regionName.lookup()}, + begin_ {initSentinel(tableName + "-hash-begin", sizeof(FamDescriptor))}, + count_ {initSentinel(tableName + "-hash-count", sizeof(size_type))} { } + +auto FamHashTable::initSentinel(const std::string& name, const fam::size_t size) const -> FamObject { + try { + return region_.allocateObject(size, name); + } catch (const AlreadyExists&) { + auto object = region_.lookupObject(name); + ASSERT(object.size() == size); + return object; + } +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamHashTable.h b/src/eckit/io/fam/FamHashTable.h new file mode 100644 index 000000000..9d3c8b47c --- /dev/null +++ b/src/eckit/io/fam/FamHashTable.h @@ -0,0 +1,108 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamHashTable.h +/// @author Metin Cakircali +/// @date Jul 2024 + +#pragma once + +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamRegion.h" +// #include "eckit/io/fam/FamRegionName.h" +// #include "eckit/io/fam/FamVector.h" +#include "eckit/io/fam/FamMapIterator.h" +#include "eckit/types/FixedString.h" + +#include +#include + +namespace eckit { + +class FamList; +// class FamRegion; +class FamRegionName; + +//---------------------------------------------------------------------------------------------------------------------- +// FAM HASHER + +/// @brief Hash functor. Override this to make a specialized hasher +template +struct FamHash { + auto operator()(const key_type& key) const noexcept -> std::size_t { + return std::hash {}(key.asString()); + /// @note example for a 3-level key + // const auto l1 = std::hash {}(key.firstLevel); + // const auto l2 = std::hash {}(key.secondLevel); + // const auto l3 = std::hash {}(key.thirdLevel); + // return l1 ^ (l2 ^ (l3 << 1)); + } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +/// @todo template: initial table size, key size, (also equal and/or hasher ?) + +/// @brief data structure is array of lists: FamVector< FamList > +// unsigned int index = key % table->size; + +class FamHashTable { + static constexpr auto keySize = 32; // template? + + static constexpr auto capacity = 1024; + +public: // types + using key_type = FixedString; + using hash_type = FamHash; + /// @todo char array ? + using value_type = char; + // using key_equal = key_equal; + using size_type = fam::size_t; + using difference_type = size_type; + + // using mapped_type = mapped_type; + // using allocator_type = allocator_type; + // using pointer = pointer; + // using const_pointer = const_pointer; + + using reference = value_type&; + using const_reference = const value_type&; + + using iterator = FamMapIterator; + using const_iterator = const FamMapIterator; + + // using local_iterator = local_iterator; + // using const_local_iterator = const_local_iterator; + + using node_type = FamList; + +public: // methods + FamHashTable(const FamRegionName& regionName, const std::string& tableName); + +private: // methods + auto initSentinel(const std::string& name, fam::size_t size) const -> FamObject; + +private: // members + FamRegion region_; + + FamObject begin_; + FamObject count_; + + std::array table_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamList.cc b/src/eckit/io/fam/FamList.cc new file mode 100644 index 000000000..9e758ec6a --- /dev/null +++ b/src/eckit/io/fam/FamList.cc @@ -0,0 +1,187 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamList.h" + +#include "detail/FamListNode.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamRegion.h" +#include "eckit/io/fam/FamRegionName.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +namespace { + +auto initSentinel(const FamRegion& region, const std::string& objectName, const fam::size_t objectSize) -> FamObject { + try { + return region.allocateObject(objectSize, objectName); + } catch (const AlreadyExists&) { + auto object = region.lookupObject(objectName); + ASSERT(object.size() == objectSize); + return object; + } +} + +} // namespace + +//---------------------------------------------------------------------------------------------------------------------- + +FamList::FamList(const FamRegion& region, const Descriptor& desc): + region_ {region}, + head_ {region_.proxyObject(desc.head)}, + tail_ {region_.proxyObject(desc.tail)}, + size_ {region_.proxyObject(desc.size)} { + ASSERT(region.index() == desc.region); +} + +FamList::FamList(const FamRegion& region, const std::string& listName): + region_ {region}, + head_ {initSentinel(region_, listName + "-list-head", sizeof(FamListNode))}, + tail_ {initSentinel(region_, listName + "-list-tail", sizeof(FamListNode))}, + size_ {initSentinel(region_, listName + "-list-size", sizeof(size_type))} { + // set head's next to tail's prev + if (FamListNode::getNextOffset(head_) == 0) { head_.put(tail_.descriptor(), offsetof(FamListNode, next)); } + // set tail's prev to head's next + if (FamListNode::getPrevOffset(tail_) == 0) { tail_.put(head_.descriptor(), offsetof(FamListNode, prev)); } +} + +FamList::FamList(const FamRegionName& name): FamList(name.lookup(), name.path().objectName) { } + +FamList::~FamList() = default; + +auto FamList::descriptor() const -> Descriptor { + return {region_.index(), head_.offset(), tail_.offset(), size_.offset()}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// iterators + +auto FamList::begin() const -> iterator { + return {region_.proxyObject(FamListNode::getNextOffset(head_))}; +} + +auto FamList::cbegin() const -> const_iterator { + return {region_.proxyObject(FamListNode::getNextOffset(head_))}; +} + +auto FamList::end() const -> iterator { + return {region_.proxyObject(tail_.offset())}; +} + +auto FamList::cend() const -> const_iterator { + return {region_.proxyObject(tail_.offset())}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// accessors + +auto FamList::front() const -> Buffer { + return std::move(*begin()); +} + +auto FamList::back() const -> Buffer { + return std::move(*--end()); +} + +//---------------------------------------------------------------------------------------------------------------------- +// modifiers + +void FamList::push_front(const void* data, const size_type length) { + // allocate an object + auto newObject = region_.allocateObject(sizeof(FamListNode) + length); + + // set new object's previous to head + newObject.put(head_.descriptor(), offsetof(FamListNode, prev)); + + // set head's next to new object + const auto prevOffset = head_.swap(offsetof(FamListNode, next.offset), newObject.offset()); + const auto oldObject = region_.proxyObject(prevOffset); + + // set old object's prev to new object + oldObject.put(newObject.descriptor(), offsetof(FamListNode, prev)); + // set new object's next to old object + newObject.put(oldObject.descriptor(), offsetof(FamListNode, next)); + + // finally put the data + newObject.put(length, offsetof(FamListNode, length)); + newObject.put(data, sizeof(FamListNode), length); + + // increment size + size_.add(0, 1UL); +} + +void FamList::push_back(const void* data, const size_type length) { + // allocate an object + auto newObject = region_.allocateObject(sizeof(FamListNode) + length); + + // set new object's next to tail + newObject.put(tail_.descriptor(), offsetof(FamListNode, next)); + + // set tail's prev to new object + const auto prevOffset = tail_.swap(offsetof(FamListNode, prev.offset), newObject.offset()); + const auto oldObject = region_.proxyObject(prevOffset); + + // set old object's next to new object + oldObject.put(newObject.descriptor(), offsetof(FamListNode, next)); + // set new object's prev to old object + newObject.put(oldObject.descriptor(), offsetof(FamListNode, prev)); + + // finally put the data + newObject.put(length, offsetof(FamListNode, length)); + newObject.put(data, sizeof(FamListNode), length); + + // increment size + size_.add(0, 1UL); +} + +void FamList::pop_front() { + NOTIMP; +} + +void FamList::pop_back() { + NOTIMP; +} + +//---------------------------------------------------------------------------------------------------------------------- +// capacity + +auto FamList::size() const -> size_type { + return size_.get(); +} + +auto FamList::empty() const -> bool { + return (FamListNode::getNextOffset(head_) == tail_.offset()); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamList::print(std::ostream& out) const { + out << "FamList[size=" << size() << ", region=" << region_ << ", head=" << head_ << ", tail=" << tail_ << "]"; +} + +std::ostream& operator<<(std::ostream& out, const FamList& list) { + list.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamList.h b/src/eckit/io/fam/FamList.h new file mode 100644 index 000000000..8fed0a4a4 --- /dev/null +++ b/src/eckit/io/fam/FamList.h @@ -0,0 +1,106 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamList.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamListIterator.h" +#include "eckit/io/fam/FamRegion.h" + +#include +#include + +namespace eckit { + +class FamRegionName; + +//---------------------------------------------------------------------------------------------------------------------- + +class FamList { +public: // types + using size_type = fam::size_t; + using iterator = FamListIterator; + using const_iterator = FamListConstIterator; + + struct Descriptor { + fam::index_t region; // region ID + fam::index_t head; // offset of head sentinel + fam::index_t tail; // offset of tail sentinel + fam::index_t size; // offset of size sentinel + }; + +public: // methods + FamList(const FamRegion& region, const Descriptor& desc); + + FamList(const FamRegion& region, const std::string& listName); + + FamList(const FamRegionName& name); + + ~FamList(); + + auto descriptor() const -> Descriptor; + + // capacity + + auto size() const -> size_type; + + auto empty() const -> bool; + + // iterators + + auto begin() const -> iterator; + + auto cbegin() const -> const_iterator; + + auto end() const -> iterator; + + auto cend() const -> const_iterator; + + // accessors + + auto front() const -> Buffer; + + auto back() const -> Buffer; + + // modifiers + + // void clear() noexcept; + + void push_back(const void* data, size_type length); + + void push_front(const void* data, size_type length); + + void pop_front(); + + void pop_back(); + +private: // methods + void print(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const FamList& list); + +private: // members + FamRegion region_; + FamObject head_; + FamObject tail_; + FamObject size_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamListIterator.cc b/src/eckit/io/fam/FamListIterator.cc new file mode 100644 index 000000000..4e8b64289 --- /dev/null +++ b/src/eckit/io/fam/FamListIterator.cc @@ -0,0 +1,60 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamListIterator.h" + +#include "detail/FamListNode.h" +#include "detail/FamSessionDetail.h" +#include "eckit/exception/Exceptions.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- +// ITERATOR + +FamListIterator::FamListIterator(const value_type& object): object_ {object} { } + +auto FamListIterator::operator++() -> FamListIterator& { + if (const auto next = FamListNode::getNext(object_); next.offset > 0) { + invalid_ = true; + object_.replaceWith(next); + } + return *this; +} + +auto FamListIterator::operator--() -> FamListIterator& { + if (const auto prev = FamListNode::getPrev(object_); prev.offset > 0) { + invalid_ = true; + object_.replaceWith(prev); + } + return *this; +} + +auto FamListIterator::operator==(const FamListIterator& other) const -> bool { + return other.object_ == object_; +} + +auto FamListIterator::operator->() -> pointer { + return &object_; +} + +auto FamListIterator::operator*() -> reference { + if (invalid_) { FamListNode::getData(object_, data_); } + return data_; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamListIterator.h b/src/eckit/io/fam/FamListIterator.h new file mode 100644 index 000000000..e0721743a --- /dev/null +++ b/src/eckit/io/fam/FamListIterator.h @@ -0,0 +1,80 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamListIterator.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/Buffer.h" +#include "eckit/io/fam/FamObject.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- +// ITERATOR + +class FamListIterator { +public: // types + using iterator_category = std::bidirectional_iterator_tag; + + using value_type = FamObject; + using pointer = value_type*; + using reference = Buffer&; + +public: // methods + FamListIterator(const value_type& object); + + // iterate forwards + auto operator++() -> FamListIterator&; + + // iterate backwards + auto operator--() -> FamListIterator&; + + auto operator==(const FamListIterator& other) const -> bool; + + auto operator!=(const FamListIterator& other) const -> bool { return !operator==(other); } + + auto operator->() -> pointer; + + auto operator*() -> reference; + +private: // members + bool invalid_ {true}; + Buffer data_ {0}; + + value_type object_; +}; + +//---------------------------------------------------------------------------------------------------------------------- +// CONST ITERATOR + +class FamListConstIterator: public FamListIterator { + using FamListIterator::FamListIterator; + + using value_type = FamListIterator::value_type; + using pointer = const value_type*; + using reference = const Buffer&; + +public: // methods + auto operator->() -> pointer { return FamListIterator::operator->(); } + + auto operator*() -> reference { return FamListIterator::operator*(); } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamMap.cc b/src/eckit/io/fam/FamMap.cc new file mode 100644 index 000000000..27fadebd1 --- /dev/null +++ b/src/eckit/io/fam/FamMap.cc @@ -0,0 +1,158 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamMap.h" + +#include "detail/FamMapNode.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamRegion.h" +#include "eckit/io/fam/FamRegionName.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamMap::FamMap(const FamRegionName& regionName, const std::string& tableName): + region_ {regionName.lookup()}, + root_ {initSentinel(tableName + "-map-root", sizeof(FamMapNode))}, + table_ {initSentinel(tableName + "-map-table", capacity * sizeof(FamMapNode))}, + count_ {initSentinel(tableName + "-map-count", sizeof(size_type))} { } + +auto FamMap::initSentinel(const std::string& objectName, const size_type objectSize) const -> FamObject { + try { + return region_.allocateObject(objectSize, objectName); + } catch (const AlreadyExists&) { + auto object = region_.lookupObject(objectName); + ASSERT(object.size() == objectSize); + return object; + } +} + +//---------------------------------------------------------------------------------------------------------------------- +// iterators + +auto FamMap::begin() const -> iterator { + return {region_, FamMapNode::getNextOffset(root_)}; +} + +auto FamMap::cbegin() const -> const_iterator { + return {region_, FamMapNode::getNextOffset(root_)}; +} + +auto FamMap::end() const -> iterator { + return {region_, 0}; +} + +auto FamMap::cend() const -> const_iterator { + return {region_, 0}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// lookup + +auto FamMap::at(const key_type& key) -> reference { + NOTIMP; +} + +auto FamMap::at(const key_type& key) const -> const_reference { + NOTIMP; +} + +auto FamMap::find(const key_type& key) -> iterator { + NOTIMP; +} + +auto FamMap::find(const key_type& key) const -> const_iterator { + NOTIMP; +} + +auto FamMap::contains(const key_type& key) const -> bool { + NOTIMP; +} + +// auto FamMap::front() const -> Buffer { +// return std::move(*begin()); +// } +// +// auto FamMap::back() const -> Buffer { +// return std::move(*--end()); +// } + +//---------------------------------------------------------------------------------------------------------------------- +// modifiers + +auto FamMap::insert(const value_type& value) -> iterator { + NOTIMP; +} + +auto FamMap::insert(value_type&& value) -> iterator { + NOTIMP; +} + +// void FamMap::push_back(const void* data, const fam::size_t length) { +// // allocate an object +// auto newObject = region_.allocateObject(sizeof(FamNode) + length); +// +// // set new object's next to tail +// newObject.put(tail_.descriptor(), offsetof(FamNode, next)); +// +// // set tail's prev to new object +// const auto prevOffset = tail_.swap(offsetof(FamNode, prev.offset), newObject.offset()); +// +// const auto oldObject = region_.proxyObject(prevOffset); +// +// // set old object's next to new object +// oldObject.put(newObject.descriptor(), offsetof(FamNode, next)); +// +// // set new object's prev to old object +// newObject.put(oldObject.descriptor(), offsetof(FamNode, prev)); +// +// // finally the data +// newObject.put(length, offsetof(FamNode, length)); +// newObject.put(data, sizeof(FamNode), length); +// +// // increment size +// size_.add(0, 1UL); +// } + +//---------------------------------------------------------------------------------------------------------------------- +// capacity + +auto FamMap::size() const -> size_type { + return count_.get(); +} + +auto FamMap::empty() const -> bool { + return size() == 0; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamMap::print(std::ostream& out) const { + out << "FamMap[capacity=" << capacity << ",region=" << region_ << ",root=" << root_ << ",count=" << count_ << ']'; +} + +std::ostream& operator<<(std::ostream& out, const FamMap& list) { + list.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamMap.h b/src/eckit/io/fam/FamMap.h new file mode 100644 index 000000000..68ca21dc3 --- /dev/null +++ b/src/eckit/io/fam/FamMap.h @@ -0,0 +1,136 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamMap.h +/// @author Metin Cakircali +/// @date Jul 2024 + +#pragma once + +#include "eckit/io/fam/FamHashTable.h" +// #include "eckit/io/fam/FamMapIterator.h" + +#include +#include + +namespace eckit { + +class FamRegionName; + +//---------------------------------------------------------------------------------------------------------------------- + +/// @brief FamMap is an associative key-value container on FAM. Each element is organized depending on the +/// hash value of its key. +class FamMap { + static constexpr auto keySize = 32; // template? + + static constexpr auto capacity = 1024; + +public: // types + using key_type = FixedString; + using hash_type = FamHash; + using value_type = char; + // using key_equal = key_equal; + using size_type = fam::size_t; + using difference_type = size_type; + + // using mapped_type = mapped_type; + // using allocator_type = allocator_type; + // using pointer = pointer; + // using const_pointer = const_pointer; + + using reference = value_type&; + using const_reference = const value_type&; + + using iterator = FamMapIterator; + using const_iterator = FamMapConstIterator; + + // using local_iterator = local_iterator; + // using const_local_iterator = const_local_iterator; + + using node_type = FamList; + + // using insert_return_type = std::pair; + +public: // methods + FamMap(const FamRegionName& regionName, const std::string& tableName); + + ~FamMap() = default; + + // capacity + + auto size() const -> size_type; + + auto empty() const -> bool; + + auto max_size() const noexcept -> size_type { return capacity; } + + // iterators + + auto begin() const -> iterator; + + auto cbegin() const -> const_iterator; + + auto end() const -> iterator; + + auto cend() const -> const_iterator; + + // lookup + + // Returns reference to the element with specified key. + // throws std::out_of_range if not found + auto at(const key_type& key) -> reference; + auto at(const key_type& key) const -> const_reference; + + // operator[] ? + + // size_type count( const Key& key ) const; + + auto find(const key_type& key) -> iterator; + auto find(const key_type& key) const -> const_iterator; + + auto contains(const key_type& key) const -> bool; + + // modifiers + + auto insert(const value_type& value) -> iterator; + auto insert(value_type&& value) -> iterator; + + // void push_back(const void* data, size_type length); + // void push_front(const void* data, size_type length); + // void pop_front(); + // void pop_back(); + + // void clear() noexcept; + +private: // methods + auto initSentinel(const std::string& name, size_type size) const -> FamObject; + + void print(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const FamMap& list); + +private: // members + FamRegion region_; + FamObject root_; + FamObject table_; + FamObject count_; + + // FamHashTable table_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamMapIterator.cc b/src/eckit/io/fam/FamMapIterator.cc new file mode 100644 index 000000000..8c0d65e09 --- /dev/null +++ b/src/eckit/io/fam/FamMapIterator.cc @@ -0,0 +1,50 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamMapIterator.h" + +#include "detail/FamMapNode.h" + +// #include "detail/FamSessionDetail.h" +// #include "eckit/exception/Exceptions.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamMapIterator::FamMapIterator(const FamRegion& region, const fam::index_t offset): + region_ {region}, node_ {region_.proxyObject(offset)} { } + +auto FamMapIterator::operator++() -> FamMapIterator& { + if (const auto next = FamMapNode::getNext(node_); next.region > 0) { + node_.replaceWith(next); + list_.reset(); + } + return *this; +} + +auto FamMapIterator::operator->() -> pointer { + if (list_) { list_ = FamMapNode::getList(region_, node_); } + return list_.get(); +} + +auto FamMapIterator::operator*() -> reference { + if (list_) { list_ = FamMapNode::getList(region_, node_); } + return *list_; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamMapIterator.h b/src/eckit/io/fam/FamMapIterator.h new file mode 100644 index 000000000..ce359e634 --- /dev/null +++ b/src/eckit/io/fam/FamMapIterator.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamMapIterator.h +/// @author Metin Cakircali +/// @date Jul 2024 + +#pragma once + +#include "eckit/io/fam/FamList.h" +#include "eckit/io/fam/FamRegion.h" + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- +// ITERATOR + +class FamMapIterator { +public: // types + using iterator_category = std::forward_iterator_tag; + + using value_type = FamList; + using pointer = value_type*; + using reference = value_type&; + +public: // methods + FamMapIterator(const FamRegion& region, fam::index_t offset); + + auto operator++() -> FamMapIterator&; + + auto operator==(const FamMapIterator& other) const -> bool { return other.node_ == node_; } + + auto operator!=(const FamMapIterator& other) const -> bool { return !operator==(other); } + + auto operator->() -> pointer; + + auto operator*() -> reference; + +private: // members + FamRegion region_; + FamObject node_; + + std::unique_ptr list_; +}; + +//---------------------------------------------------------------------------------------------------------------------- +// CONST ITERATOR + +class FamMapConstIterator: public FamMapIterator { + using FamMapIterator::FamMapIterator; + + using value_type = FamMapIterator::value_type; + using pointer = const value_type*; + using reference = const value_type&; + +public: // methods + auto operator->() -> pointer { return FamMapIterator::operator->(); } + + auto operator*() -> reference { return FamMapIterator::operator*(); } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamName.cc b/src/eckit/io/fam/FamName.cc new file mode 100644 index 000000000..980bb52e8 --- /dev/null +++ b/src/eckit/io/fam/FamName.cc @@ -0,0 +1,76 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamName.h" + +#include "eckit/filesystem/URI.h" +#include "eckit/io/fam/FamPath.h" +#include "eckit/io/fam/FamSession.h" +#include "eckit/net/Endpoint.h" +#include "eckit/serialisation/Stream.h" + +#include +#include +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamName::FamName(const net::Endpoint& endpoint, FamPath path) : endpoint_ {endpoint}, path_ {std::move(path)} { } + +FamName::FamName(const URI& uri) : FamName(uri.endpoint(), uri) { } + +FamName::FamName(Stream& stream) : endpoint_ {stream}, path_ {stream} { } + +FamName::~FamName() = default; + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamName::session() const -> FamSession::SPtr { + return FamSession::instance().getOrAdd({endpoint_}); +} + +auto FamName::asString() const -> std::string { + std::ostringstream oss; + oss << FamPath::scheme << "://" << endpoint_ << path_; + return oss.str(); +} + +auto FamName::uri() const -> URI { + return {FamPath::scheme, endpoint_, path_.asString()}; +} + +void FamName::print(std::ostream& out) const { + out << "endpoint=" << endpoint_ << ", path=" << path_; +} + +//---------------------------------------------------------------------------------------------------------------------- + +std::ostream& operator<<(std::ostream& out, const FamName& name) { + name.print(out); + return out; +} + +auto operator<<(Stream& out, const FamName& name) -> Stream& { + out << name.endpoint_; + out << name.path_; + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamName.h b/src/eckit/io/fam/FamName.h new file mode 100644 index 000000000..9e01d528a --- /dev/null +++ b/src/eckit/io/fam/FamName.h @@ -0,0 +1,75 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamName.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/io/fam/FamPath.h" +#include "eckit/io/fam/FamSession.h" +#include "eckit/net/Endpoint.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +class FamName { +public: // methods + FamName(const net::Endpoint& endpoint, FamPath path); + + FamName(const URI& uri); + + FamName(Stream& stream); + + virtual ~FamName(); + + virtual auto exists() const -> bool = 0; + + /// @todo implement + // virtual auto lookup() const -> FamItem = 0; + + auto asString() const -> std::string; + + auto uri() const -> URI; + + auto endpoint() const -> const net::Endpoint& { return endpoint_; } + + auto path() const -> const FamPath& { return path_; } + +protected: // methods + auto session() const -> FamSession::SPtr; + + auto path() -> FamPath& { return path_; } + + virtual void print(std::ostream& out) const; + + friend auto operator<<(std::ostream& out, const FamName& name) -> std::ostream&; + + friend auto operator<<(Stream& out, const FamName& name) -> Stream&; + +private: // members + net::Endpoint endpoint_; + + FamPath path_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamObject.cc b/src/eckit/io/fam/FamObject.cc new file mode 100644 index 000000000..d5defaae5 --- /dev/null +++ b/src/eckit/io/fam/FamObject.cc @@ -0,0 +1,207 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamObject.h" + +#include "detail/FamSessionDetail.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/Buffer.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamObject::FamObject(FamSessionDetail& session, std::unique_ptr object): + session_ {session.getShared()}, object_ {std::move(object)} { + ASSERT(session_); + ASSERT(object_); +} + +FamObject::~FamObject() = default; + +auto FamObject::clone() const -> UPtr { + auto clone = std::make_unique(object_->get_global_descriptor()); + clone->set_used_memsrv_cnt(object_->get_used_memsrv_cnt()); + clone->set_memserver_ids(object_->get_memserver_ids()); + clone->set_size(object_->get_size()); + clone->set_perm(object_->get_perm()); + clone->set_name(object_->get_name()); + clone->set_desc_status(object_->get_desc_status()); + clone->set_interleave_size(object_->get_interleave_size()); + clone->set_uid(object_->get_uid()); + clone->set_gid(object_->get_gid()); + return std::make_unique(*session_, std::move(clone)); +} + +bool FamObject::operator==(const FamObject& other) const { + const auto desc = object_->get_global_descriptor(); + const auto oDesc = other.object_->get_global_descriptor(); + return (desc.regionId == oDesc.regionId && desc.offset == oDesc.offset); +} + +//---------------------------------------------------------------------------------------------------------------------- +// OPERATIONS + +void FamObject::replaceWith(const FamDescriptor& object) { + object_ = std::make_unique(Fam_Global_Descriptor {object.region, object.offset}); +} + +void FamObject::deallocate() const { + session_->deallocateObject(*object_); +} + +auto FamObject::exists() const -> bool { + return (object_->get_desc_status() != FamDescriptorStatus::DESC_INVALID); +} + +//---------------------------------------------------------------------------------------------------------------------- +// PROPERTIES + +auto FamObject::regionId() const -> fam::index_t { + return object_->get_global_descriptor().regionId; +} + +auto FamObject::offset() const -> fam::index_t { + return object_->get_global_descriptor().offset; +} + +auto FamObject::size() const -> fam::size_t { + return object_->get_size(); +} + +auto FamObject::permissions() const -> fam::perm_t { + return object_->get_perm(); +} + +auto FamObject::name() const -> std::string { + return object_->get_name() ? object_->get_name() : ""; +} + +auto FamObject::property() const -> FamProperty { + return {size(), permissions(), name(), object_->get_uid(), object_->get_gid()}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// DATA + +void FamObject::put(const void* buffer, const fam::size_t offset, const fam::size_t length) const { + session_->put(*object_, buffer, offset, length); +} + +void FamObject::get(void* buffer, const fam::size_t offset, const fam::size_t length) const { + session_->get(*object_, buffer, offset, length); +} + +auto FamObject::buffer(const fam::size_t offset) const -> Buffer { + Buffer buffer(size() - offset); + get(buffer.data(), offset, buffer.size()); + return buffer; +} + +//---------------------------------------------------------------------------------------------------------------------- +// ATOMIC + +template +auto FamObject::fetch(const fam::size_t offset) const -> T { + return session_->fetch(*object_, offset); +} + +template +void FamObject::set(const fam::size_t offset, const T value) const { + session_->set(*object_, offset, value); +} + +template +void FamObject::add(const fam::size_t offset, const T value) const { + session_->add(*object_, offset, value); +} + +template +void FamObject::subtract(const fam::size_t offset, const T value) const { + session_->subtract(*object_, offset, value); +} + +template +auto FamObject::swap(const fam::size_t offset, const T value) const -> T { // NOLINT + return session_->swap(*object_, offset, value); +} + +template +auto FamObject::compareSwap(const fam::size_t offset, const T oldValue, const T newValue) const -> T { + return session_->compareSwap(*object_, offset, oldValue, newValue); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamObject::print(std::ostream& out) const { + out << "FamObject[" << property() << ", region=" << regionId() << ", offset=" << offset() << "]"; +} + +std::ostream& operator<<(std::ostream& out, const FamObject& object) { + object.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- +// forward instantiations + +template auto FamObject::fetch(const fam::size_t) const -> int32_t; +template auto FamObject::fetch(const fam::size_t) const -> int64_t; +template auto FamObject::fetch(const fam::size_t) const -> openfam::int128_t; +template auto FamObject::fetch(const fam::size_t) const -> uint32_t; +template auto FamObject::fetch(const fam::size_t) const -> uint64_t; +template auto FamObject::fetch(const fam::size_t) const -> float; +template auto FamObject::fetch(const fam::size_t) const -> double; + +template void FamObject::set(const fam::size_t, const int32_t) const; +template void FamObject::set(const fam::size_t, const int64_t) const; +template void FamObject::set(const fam::size_t, const openfam::int128_t) const; +template void FamObject::set(const fam::size_t, const uint32_t) const; +template void FamObject::set(const fam::size_t, const uint64_t) const; +template void FamObject::set(const fam::size_t, const float) const; +template void FamObject::set(const fam::size_t, const double) const; + +template void FamObject::add(const fam::size_t, const int32_t) const; +template void FamObject::add(const fam::size_t, const int64_t) const; +template void FamObject::add(const fam::size_t, const uint32_t) const; +template void FamObject::add(const fam::size_t, const uint64_t) const; +template void FamObject::add(const fam::size_t, const float) const; +template void FamObject::add(const fam::size_t, const double) const; + +template void FamObject::subtract(const fam::size_t, const int32_t) const; +template void FamObject::subtract(const fam::size_t, const int64_t) const; +template void FamObject::subtract(const fam::size_t, const uint32_t) const; +template void FamObject::subtract(const fam::size_t, const uint64_t) const; +template void FamObject::subtract(const fam::size_t, const float) const; +template void FamObject::subtract(const fam::size_t, const double) const; + +template auto FamObject::swap(const fam::size_t, const int32_t) const -> int32_t; +template auto FamObject::swap(const fam::size_t, const int64_t) const -> int64_t; +template auto FamObject::swap(const fam::size_t, const uint32_t) const -> uint32_t; +template auto FamObject::swap(const fam::size_t, const uint64_t) const -> uint64_t; +template auto FamObject::swap(const fam::size_t, const float) const -> float; +template auto FamObject::swap(const fam::size_t, const double) const -> double; + +template auto FamObject::compareSwap(const fam::size_t, const int32_t, const int32_t) const -> int32_t; +template auto FamObject::compareSwap(const fam::size_t, const int64_t, const int64_t) const -> int64_t; +template auto FamObject::compareSwap(const fam::size_t, const uint32_t, const uint32_t) const -> uint32_t; +template auto FamObject::compareSwap(const fam::size_t, const uint64_t, const uint64_t) const -> uint64_t; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamObject.h b/src/eckit/io/fam/FamObject.h new file mode 100644 index 000000000..2c949d4af --- /dev/null +++ b/src/eckit/io/fam/FamObject.h @@ -0,0 +1,127 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamObject.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamProperty.h" + +#include +#include +#include + +namespace eckit { + +class Buffer; +class FamSessionDetail; + +//---------------------------------------------------------------------------------------------------------------------- + +class FamObject { +public: // types + using UPtr = std::unique_ptr; + using SPtr = std::shared_ptr; + +public: // methods + FamObject(FamSessionDetail& session, std::unique_ptr object); + + ~FamObject(); + + // operators + + bool operator==(const FamObject& other) const; + + bool operator!=(const FamObject& other) const { return !operator==(other); } + + auto clone() const -> UPtr; + + void replaceWith(const FamDescriptor& object); + + void deallocate() const; + + auto exists() const -> bool; + + // properties + + auto regionId() const -> fam::index_t; + + auto offset() const -> fam::index_t; + + auto descriptor() const -> FamDescriptor { return {regionId(), offset()}; } + + auto size() const -> fam::size_t; + + auto permissions() const -> fam::perm_t; + + auto name() const -> std::string; + + auto property() const -> FamProperty; + + // data access + + void put(const void* buffer, fam::size_t offset, fam::size_t length) const; + + void get(void* buffer, fam::size_t offset, fam::size_t length) const; + + template + auto get(const fam::size_t offset = 0) const -> T { + auto buffer = T {0}; + get(&buffer, offset, sizeof(T)); + return buffer; + } + + template + void put(const T& buffer, const fam::size_t offset) const { + put(&buffer, offset, sizeof(T)); + } + + auto buffer(fam::size_t offset = 0) const -> Buffer; + + // atomic operations + + template + void set(fam::size_t offset, T value) const; + + template + auto fetch(fam::size_t offset) const -> T; + + template + void add(fam::size_t offset, T value) const; + + template + void subtract(fam::size_t offset, T value) const; + + template + auto swap(fam::size_t offset, T value) const -> T; + + template + auto compareSwap(fam::size_t offset, T oldValue, T newValue) const -> T; + +private: // methods + void print(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const FamObject& object); + +private: // members + std::shared_ptr session_; + std::shared_ptr object_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamObjectName.cc b/src/eckit/io/fam/FamObjectName.cc new file mode 100644 index 000000000..9826474de --- /dev/null +++ b/src/eckit/io/fam/FamObjectName.cc @@ -0,0 +1,73 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamObjectName.h" + +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamHandle.h" +#include "eckit/io/fam/detail/FamSessionDetail.h" +#include "eckit/log/Log.h" + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamObjectName::withRegion(const std::string& regionName) -> FamObjectName& { + path().regionName = regionName; + return *this; +} + +auto FamObjectName::withObject(const std::string& objectName) -> FamObjectName& { + path().objectName = objectName; + return *this; +} + +auto FamObjectName::withUUID() -> FamObjectName& { + return withObject(path().generateUUID()); +} + +auto FamObjectName::lookup() const -> FamObject { + return session()->lookupObject(path().regionName, path().objectName); +} + +auto FamObjectName::allocate(const fam::size_t objectSize, const bool overwrite) const -> FamObject { + return session()->lookupRegion(path().regionName).allocateObject(objectSize, path().objectName, overwrite); +} + +auto FamObjectName::exists() const -> bool { + try { + return lookup().exists(); + } catch (const NotFound& notFound) { + Log::debug() << notFound << '\n'; + } catch (const PermissionDenied& permissionDenied) { Log::debug() << permissionDenied << '\n'; } + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamObjectName::dataHandle(const bool overwrite) const -> DataHandle* { + return new FamHandle(*this, overwrite); +} + +auto FamObjectName::dataHandle(const Offset& offset, const Length& length) const -> DataHandle* { + return new FamHandle(*this, offset, length, true); +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamObjectName.h b/src/eckit/io/fam/FamObjectName.h new file mode 100644 index 000000000..2367e91be --- /dev/null +++ b/src/eckit/io/fam/FamObjectName.h @@ -0,0 +1,68 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamObjectName.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/io/Length.h" +#include "eckit/io/Offset.h" +#include "eckit/io/fam/FamName.h" +#include "eckit/io/fam/FamProperty.h" + +#include +#include + +namespace eckit { + +class DataHandle; +class FamObject; + +//---------------------------------------------------------------------------------------------------------------------- + +class FamObjectName: public FamName { +public: // methods + using FamName::FamName; + + ~FamObjectName() = default; + + auto withRegion(const std::string& regionName) -> FamObjectName&; + + auto withObject(const std::string& objectName) -> FamObjectName&; + + /// @brief Replaces [objectName] with UUID (e.g., 34bd2214-2a97-5a8a-802f-76ebefd84816) + auto withUUID() -> FamObjectName&; + + auto lookup() const -> FamObject; + + /// @note we have API for permissions but we don't use it for now + auto allocate(fam::size_t objectSize, bool overwrite = false) const -> FamObject; + + auto exists() const -> bool override; + + // data handles + + [[nodiscard]] + auto dataHandle(bool overwrite = false) const -> DataHandle*; + + [[nodiscard]] + auto dataHandle(const Offset& offset, const Length& length) const -> DataHandle*; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamPath.cc b/src/eckit/io/fam/FamPath.cc new file mode 100644 index 000000000..7dd1fded3 --- /dev/null +++ b/src/eckit/io/fam/FamPath.cc @@ -0,0 +1,109 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamPath.h" + +#include "eckit/exception/Exceptions.h" +#include "eckit/filesystem/URI.h" +#include "eckit/serialisation/Stream.h" +#include "eckit/utils/Tokenizer.h" + +#include + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +namespace { + +auto parsePath(const std::string& path) -> std::tuple { + const auto names = Tokenizer("/").tokenize(path); + switch (names.size()) { + case 1: return {names[0], ""}; break; + case 2: return {names[0], names[1]}; break; + default: return {}; break; + } +} + +/* ISO Object Identifier Namespace */ +const uuid_t nsOID = {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}; + +} // namespace + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamPath::generateUUID(const std::string& name) -> std::string { + std::string result = "00000000-0000-0000-0000-000000000000"; + + uuid_t oid; + uuid_generate_sha1(&oid[0], &nsOID[0], name.c_str(), name.length()); + uuid_unparse(&oid[0], result.data()); + + return result; +} + +//---------------------------------------------------------------------------------------------------------------------- + +FamPath::FamPath(std::string region, std::string object): + regionName {std::move(region)}, objectName {std::move(object)} { } + +FamPath::FamPath(const std::string& path) { + std::tie(regionName, objectName) = parsePath(path); +} + +FamPath::FamPath(const char* path): FamPath(std::string(path)) { } + +FamPath::FamPath(const URI& uri): FamPath(uri.name()) { + ASSERT(uri.scheme() == scheme); +} + +FamPath::FamPath(Stream& stream) { + stream >> regionName; + stream >> objectName; +} + +bool FamPath::operator==(const FamPath& other) const { + return (regionName == other.regionName && objectName == other.objectName); +} + +auto FamPath::generateUUID() const -> std::string { + return generateUUID(regionName + objectName); +} + +void FamPath::encode(Stream& stream) const { + stream << regionName; + stream << objectName; +} + +auto FamPath::asString() const -> std::string { + return objectName.empty() ? '/' + regionName : '/' + regionName + '/' + objectName; +} + +auto operator<<(std::ostream& out, const FamPath& path) -> std::ostream& { + out << path.asString(); + return out; +} + +auto operator<<(Stream& stream, const FamPath& name) -> Stream& { + name.encode(stream); + return stream; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamPath.h b/src/eckit/io/fam/FamPath.h new file mode 100644 index 000000000..8131431f0 --- /dev/null +++ b/src/eckit/io/fam/FamPath.h @@ -0,0 +1,67 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamPath.h +/// @author Metin Cakircali +/// @date Jun 2024 + +#pragma once + +#include +#include + +namespace eckit { + +class URI; +class Stream; + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamPath { + static constexpr const auto scheme = "fam"; + + static auto generateUUID(const std::string& name) -> std::string; + + FamPath() = default; + + FamPath(std::string region, std::string object); + + FamPath(const std::string& path); + + FamPath(const char* path); + + FamPath(const URI& uri); + + FamPath(Stream& stream); + + bool operator==(const FamPath& other) const; + + auto generateUUID() const -> std::string; + + void encode(Stream& stream) const; + + auto asString() const -> std::string; + + friend auto operator<<(std::ostream& out, const FamPath& path) -> std::ostream&; + + friend auto operator<<(Stream& stream, const FamPath& name) -> Stream&; + + std::string regionName; + std::string objectName; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamProperty.cc b/src/eckit/io/fam/FamProperty.cc new file mode 100644 index 000000000..7aae2277b --- /dev/null +++ b/src/eckit/io/fam/FamProperty.cc @@ -0,0 +1,67 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamProperty.h" + +#include +#include + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +namespace { + +fam::perm_t stringToPerm(const std::string& perm) { + return static_cast(std::stoul(perm, nullptr, 8)); +} + +std::string permToString(fam::perm_t perm) { + char buf[5]; + snprintf(buf, sizeof(buf), "%04o", perm); + return std::string(buf); +} + +} // namespace + +//---------------------------------------------------------------------------------------------------------------------- + +FamProperty::FamProperty(fam::size_t size, fam::perm_t perm, std::string name, std::uint32_t uid, std::uint32_t gid) + : size {size}, perm {perm}, name {std::move(name)}, uid {uid}, gid {gid} { } + +FamProperty::FamProperty(fam::size_t size, fam::perm_t perm, const std::string& name) + : FamProperty(size, perm, name, getuid(), getgid()) { } + +FamProperty::FamProperty(fam::size_t size, fam::perm_t perm) : FamProperty(size, perm, "") { } + +FamProperty::FamProperty(fam::size_t size, const std::string& perm) : FamProperty(size, stringToPerm(perm)) { } + +void FamProperty::print(std::ostream& out) const { + out << "Property[size=" << size << ", perm=" << perm << "(" << permToString(perm) << ")" << ",name=" << name + << ",uid=" << uid << ",gid=" << gid << "]"; +} + +//---------------------------------------------------------------------------------------------------------------------- + +std::ostream& operator<<(std::ostream& out, const FamProperty& prop) { + prop.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamProperty.h b/src/eckit/io/fam/FamProperty.h new file mode 100644 index 000000000..33f704405 --- /dev/null +++ b/src/eckit/io/fam/FamProperty.h @@ -0,0 +1,87 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamProperty.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include // mode_t + +#include // uint64_t +#include +#include + +namespace openfam { +class Fam_Descriptor; +class Fam_Region_Descriptor; +} // namespace openfam + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- +// TYPES + +using FamObjectDescriptor = openfam::Fam_Descriptor; +using FamRegionDescriptor = openfam::Fam_Region_Descriptor; + +//---------------------------------------------------------------------------------------------------------------------- + +namespace fam { + +using size_t = std::uint64_t; +using perm_t = mode_t; +using index_t = std::uint64_t; + +} // namespace fam + +/// @note mirrors Fam_Global_Descriptor +struct FamDescriptor { + fam::index_t region {0}; + fam::index_t offset {0}; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamProperty { + FamProperty() = default; + + FamProperty(fam::size_t size, fam::perm_t perm, std::string name, std::uint32_t uid, std::uint32_t gid); + + FamProperty(fam::size_t size, fam::perm_t perm, const std::string& name); + + FamProperty(fam::size_t size, fam::perm_t perm); + + FamProperty(fam::size_t size, const std::string& perm); + + void print(std::ostream& out) const; + + auto operator==(const FamProperty& other) const -> bool { + return (size == other.size && perm == other.perm && name == other.name && uid == other.uid && gid == other.gid); + } + + friend std::ostream& operator<<(std::ostream& out, const FamProperty& prop); + + fam::size_t size {0}; + fam::perm_t perm {0640}; + std::string name {""}; + std::uint32_t uid {0}; + std::uint32_t gid {0}; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamRegion.cc b/src/eckit/io/fam/FamRegion.cc new file mode 100644 index 000000000..2a2a984f6 --- /dev/null +++ b/src/eckit/io/fam/FamRegion.cc @@ -0,0 +1,125 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamRegion.h" + +#include "detail/FamSessionDetail.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamObject.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamRegion::FamRegion(FamSessionDetail& session, std::unique_ptr region): + session_ {session.getShared()}, region_(std::move(region)) { + ASSERT(region_); +} + +FamRegion::~FamRegion() = default; + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamRegion::clone() const -> UPtr { + auto clone = std::make_unique(region_->get_global_descriptor()); + clone->set_size(region_->get_size()); + clone->set_perm(region_->get_perm()); + clone->set_name(region_->get_name()); + clone->set_desc_status(region_->get_desc_status()); + clone->set_redundancyLevel(region_->get_redundancyLevel()); + clone->set_memoryType(region_->get_memoryType()); + clone->set_interleaveEnable(region_->get_interleaveEnable()); + return std::make_unique(*session_, std::move(clone)); +} + +void FamRegion::destroy() const { + session_->destroyRegion(*region_); +} + +auto FamRegion::exists() const -> bool { + return (region_->get_desc_status() != FamDescriptorStatus::DESC_INVALID); +} + +//---------------------------------------------------------------------------------------------------------------------- +// PROPERTIES + +auto FamRegion::index() const -> fam::index_t { + return region_->get_global_descriptor().regionId; +} + +auto FamRegion::size() const -> fam::size_t { + return region_->get_size(); +} + +auto FamRegion::permissions() const -> fam::perm_t { + return region_->get_perm(); +} + +auto FamRegion::name() const -> std::string { + return region_->get_name() ? region_->get_name() : ""; +} + +auto FamRegion::property() const -> FamProperty { + return {size(), permissions(), name()}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// OBJECT factory methods + +auto FamRegion::proxyObject(const fam::index_t offset) const -> FamObject { + return session_->proxyObject(index(), offset); +} + +auto FamRegion::lookupObject(const std::string& objectName) const -> FamObject { + return session_->lookupObject(name(), objectName); +} + +auto FamRegion::allocateObject(const fam::size_t objectSize, + const fam::perm_t objectPerm, + const std::string& objectName, + const bool overwrite) const -> FamObject { + if (overwrite) { return session_->ensureAllocateObject(*region_, objectSize, objectPerm, objectName); } + return session_->allocateObject(*region_, objectSize, objectPerm, objectName); +} + +void FamRegion::deallocateObject(const std::string& objectName) const { + session_->deallocateObject(region_->get_name(), objectName); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamRegion::print(std::ostream& out) const { + out << "FamRegion[" << property() << ",status="; + switch (region_->get_desc_status()) { + case FamDescriptorStatus::DESC_INVALID: out << "invalid"; break; + case FamDescriptorStatus::DESC_INIT_DONE: out << "initialized"; break; + case FamDescriptorStatus::DESC_INIT_DONE_BUT_KEY_NOT_VALID: out << "initialized_invalidkey"; break; + case FamDescriptorStatus::DESC_UNINITIALIZED: out << "uninitialized"; break; + default: out << "unknown"; break; + } + out << "]"; +} + +std::ostream& operator<<(std::ostream& out, const FamRegion& region) { + region.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamRegion.h b/src/eckit/io/fam/FamRegion.h new file mode 100644 index 000000000..4f9940357 --- /dev/null +++ b/src/eckit/io/fam/FamRegion.h @@ -0,0 +1,96 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamRegion.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamProperty.h" + +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +class FamRegion { +public: // types + using UPtr = std::unique_ptr; + using SPtr = std::shared_ptr; + +public: // methods + FamRegion(FamSessionDetail& session, std::unique_ptr region); + + ~FamRegion(); + + auto clone() const -> UPtr; + + void destroy() const; + + auto exists() const -> bool; + + // properties + + auto index() const -> fam::index_t; + + auto size() const -> fam::size_t; + + auto permissions() const -> fam::perm_t; + + auto name() const -> std::string; + + auto property() const -> FamProperty; + + // object methods + + [[nodiscard]] + auto proxyObject(fam::index_t offset) const -> FamObject; + + [[nodiscard]] + auto lookupObject(const std::string& objectName) const -> FamObject; + + [[nodiscard]] + auto allocateObject(fam::size_t objectSize, + fam::perm_t objectPerm, + const std::string& objectName = "", + bool overwrite = false) const -> FamObject; + + /// IMPOTANT: this uses the region's permissions for the object + [[nodiscard]] + auto allocateObject(fam::size_t objectSize, + const std::string& objectName = "", + bool overwrite = false) const -> FamObject { + return allocateObject(objectSize, permissions(), objectName, overwrite); + } + + void deallocateObject(const std::string& objectName) const; + +private: // methods + void print(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const FamRegion& region); + +private: // members + std::shared_ptr session_; + std::shared_ptr region_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamRegionName.cc b/src/eckit/io/fam/FamRegionName.cc new file mode 100644 index 000000000..5d6b6da3b --- /dev/null +++ b/src/eckit/io/fam/FamRegionName.cc @@ -0,0 +1,67 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamRegionName.h" + +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/filesystem/URI.h" +#include "eckit/io/fam/FamRegion.h" +#include "eckit/io/fam/detail/FamSessionDetail.h" +#include "eckit/log/Log.h" + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamRegionName::withRegion(const std::string& regionName) -> FamRegionName& { + path().regionName = regionName; + return *this; +} + +auto FamRegionName::object(const std::string& objectName) const -> FamObjectName { + return {endpoint(), {path().regionName, objectName}}; +} + +auto FamRegionName::lookup() const -> FamRegion { + return session()->lookupRegion(path().regionName); +} + +auto FamRegionName::create(const fam::size_t regionSize, + const fam::perm_t regionPerm, + const bool overwrite) const -> FamRegion { + if (overwrite) { return session()->ensureCreateRegion(regionSize, regionPerm, path().regionName); } + return session()->createRegion(regionSize, regionPerm, path().regionName); +} + +auto FamRegionName::exists() const -> bool { + try { + return lookup().exists(); + } catch (const NotFound& notFound) { + Log::debug() << notFound << '\n'; + } catch (const PermissionDenied& permissionDenied) { Log::debug() << permissionDenied << '\n'; } + return false; +} + +auto FamRegionName::uriBelongs(const URI& uri) const -> bool { + /// @todo check if usage requires nothrow + return (uri.endpoint() == endpoint() && FamPath(uri).regionName == path().regionName); +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamRegionName.h b/src/eckit/io/fam/FamRegionName.h new file mode 100644 index 000000000..a39c447dd --- /dev/null +++ b/src/eckit/io/fam/FamRegionName.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamRegionName.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/io/fam/FamName.h" +#include "eckit/io/fam/FamObjectName.h" +#include "eckit/io/fam/FamProperty.h" + +#include + +namespace eckit { + +class FamRegion; + +//---------------------------------------------------------------------------------------------------------------------- + +class FamRegionName : public FamName { +public: // methods + using FamName::FamName; + + ~FamRegionName() = default; + + auto withRegion(const std::string& regionName) -> FamRegionName&; + + auto object(const std::string& objectName) const -> FamObjectName; + + auto lookup() const -> FamRegion; + + auto create(fam::size_t regionSize, fam::perm_t regionPerm, bool overwrite = false) const -> FamRegion; + + auto exists() const -> bool override; + + auto uriBelongs(const URI& uri) const -> bool; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamSession.cc b/src/eckit/io/fam/FamSession.cc new file mode 100644 index 000000000..e848811d3 --- /dev/null +++ b/src/eckit/io/fam/FamSession.cc @@ -0,0 +1,73 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamSession.h" + +#include "detail/FamSessionDetail.h" +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/log/Log.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamSession::instance() -> FamSession& { + static FamSession instance; + return instance; +} + +FamSession::FamSession() = default; + +FamSession::~FamSession() = default; + +//---------------------------------------------------------------------------------------------------------------------- + +auto FamSession::get(const FamConfig& config) -> SPtr { + + if (config.sessionName.empty()) { throw SeriousBug("FamSession::get() empty session name", Here()); } + + for (auto& session : registry_) { + if (session->config() == config) { return session; } + } + + return {}; +} + +auto FamSession::getOrAdd(const FamConfig& config) -> SPtr { + + if (auto session = get(config)) { return session; } + + // add new session + auto session = std::make_shared(config); + registry_.emplace_back(session); + return session; +} + +void FamSession::remove(const FamConfig& config) { + registry_.remove_if([&config](const auto& session) { return session->config() == config; }); +} + +void FamSession::remove(const std::string& sessionName) { + registry_.remove_if([&sessionName](const auto& session) { return session->name() == sessionName; }); +} + +void FamSession::clear() { + registry_.clear(); +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamSession.h b/src/eckit/io/fam/FamSession.h new file mode 100644 index 000000000..9aaf31bd6 --- /dev/null +++ b/src/eckit/io/fam/FamSession.h @@ -0,0 +1,68 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamSession.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamConfig.h" + +#include +#include +#include + +namespace eckit { + +class FamSessionDetail; + +//---------------------------------------------------------------------------------------------------------------------- + +/// @brief Manages a list of FamSessionDetail. +class FamSession { +public: // types + using SPtr = std::shared_ptr; + +public: // methods + FamSession(const FamSession&) = delete; + FamSession& operator=(const FamSession&) = delete; + FamSession(FamSession&&) = delete; + FamSession& operator=(FamSession&&) = delete; + + static auto instance() -> FamSession&; + + auto get(const FamConfig& config) -> FamSession::SPtr; + + auto getOrAdd(const FamConfig& config) -> FamSession::SPtr; + + void remove(const FamConfig& config); + + void remove(const std::string& sessionName); + + void clear(); + +private: // methods + FamSession(); + + ~FamSession(); + +private: // members + std::list registry_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamURIManager.cc b/src/eckit/io/fam/FamURIManager.cc new file mode 100644 index 000000000..4ed927320 --- /dev/null +++ b/src/eckit/io/fam/FamURIManager.cc @@ -0,0 +1,60 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "eckit/io/fam/FamURIManager.h" + +#include "eckit/filesystem/URI.h" +#include "eckit/io/fam/FamObjectName.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +FamURIManager::FamURIManager(const std::string& name): URIManager(name) { } + +FamURIManager::~FamURIManager() = default; + +bool FamURIManager::exists(const URI& uri) { + return FamObjectName(uri).exists(); +} + +DataHandle* FamURIManager::newWriteHandle(const URI& uri) { + return FamObjectName(uri).dataHandle(); +} + +DataHandle* FamURIManager::newReadHandle(const URI& uri) { + return FamObjectName(uri).dataHandle(); +} + +DataHandle* FamURIManager::newReadHandle(const URI& uri, const OffsetList& /*offsets*/, const LengthList& /*lengths*/) { + return FamObjectName(uri).dataHandle(); +} + +std::string FamURIManager::asString(const URI& uri) const { + std::string query = uri.query(); + if (!query.empty()) { query = "?" + query; } + + std::string fragment = uri.fragment(); + if (!fragment.empty()) { fragment = "#" + fragment; } + + /// @todo consider return FamObjectName(uri).asString() + query + fragment; + return uri.scheme() + ":" + uri.name() + query + fragment; +} + +static FamURIManager manager(FamPath::scheme); + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/FamURIManager.h b/src/eckit/io/fam/FamURIManager.h new file mode 100644 index 000000000..6fc688fc8 --- /dev/null +++ b/src/eckit/io/fam/FamURIManager.h @@ -0,0 +1,48 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamURIManager.h +/// @author Metin Cakircali +/// @date May 2024 + +#pragma once + +#include "eckit/filesystem/URIManager.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +class FamURIManager: public URIManager { +public: // methods + FamURIManager(const std::string& name); + + ~FamURIManager() override; + + bool authority() override { return true; } + +private: // methods + bool exists(const URI&) override; + + DataHandle* newWriteHandle(const URI& uri) override; + DataHandle* newReadHandle(const URI& uri) override; + DataHandle* newReadHandle(const URI& uri, const OffsetList& offsets, const LengthList& lengths) override; + + std::string asString(const URI& uri) const override; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/detail/FamListNode.h b/src/eckit/io/fam/detail/FamListNode.h new file mode 100644 index 000000000..84adb0a9b --- /dev/null +++ b/src/eckit/io/fam/detail/FamListNode.h @@ -0,0 +1,58 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamListNode.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "FamNode.h" +#include "eckit/io/Buffer.h" + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamListNode: public FamNode { + FamDescriptor prev; + fam::size_t length {0}; + + //------------------------------------------------------------------------------------------------------------------ + // HELPERS (DO NOT add any virtual function here) + + static auto getPrev(const FamObject& object) -> FamDescriptor { + return object.get(offsetof(FamListNode, prev)); + } + + static auto getPrevOffset(const FamObject& object) -> std::uint64_t { + return object.get(offsetof(FamListNode, prev.offset)); + } + + static auto getLength(const FamObject& object) -> fam::size_t { + return object.get(offsetof(FamListNode, length)); + } + + static void getData(const FamObject& object, Buffer& buffer) { + if (const auto length = getLength(object); length > 0) { + buffer.resize(length); + object.get(buffer.data(), sizeof(FamListNode), length); + } + } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/detail/FamMapNode.h b/src/eckit/io/fam/detail/FamMapNode.h new file mode 100644 index 000000000..b1fdb740f --- /dev/null +++ b/src/eckit/io/fam/detail/FamMapNode.h @@ -0,0 +1,48 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamMapNode.h +/// @author Metin Cakircali +/// @date Jul 2024 + +#pragma once + +#include "FamNode.h" +#include "eckit/io/fam/FamList.h" + +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamMapNode: public FamNode { + FamList::Descriptor desc; + + //------------------------------------------------------------------------------------------------------------------ + // HELPERS (DO NOT add any virtual function here) + + static auto getDescriptor(const FamObject& object) -> FamList::Descriptor { + return object.get(offsetof(FamMapNode, desc)); + } + + static auto getList(const FamRegion& region, const FamObject& object) -> std::unique_ptr { + return std::make_unique(region, FamMapNode::getDescriptor(object)); + } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/detail/FamNode.h b/src/eckit/io/fam/detail/FamNode.h new file mode 100644 index 000000000..dc83f033c --- /dev/null +++ b/src/eckit/io/fam/detail/FamNode.h @@ -0,0 +1,48 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamNode.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamObject.h" + +#include // uint8_t + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- + +struct FamNode { + std::uint8_t version {1}; // 1 byte + FamDescriptor next; + + //------------------------------------------------------------------------------------------------------------------ + // HELPERS (DO NOT add any virtual function here) + + static auto getNext(const FamObject& object) -> FamDescriptor { + return object.get(offsetof(FamNode, next)); + } + + static auto getNextOffset(const FamObject& object) -> std::uint64_t { + return object.get(offsetof(FamNode, next.offset)); + } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/detail/FamSessionDetail.cc b/src/eckit/io/fam/detail/FamSessionDetail.cc new file mode 100644 index 000000000..20ddc28e3 --- /dev/null +++ b/src/eckit/io/fam/detail/FamSessionDetail.cc @@ -0,0 +1,389 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +#include "FamSessionDetail.h" + +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/io/fam/FamConfig.h" +#include "eckit/log/Log.h" + +#include + +#include +#include // isspace isprint +#include // memset strndup +#include +#include +#include + +namespace eckit { + +//---------------------------------------------------------------------------------------------------------------------- +// HELPERS + +namespace { + +template +auto invokeFam(openfam::fam& fam, Func&& fnPtr, Args&&... args) { + try { + return (fam.*std::forward(fnPtr))(std::forward(args)...); + } catch (openfam::Fam_Exception& e) { + const auto code = e.fam_error(); + if (code == openfam::Fam_Error::FAM_ERR_NOTFOUND) { throw NotFound(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_ALREADYEXIST) { throw AlreadyExists(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_NOPERM) { throw PermissionDenied(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_INVALID) { throw BadValue(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_NO_SPACE) { throw OutOfStorage(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_OUTOFRANGE) { throw OutOfRange(e.fam_error_msg(), Here()); } + if (code == openfam::Fam_Error::FAM_ERR_METADATA) { throw NotFound(e.fam_error_msg()); } + if (code == openfam::Fam_Error::FAM_ERR_RPC) { + std::string optionName = "CIS_SERVER"; + const std::string serverName = static_cast(fam.fam_get_option(optionName.data())); + throw RemoteException(e.fam_error_msg(), serverName); + } + throw SeriousBug("Code=" + std::to_string(code) + ' ' + e.fam_error_msg()); + } +} + +auto isValidName(std::string_view str) -> bool { + if (str.empty()) { return false; } + return std::all_of(str.begin(), str.end(), [](char c) { return std::isprint(c) > 0 && std::isspace(c) == 0; }); +} + +} // namespace + +//---------------------------------------------------------------------------------------------------------------------- +// SESSION + +FamSessionDetail::FamSessionDetail(const FamConfig& config): name_ {config.sessionName} { + ASSERT(isValidName(name_)); + + Log::debug() << "Initializing FAM session: " << config << '\n'; + + try { + // pins + auto runtime = std::string {"NONE"}; + auto host = config.endpoint.host(); + auto port = std::to_string(config.endpoint.port()); + + Fam_Options options; + ::memset(static_cast(&options), 0, sizeof(Fam_Options)); + options.runtime = runtime.data(); + options.cisServer = host.data(); + options.grpcPort = port.data(); + + fam_.fam_initialize(name_.c_str(), &options); + } catch (openfam::Fam_Exception& e) { + fam_.fam_abort(-1); + throw Exception(e.fam_error_msg(), Here()); + } +} + +FamSessionDetail::~FamSessionDetail() { + // Log::debug() << "Finalizing FAM session: " << name_ << '\n'; + try { + fam_.fam_finalize(name_.c_str()); + } catch (openfam::Fam_Exception& e) { + Log::error() << "Failed to finalize session: " << name_ << ", msg=" << e.fam_error_msg() << '\n'; + fam_.fam_abort(-1); + } +} + +//---------------------------------------------------------------------------------------------------------------------- + +void FamSessionDetail::print(std::ostream& out) const { + out << "FamSessionDetail[name=" << name_ << "]"; +} + +std::ostream& operator<<(std::ostream& out, const FamSessionDetail& session) { + session.print(out); + return out; +} + +//---------------------------------------------------------------------------------------------------------------------- +// REGION + +auto FamSessionDetail::config() -> FamConfig { + const std::string host = static_cast(fam_.fam_get_option("CIS_SERVER")); + const std::string port = static_cast(fam_.fam_get_option("GRPC_PORT")); + + const net::Endpoint endpoint {host, std::stoi(port)}; + + return {endpoint, name_}; +} + +auto FamSessionDetail::lookupRegion(const std::string& regionName) -> FamRegion { + ASSERT(isValidName(regionName)); + + auto* region = invokeFam(fam_, &openfam::fam::fam_lookup_region, regionName.c_str()); + + return {*this, std::unique_ptr(region)}; +} + +auto FamSessionDetail::createRegion(const fam::size_t regionSize, + const fam::perm_t regionPerm, + const std::string& regionName) -> FamRegion { + ASSERT(regionSize > 0); + ASSERT(isValidName(regionName)); + + auto* region = invokeFam(fam_, &openfam::fam::fam_create_region, regionName.c_str(), regionSize, regionPerm, nullptr); + + return {*this, std::unique_ptr(region)}; +} + +void FamSessionDetail::resizeRegion(FamRegionDescriptor& region, const fam::size_t size) { + ASSERT(size > 0); + + invokeFam(fam_, &openfam::fam::fam_resize_region, ®ion, size); +} + +void FamSessionDetail::destroyRegion(FamRegionDescriptor& region) { + invokeFam(fam_, &openfam::fam::fam_destroy_region, ®ion); +} + +void FamSessionDetail::destroyRegion(const std::string& regionName) { + lookupRegion(regionName).destroy(); +} + +auto FamSessionDetail::ensureCreateRegion(const fam::size_t regionSize, + const fam::perm_t regionPerm, + const std::string& regionName) -> FamRegion { + try { + return createRegion(regionSize, regionPerm, regionName); + } catch (const AlreadyExists& e) { + destroyRegion(regionName); + return createRegion(regionSize, regionPerm, regionName); + } +} + +auto FamSessionDetail::stat(FamRegionDescriptor& region) -> FamProperty { + Fam_Stat info; + + auto fnPtr = static_cast(&openfam::fam::fam_stat); + invokeFam(fam_, fnPtr, ®ion, &info); + + return {info.size, info.perm, info.name, info.uid, info.gid}; +} + +//---------------------------------------------------------------------------------------------------------------------- +// OBJECT + +auto FamSessionDetail::proxyObject(const std::uint64_t region, const std::uint64_t offset) -> FamObject { + return {*this, std::make_unique(Fam_Global_Descriptor {region, offset})}; +} + +auto FamSessionDetail::lookupObject(const std::string& regionName, const std::string& objectName) -> FamObject { + ASSERT(isValidName(regionName)); + ASSERT(isValidName(objectName)); + + auto* object = invokeFam(fam_, &openfam::fam::fam_lookup, objectName.c_str(), regionName.c_str()); + + return {*this, std::unique_ptr(object)}; +} + +auto FamSessionDetail::allocateObject(FamRegionDescriptor& region, + const fam::size_t objectSize, + const fam::perm_t objectPerm, + const std::string& objectName) -> FamObject { + ASSERT(objectSize > 0); + + auto allocate = + static_cast( + &openfam::fam::fam_allocate); + + auto* object = invokeFam(fam_, allocate, objectName.c_str(), objectSize, objectPerm, ®ion); + + return {*this, std::unique_ptr(object)}; +} + +void FamSessionDetail::deallocateObject(FamObjectDescriptor& object) { + invokeFam(fam_, &openfam::fam::fam_deallocate, &object); +} + +void FamSessionDetail::deallocateObject(const std::string& regionName, const std::string& objectName) { + lookupObject(regionName, objectName).deallocate(); +} + +auto FamSessionDetail::ensureAllocateObject(FamRegionDescriptor& region, + const fam::size_t objectSize, + const fam::perm_t objectPerm, + const std::string& objectName) -> FamObject { + try { + return allocateObject(region, objectSize, objectPerm, objectName); + } catch (const AlreadyExists& e) { + deallocateObject(region.get_name(), objectName); + return allocateObject(region, objectSize, objectPerm, objectName); + } +} + +auto FamSessionDetail::stat(FamObjectDescriptor& object) -> FamProperty { + Fam_Stat info; + + auto fnPtr = static_cast(&openfam::fam::fam_stat); + invokeFam(fam_, fnPtr, &object, &info); + + return {info.size, info.perm, info.name, info.uid, info.gid}; +} + +void FamSessionDetail::put(FamObjectDescriptor& object, + const void* buffer, + const fam::size_t offset, + const fam::size_t length) { + ASSERT(buffer); + ASSERT(length > 0); + + /// @note we have to remove "const" qualifier from buffer + invokeFam(fam_, &openfam::fam::fam_put_blocking, const_cast(buffer), &object, offset, length); +} + +void FamSessionDetail::get(FamObjectDescriptor& object, void* buffer, const fam::size_t offset, const fam::size_t length) { + ASSERT(buffer); + ASSERT(length > 0); + + invokeFam(fam_, &openfam::fam::fam_get_blocking, buffer, &object, offset, length); +} + +//---------------------------------------------------------------------------------------------------------------------- +// OBJECT - ATOMIC + +template +auto FamSessionDetail::fetch(FamObjectDescriptor& /* object */, const fam::size_t /* offset */) -> T { + throw SeriousBug("This type is not specialized!"); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> int32_t { + return invokeFam(fam_, &openfam::fam::fam_fetch_int32, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> int64_t { + return invokeFam(fam_, &openfam::fam::fam_fetch_int64, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> openfam::int128_t { + return invokeFam(fam_, &openfam::fam::fam_fetch_int128, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> uint32_t { + return invokeFam(fam_, &openfam::fam::fam_fetch_uint32, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> uint64_t { + return invokeFam(fam_, &openfam::fam::fam_fetch_uint64, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> float { + return invokeFam(fam_, &openfam::fam::fam_fetch_float, &object, offset); +} + +template<> +auto FamSessionDetail::fetch(FamObjectDescriptor& object, const fam::size_t offset) -> double { + return invokeFam(fam_, &openfam::fam::fam_fetch_double, &object, offset); +} + +template +void FamSessionDetail::set(FamObjectDescriptor& object, const fam::size_t offset, const T value) { + auto fptr = static_cast(&openfam::fam::fam_set); + invokeFam(fam_, fptr, &object, offset, value); +} + +template +void FamSessionDetail::add(FamObjectDescriptor& object, const fam::size_t offset, const T value) { + auto fptr = static_cast(&openfam::fam::fam_add); + invokeFam(fam_, fptr, &object, offset, value); +} + +template +void FamSessionDetail::subtract(FamObjectDescriptor& object, const fam::size_t offset, const T value) { + auto fptr = static_cast(&openfam::fam::fam_subtract); + invokeFam(fam_, fptr, &object, offset, value); +} + +template +auto FamSessionDetail::swap(FamObjectDescriptor& object, const fam::size_t offset, const T value) -> T { // NOLINT + auto fptr = static_cast(&openfam::fam::fam_swap); + return invokeFam(fam_, fptr, &object, offset, value); +} + +template +auto FamSessionDetail::compareSwap(FamObjectDescriptor& object, + const fam::size_t offset, + const T oldValue, + const T newValue) -> T { + auto fptr = + static_cast(&openfam::fam::fam_compare_swap); + return invokeFam(fam_, fptr, &object, offset, oldValue, newValue); +} + +//---------------------------------------------------------------------------------------------------------------------- +// forward instantiations + +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> int32_t; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> int64_t; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> openfam::int128_t; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> uint32_t; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> uint64_t; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> float; +template auto FamSessionDetail::fetch(FamObjectDescriptor&, const uint64_t) -> double; + +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const int32_t); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const int64_t); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const openfam::int128_t); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const uint32_t); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const uint64_t); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const float); +template void FamSessionDetail::set(FamObjectDescriptor&, const fam::size_t, const double); + +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const int32_t); +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const int64_t); +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const uint32_t); +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const uint64_t); +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const float); +template void FamSessionDetail::add(FamObjectDescriptor&, const fam::size_t, const double); + +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const int32_t); +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const int64_t); +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const uint32_t); +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const uint64_t); +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const float); +template void FamSessionDetail::subtract(FamObjectDescriptor&, const fam::size_t, const double); + +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const int32_t) -> int32_t; +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const int64_t) -> int64_t; +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const uint32_t) -> uint32_t; +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const uint64_t) -> uint64_t; +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const float) -> float; +template auto FamSessionDetail::swap(FamObjectDescriptor&, const fam::size_t, const double) -> double; + +template auto FamSessionDetail::compareSwap(FamObjectDescriptor&, const fam::size_t, const int32_t, const int32_t) -> int32_t; +template auto FamSessionDetail::compareSwap(FamObjectDescriptor&, const fam::size_t, const int64_t, const int64_t) -> int64_t; +template auto FamSessionDetail::compareSwap(FamObjectDescriptor&, + const fam::size_t, + const uint32_t, + const uint32_t) -> uint32_t; +template auto FamSessionDetail::compareSwap(FamObjectDescriptor&, + const fam::size_t, + const uint64_t, + const uint64_t) -> uint64_t; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/io/fam/detail/FamSessionDetail.h b/src/eckit/io/fam/detail/FamSessionDetail.h new file mode 100644 index 000000000..e42dbc803 --- /dev/null +++ b/src/eckit/io/fam/detail/FamSessionDetail.h @@ -0,0 +1,155 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file FamSessionDetail.h +/// @author Metin Cakircali +/// @date Mar 2024 + +#pragma once + +#include "eckit/io/fam/FamConfig.h" +#include "eckit/io/fam/FamObject.h" +#include "eckit/io/fam/FamProperty.h" +#include "eckit/io/fam/FamRegion.h" + +#include + +#include +#include +#include + +namespace eckit { + +using FamDescriptorStatus = openfam::Fam_Descriptor_Status; + +//---------------------------------------------------------------------------------------------------------------------- +// SESSION + +class FamSessionDetail : public std::enable_shared_from_this { +public: // methods + FamSessionDetail(const FamConfig& config); + + FamSessionDetail(const FamSessionDetail&) = delete; + FamSessionDetail& operator=(const FamSessionDetail&) = delete; + FamSessionDetail(FamSessionDetail&&) = delete; + FamSessionDetail& operator=(FamSessionDetail&&) = delete; + + ~FamSessionDetail(); + + auto getShared() -> std::shared_ptr { return shared_from_this(); } + + auto name() const -> std::string { return name_; } + + auto config() -> FamConfig; + + //------------------------------------------------------------------------------------------------------------------ + // REGION + + [[nodiscard]] + auto lookupRegion(const std::string& regionName) -> FamRegion; + + [[nodiscard]] + auto createRegion(fam::size_t regionSize, fam::perm_t regionPerm, const std::string& regionName) -> FamRegion; + + [[nodiscard]] + auto createRegion(const FamProperty& property) -> FamRegion { + return createRegion(property.size, property.perm, property.name); + } + + void resizeRegion(FamRegionDescriptor& region, fam::size_t size); + + void destroyRegion(FamRegionDescriptor& region); + + void destroyRegion(const std::string& regionName); + + [[nodiscard]] + auto ensureCreateRegion(fam::size_t regionSize, fam::perm_t regionPerm, const std::string& regionName) -> FamRegion; + + auto stat(FamRegionDescriptor& region) -> FamProperty; + + //------------------------------------------------------------------------------------------------------------------ + // OBJECT + + [[nodiscard]] + auto proxyObject(std::uint64_t region, std::uint64_t offset) -> FamObject; + + [[nodiscard]] + auto lookupObject(const std::string& regionName, const std::string& objectName) -> FamObject; + + [[nodiscard]] + auto allocateObject(FamRegionDescriptor& region, + fam::size_t objectSize, + fam::perm_t objectPerm, + const std::string& objectName = "") -> FamObject; + + [[nodiscard]] + auto allocateObject(FamRegionDescriptor& region, const FamProperty& property) -> FamObject { + return allocateObject(region, property.size, property.perm, property.name); + } + + void deallocateObject(FamObjectDescriptor& object); + + void deallocateObject(const std::string& regionName, const std::string& objectName); + + /// IMPORTANT: This method will deallocate any existing object with the same name + [[nodiscard]] + auto ensureAllocateObject(FamRegionDescriptor& region, + fam::size_t objectSize, + fam::perm_t objectPerm, + const std::string& objectName) -> FamObject; + + auto stat(FamObjectDescriptor& object) -> FamProperty; + + void put(FamObjectDescriptor& object, const void* buffer, fam::size_t offset, fam::size_t length); + + void get(FamObjectDescriptor& object, void* buffer, fam::size_t offset, fam::size_t length); + + //------------------------------------------------------------------------------------------------------------------ + // OBJECT - ATOMIC + + template + auto fetch(FamObjectDescriptor& object, fam::size_t offset) -> T; + + template + void set(FamObjectDescriptor& object, fam::size_t offset, T value); + + template + void add(FamObjectDescriptor& object, fam::size_t offset, T value); + + template + void subtract(FamObjectDescriptor& object, fam::size_t offset, T value); + + template + auto swap(FamObjectDescriptor& object, fam::size_t offset, T value) -> T; + + template + auto compareSwap(FamObjectDescriptor& object, fam::size_t offset, T oldValue, T newValue) -> T; + + //------------------------------------------------------------------------------------------------------------------ + +private: // methods + friend std::ostream& operator<<(std::ostream& out, const FamSessionDetail& session); + + void print(std::ostream& out) const; + +private: // members + const std::string name_; + + openfam::fam fam_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit diff --git a/src/eckit/net/Endpoint.cc b/src/eckit/net/Endpoint.cc index 8be1ff83d..ad58f976e 100644 --- a/src/eckit/net/Endpoint.cc +++ b/src/eckit/net/Endpoint.cc @@ -10,17 +10,22 @@ #include "eckit/net/Endpoint.h" -#include - #include "eckit/exception/Exceptions.h" +#include "eckit/filesystem/URI.h" #include "eckit/serialisation/Stream.h" #include "eckit/utils/Tokenizer.h" #include "eckit/utils/Translator.h" +#include + namespace eckit::net { //---------------------------------------------------------------------------------------------------------------------- +Endpoint::Endpoint(const URI& uri): host_(uri.host()), port_(uri.port()) { + validate(); +} + Endpoint::Endpoint(const std::string& s) { Tokenizer tokenize(":"); std::vector tokens; diff --git a/src/eckit/net/Endpoint.h b/src/eckit/net/Endpoint.h index fa53731c7..4cac0ce91 100644 --- a/src/eckit/net/Endpoint.h +++ b/src/eckit/net/Endpoint.h @@ -21,6 +21,7 @@ namespace eckit { class Stream; +class URI; namespace net { @@ -28,7 +29,8 @@ namespace net { class Endpoint { -public: // methods +public: // methods + Endpoint(const URI& uri); // gets hostname:port from uri Endpoint(const std::string&); // parses the std::string formated as hostname:port Endpoint(const std::string& host, int port); Endpoint(Stream& s); diff --git a/tests/io/CMakeLists.txt b/tests/io/CMakeLists.txt index b2a35896e..1c93bcd0f 100644 --- a/tests/io/CMakeLists.txt +++ b/tests/io/CMakeLists.txt @@ -48,6 +48,18 @@ ecbuild_add_test( TARGET eckit_test_multihandle SOURCES test_multihandle.cc LIBS eckit ) +ecbuild_add_test( TARGET eckit_test_fam + SOURCES test_fam.cc + CONDITION HAVE_OPENFAM + LABELS openfam + LIBS eckit ) + +ecbuild_add_test( TARGET eckit_test_fam_list + SOURCES test_famlist.cc + CONDITION HAVE_OPENFAM + LABELS openfam + LIBS eckit ) + ecbuild_add_test( TARGET eckit_test_partfilehandle SOURCES test_partfilehandle.cc LIBS eckit ) diff --git a/tests/io/fam_common.h b/tests/io/fam_common.h new file mode 100644 index 000000000..d7af6f68d --- /dev/null +++ b/tests/io/fam_common.h @@ -0,0 +1,80 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file fam_common.h +/// @author Metin Cakircali +/// @date Jun 2024 + +#include "eckit/config/LibEcKit.h" +#include "eckit/exception/Exceptions.h" +#include "eckit/filesystem/URI.h" +#include "eckit/io/fam/FamObjectName.h" +#include "eckit/io/fam/FamRegion.h" +#include "eckit/io/fam/FamRegionName.h" + +#include + +#include + +namespace eckit::test { + +using namespace std::string_literals; + +//---------------------------------------------------------------------------------------------------------------------- +// HELPERS + +namespace fam { + +// This returns a random number as string. +inline auto randomNumber() -> std::string { + struct timeval tv; + ::gettimeofday(&tv, nullptr); + // ::getpid() ? + ::srandom(static_cast(tv.tv_sec + tv.tv_usec)); + return std::to_string(::random()); +} + +const auto testEndpoint = "127.0.0.1:8880"s; + +class TestFam { +public: + ~TestFam() { destroyRegions(); } + + void destroyRegions() { + for (auto&& region : regions_) { region->destroy(); } + } + + static auto makeRandomText(const std::string& text = "") -> std::string { + return "ECKIT_TEST_FAM_" + text + '_' + randomNumber(); + } + + auto makeRandomRegion(const eckit::fam::size_t size) -> FamRegion::SPtr { + auto region = name_.withRegion(makeRandomText("REGION")).create(size, 0640, true); + return regions_.emplace_back(region.clone()); + } + + auto getLastRegion() const -> FamRegion::SPtr { return regions_.back(); } + +private: + FamRegionName name_ {testEndpoint, {}}; + + std::vector regions_; +}; + +} // namespace fam + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit::test diff --git a/tests/io/test_fam.cc b/tests/io/test_fam.cc new file mode 100644 index 000000000..8ce8b55ab --- /dev/null +++ b/tests/io/test_fam.cc @@ -0,0 +1,271 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file test_fam.cc +/// @author Metin Cakircali +/// @date May 2024 + +#include "eckit/config/LibEcKit.h" +#include "eckit/io/Buffer.h" +#include "eckit/io/fam/FamRegion.h" +#include "eckit/testing/Test.h" +#include "fam_common.h" + +using namespace eckit; +using namespace eckit::testing; + +namespace eckit::test { + +//---------------------------------------------------------------------------------------------------------------------- + +CASE("FamPath: ctor and uuid generation") { + { + // uuid of "/region/object" + constexpr auto* const uuid = "650fa148-fc69-5d6f-a793-5b1190c77e1a"; + + const FamPath path {"region", "object"}; + EXPECT_EQUAL(path.generateUUID(), uuid); + + EXPECT_EQUAL(FamPath("/region/object").generateUUID(), uuid); + } + + { // assert uri.scheme + const auto uri = URI("/regionName/objectName"); + EXPECT_THROWS_AS(FamPath {uri}, eckit::Exception); + } + + { + const auto uri = URI("fam://" + fam::testEndpoint + "/regionName/objectName"); + EXPECT_NO_THROW(FamPath {uri}); + } + + { + const auto uri = URI("fam", fam::testEndpoint, "/regionName/objectName"); + EXPECT_EQUAL(uri.scheme(), FamPath::scheme); + EXPECT_EQUAL(uri.hostport(), fam::testEndpoint); + EXPECT_EQUAL(uri.name(), "/regionName/objectName"); + EXPECT_NO_THROW(const auto path = FamPath(uri)); + } + + { + const auto uri = URI("fam://" + fam::testEndpoint + "/regionName/objectName"); + + EXPECT_EQUAL(uri.scheme(), FamPath::scheme); + EXPECT_EQUAL(uri.hostport(), fam::testEndpoint); + EXPECT_EQUAL(uri.name(), "/regionName/objectName"); + } +} + +CASE("FamRegionName: ctor, lookup, and allocate") { + const auto regionName = fam::TestFam::makeRandomText("REGION"); + + const FamRegionName region(fam::testEndpoint, regionName); + + EXPECT_EQUAL(region.uri().scheme(), FamPath::scheme); + EXPECT_EQUAL(region.uri().hostport(), fam::testEndpoint); + EXPECT_EQUAL(region.uri().name(), '/' + regionName); + EXPECT_EQUAL(region.uri(), URI("fam://" + fam::testEndpoint + '/' + regionName)); + EXPECT_EQUAL(region.asString(), "fam://" + fam::testEndpoint + '/' + regionName); + EXPECT_EQUAL(region.path().regionName, regionName); + + EXPECT_THROWS_AS(region.lookup(), NotFound); + + EXPECT_NOT(region.exists()); + + EXPECT_NO_THROW(region.create(1024, 0640)); + + EXPECT(region.exists()); + + EXPECT_NO_THROW(region.lookup()); + + { + auto name = FamRegionName(fam::testEndpoint, ""); + EXPECT_NO_THROW(name.withRegion(regionName).lookup().destroy()); + } +} + +CASE("FamObjectName: ctor, lookup, and allocate") { + FamPath path {fam::TestFam::makeRandomText("REGION"), fam::TestFam::makeRandomText("OBJECT")}; + + // create region + EXPECT_NO_THROW(FamRegionName(fam::testEndpoint, "").withRegion(path.regionName).create(1024, 0640)); + + const FamObjectName object(fam::testEndpoint, path); + + EXPECT_EQUAL(object.uri().scheme(), FamPath::scheme); + EXPECT_EQUAL(object.uri().hostport(), fam::testEndpoint); + EXPECT_EQUAL(object.uri().name(), path.asString()); + EXPECT_EQUAL(object.uri(), URI("fam", fam::testEndpoint, path.asString())); + EXPECT_EQUAL(object.asString(), "fam://" + fam::testEndpoint + path.asString()); + EXPECT_EQUAL(object.path(), path); + + EXPECT_THROWS_AS(object.lookup(), NotFound); + + EXPECT_NOT(object.exists()); + + EXPECT_THROWS_AS(object.allocate(1025), OutOfStorage); + + EXPECT_NO_THROW(object.allocate(512)); + + EXPECT(object.exists()); + + EXPECT_NO_THROW(object.lookup().deallocate()); +} + +CASE("FamRegion: lookup, create, validate properties, and destroy") { + FamRegion::UPtr region; + + const auto regionName = fam::TestFam::makeRandomText("REGION"); + const auto regionSize = 1024; + const auto regionPerm = static_cast(0640); + + { + const FamRegionName name {fam::testEndpoint, regionName}; + EXPECT_THROWS_AS(name.lookup(), NotFound); + EXPECT_NO_THROW(name.create(regionSize, regionPerm)); + EXPECT_NO_THROW(region = name.lookup().clone()); + } + + EXPECT_EQUAL(region->size(), regionSize); + EXPECT_EQUAL(region->permissions(), regionPerm); + EXPECT_EQUAL(region->name(), regionName); + + const FamProperty prop {regionSize, regionPerm, regionName}; + EXPECT_EQUAL(region->property(), prop); + + EXPECT_NO_THROW(region->destroy()); + + EXPECT_THROWS_AS(FamRegionName(fam::testEndpoint, regionName).lookup(), NotFound); +} + +//---------------------------------------------------------------------------------------------------------------------- + +CASE("FamObject: lookup, create, and destroy") { + const auto regionName = fam::TestFam::makeRandomText("REGION"); + const auto regionSize = 64; + const auto regionPerm = static_cast(0640); + + const auto objectName = fam::TestFam::makeRandomText("OBJECT"); + const auto objectSize = 24; + const auto objectPerm = static_cast(0400); + + const auto path = '/' + regionName + '/' + objectName; + + EXPECT_NO_THROW(FamRegionName(fam::testEndpoint, path).create(regionSize, regionPerm)); + + FamObject::UPtr object; + + // object inherits permissions from region + EXPECT_NO_THROW(object = FamObjectName(fam::testEndpoint, path).allocate(objectSize).clone()); + + const FamProperty prop {objectSize, regionPerm, objectName}; + EXPECT_EQUAL(prop, object->property()); + + EXPECT_NO_THROW(object->deallocate()); + + { + auto name = FamRegionName(fam::testEndpoint, ""); + + auto region = name.withRegion(regionName).lookup(); + + EXPECT_THROWS_AS(region.lookupObject(objectName), NotFound); + + { + const auto size = 12; + EXPECT_NO_THROW(region.allocateObject(size, objectPerm, objectName)); + EXPECT(region.lookupObject(objectName).size() == size); + } + + // overwrite: allocate with different size + EXPECT_NO_THROW(region.allocateObject(objectSize, objectPerm, objectName, true)); + + auto object = region.lookupObject(objectName); + + const FamProperty prop {objectSize, objectPerm, objectName}; + EXPECT(object.property() == prop); + + EXPECT_NO_THROW(object.deallocate()); + + EXPECT_THROWS_AS(region.lookupObject(objectName), NotFound); + + EXPECT_NO_THROW(region.destroy()); + + EXPECT_THROWS_AS(name.lookup(), NotFound); + } +} + +CASE("FamObject: large data small object") { + const auto regionName = fam::TestFam::makeRandomText("REGION"); + const auto regionSize = 64; + const auto regionPerm = static_cast(0640); + + const auto objectName = fam::TestFam::makeRandomText("OBJECT"); + const auto objectSize = 32; + const auto objectPerm = static_cast(0400); + + const FamPath path {regionName, objectName}; + + { + auto region = FamRegionName(fam::testEndpoint, path).create(regionSize, regionPerm, true); + + // object bigger than region + EXPECT_THROWS_AS(region.allocateObject(regionSize + 1, objectPerm, objectName), OutOfStorage); + EXPECT_THROWS_AS(region.lookupObject(objectName), NotFound); + + EXPECT(regionSize >= objectSize); + + // object fits + EXPECT_NO_THROW(region.allocateObject(objectSize, objectPerm, objectName)); + EXPECT_NO_THROW(region.lookupObject(objectName)); + EXPECT_NO_THROW(region.deallocateObject(objectName)); + EXPECT_THROWS_AS(region.lookupObject(objectName), NotFound); + } + + // data ops + + const auto testData = "ECKIT_TEST_FAM_DATA_2048413561EC"s; // size=32 + + { // write + auto object = FamObjectName(fam::testEndpoint, path).allocate(objectSize, true); + EXPECT_NO_THROW(object.put(testData.data(), 0, testData.size())); + } + + { // read + auto object = FamObjectName(fam::testEndpoint, path).lookup(); + + Buffer testBuffer(object.size()); + testBuffer.zero(); + + EXPECT_NO_THROW(object.get(testBuffer.data(), 0, testBuffer.size())); + + EXPECT(testData == testBuffer.view()); + } + + { // cleanup + auto region = FamRegionName(fam::testEndpoint, path); + + EXPECT_NO_THROW(region.lookup().destroy()); + + EXPECT_THROWS_AS(region.lookup(), NotFound); + } +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace eckit::test + +int main(int argc, char** argv) { + return run_tests(argc, argv); +} diff --git a/tests/io/test_famlist.cc b/tests/io/test_famlist.cc new file mode 100644 index 000000000..ea69a6369 --- /dev/null +++ b/tests/io/test_famlist.cc @@ -0,0 +1,125 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +/* + * This software was developed as part of the Horizon Europe programme funded project OpenCUBE + * (Grant agreement: 101092984) horizon-opencube.eu + */ + +/// @file test_famlist.cc +/// @author Metin Cakircali +/// @date May 2024 + +#include "eckit/config/LibEcKit.h" +#include "eckit/io/Buffer.h" +#include "eckit/io/fam/FamConfig.h" +#include "eckit/io/fam/FamList.h" +#include "eckit/testing/Test.h" +#include "fam_common.h" + +#include +#include +#include + +using namespace eckit; +using namespace eckit::testing; + +namespace eckit::test { + +//---------------------------------------------------------------------------------------------------------------------- +// HELPERS + +namespace { + +fam::TestFam tester; + +constexpr const auto numThreads = 8; +constexpr const auto listSize = 200; +const auto listName = test::fam::TestFam::makeRandomText("LIST"); +const auto listData = test::fam::TestFam::makeRandomText("DATA"); + +std::vector testData; +std::mutex testMutex; + +auto makeTestData(const int number) -> std::string_view { + std::ostringstream oss; + oss << "tid:" << std::this_thread::get_id() << " #" << number << '-' << listData; + // add to the control list + const std::lock_guard lock(testMutex); + return testData.emplace_back(oss.str()); +} + +void populateList() { + FamList lst(*tester.getLastRegion(), listName); + for (auto i = 0; i < listSize; i++) { + auto buffer = makeTestData(i); + EXPECT_NO_THROW(lst.push_back(buffer.data(), buffer.size())); + } +} + +} // namespace + +//---------------------------------------------------------------------------------------------------------------------- + +CASE("FamList: create an empty list and validate size, empty, front, back") { + auto listRegion = tester.makeRandomRegion(1024); + + const auto lst = FamList(*listRegion, listName); + + EXPECT(lst.empty()); + + EXPECT_EQUAL(lst.size(), 0); + + Buffer front; + EXPECT_NO_THROW(front = lst.front()); + EXPECT_EQUAL(front.size(), 0); + /// @todo remove comment once Buffer fixed + // EXPECT_EQUAL(front.data(), nullptr); + + Buffer back; + EXPECT_NO_THROW(back = lst.back()); + EXPECT_EQUAL(back.size(), 0); + /// @todo remove comment once Buffer fixed + // EXPECT_EQUAL(back.data(), nullptr); +} + +//---------------------------------------------------------------------------------------------------------------------- + +CASE("FamList: populate with " + std::to_string(listSize) + " items by " + std::to_string(numThreads) + " threads") { + std::vector threads; + + threads.reserve(numThreads); + + for (auto i = 0; i < numThreads; i++) { threads.emplace_back(populateList); } + + for (auto&& thread : threads) { thread.join(); } +} + +//---------------------------------------------------------------------------------------------------------------------- + +CASE("FamList: validate size and values after creation") { + const auto lst = FamList(*tester.getLastRegion(), listName); + + EXPECT_NOT(lst.empty()); + + EXPECT_EQUAL(lst.size(), numThreads * listSize); + + for (const auto& buffer : lst) { + EXPECT(std::find(testData.cbegin(), testData.cend(), buffer.view()) != testData.cend()); + } +} + +} // namespace eckit::test + +//---------------------------------------------------------------------------------------------------------------------- + +int main(int argc, char** argv) { + return run_tests(argc, argv); +}