From 99a1ca36969208b765c9bec9557776f748204831 Mon Sep 17 00:00:00 2001 From: Matthieu Dorier Date: Thu, 6 Jun 2024 09:39:09 +0100 Subject: [PATCH] started python and C++ interface --- CMakeLists.txt | 11 ++ build.sh | 2 +- include/flock/cxx/client.hpp | 78 +++++++++ include/flock/cxx/exception.hpp | 46 ++++++ include/flock/cxx/group-view.hpp | 148 +++++++++++++++++ include/flock/cxx/group.hpp | 198 ++++++++++++++++++++++ include/flock/cxx/server.hpp | 143 ++++++++++++++++ python/CMakeLists.txt | 24 +++ python/src/.py-flock-common.cpp.swp | Bin 0 -> 16384 bytes python/src/py-flock-client.cpp | 153 +++++++++++++++++ python/src/py-flock-common.cpp | 41 +++++ python/src/py-flock-server.cpp | 248 ++++++++++++++++++++++++++++ spack.yaml | 2 + tests/spack.yaml | 2 + 14 files changed, 1095 insertions(+), 1 deletion(-) create mode 100644 include/flock/cxx/client.hpp create mode 100644 include/flock/cxx/exception.hpp create mode 100644 include/flock/cxx/group-view.hpp create mode 100644 include/flock/cxx/group.hpp create mode 100644 include/flock/cxx/server.hpp create mode 100644 python/CMakeLists.txt create mode 100644 python/src/.py-flock-common.cpp.swp create mode 100644 python/src/py-flock-client.cpp create mode 100644 python/src/py-flock-common.cpp create mode 100644 python/src/py-flock-server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 60bd3fd..071ce5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ # See COPYRIGHT in top-level directory. cmake_minimum_required (VERSION 3.8) project (flock C CXX) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) enable_testing () add_definitions (-Wextra -Wall -Wpedantic) @@ -13,6 +15,7 @@ option (ENABLE_EXAMPLES "Build examples" OFF) option (ENABLE_BEDROCK "Build bedrock module" OFF) option (ENABLE_COVERAGE "Build with coverage" OFF) option (ENABLE_MPI "Build with MPI support" OFF) +option (ENABLE_PYTHON "Build with Python support" OFF) # library version set here (e.g. for shared libs). set (FLOCK_VERSION_MAJOR 0) @@ -67,6 +70,14 @@ if (ENABLE_MPI) find_package (MPI REQUIRED) endif () +# search for Python +if (ENABLE_PYTHON) + set (Python3_FIND_STRATEGY LOCATION) + find_package (Python3 COMPONENTS Interpreter Development REQUIRED) + find_package (pybind11 REQUIRED) + add_subdirectory (python) +endif () + add_subdirectory (src) if (${ENABLE_TESTS}) enable_testing () diff --git a/build.sh b/build.sh index 92573ba..93061d6 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ #!/bin/bash SCRIPT_DIR=$(dirname "$0") -cmake .. -DENABLE_TESTS=ON -DENABLE_BEDROCK=ON -DENABLE_EXAMPLES=ON -DENABLE_MPI=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +cmake .. -DENABLE_TESTS=ON -DENABLE_BEDROCK=ON -DENABLE_EXAMPLES=ON -DENABLE_MPI=ON -DENABLE_PYTHON=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON make diff --git a/include/flock/cxx/client.hpp b/include/flock/cxx/client.hpp new file mode 100644 index 0000000..c1f0a83 --- /dev/null +++ b/include/flock/cxx/client.hpp @@ -0,0 +1,78 @@ +/* + * (C) 2024 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#ifndef __FLOCK_CLIENT_HPP +#define __FLOCK_CLIENT_HPP + +#include +#include +#include +#include + +namespace flock { + +class Client { + + public: + + Client() = default; + + Client(margo_instance_id mid, ABT_pool pool = ABT_POOL_NULL) { + auto err = flock_client_init(mid, pool, &m_client); + FLOCK_CONVERT_AND_THROW(err); + } + + ~Client() { + if(m_client != FLOCK_CLIENT_NULL) { + flock_client_finalize(m_client); + } + } + + Client(Client&& other) + : m_client(other.m_client) { + other.m_client = FLOCK_CLIENT_NULL; + } + + Client(const Client&) = delete; + + Client& operator=(const Client&) = delete; + + Client& operator=(Client&& other) { + if(this == &other) return *this; + if(m_client != FLOCK_CLIENT_NULL) { + flock_client_finalize(m_client); + } + m_client = other.m_client; + other.m_client = FLOCK_CLIENT_NULL; + return *this; + } + + GroupHandle makeGroupHandle( + hg_addr_t addr, + uint16_t provider_id, + uint32_t mode = 0) const { + flock_group_handle_t gh; + auto err = flock_group_handle_create( + m_client, addr, provider_id, mode, &gh); + FLOCK_CONVERT_AND_THROW(err); + return GroupHandle(gh, false); + } + + auto handle() const { + return m_client; + } + + operator flock_client_t() const { + return m_client; + } + + private: + + flock_client_t m_client = FLOCK_CLIENT_NULL; +}; + +} + +#endif diff --git a/include/flock/cxx/exception.hpp b/include/flock/cxx/exception.hpp new file mode 100644 index 0000000..f80973e --- /dev/null +++ b/include/flock/cxx/exception.hpp @@ -0,0 +1,46 @@ +/* + * (C) 2024 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#ifndef __FLOCK_EXCEPTION_HPP +#define __FLOCK_EXCEPTION_HPP + +#include +#include + +namespace flock { + +class Exception : public std::exception { + + public: + + Exception(flock_return_t code) + : m_code(code) {} + + const char* what() const noexcept override { + #define X(__err__, __msg__) case __err__: return __msg__; + switch(m_code) { + FLOCK_RETURN_VALUES + } + #undef X + return "Unknown error"; + } + + auto code() const { + return m_code; + } + + private: + + flock_return_t m_code; +}; + +#define FLOCK_CONVERT_AND_THROW(__err__) do { \ + if((__err__) != FLOCK_SUCCESS) { \ + throw ::flock::Exception((__err__)); \ + } \ +} while(0) + +} +#endif diff --git a/include/flock/cxx/group-view.hpp b/include/flock/cxx/group-view.hpp new file mode 100644 index 0000000..3fd69fe --- /dev/null +++ b/include/flock/cxx/group-view.hpp @@ -0,0 +1,148 @@ +/* + * (C) 2024 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#ifndef __FLOCK_GROUP_VIEW_HPP +#define __FLOCK_GROUP_VIEW_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace flock { + +class GroupHandle; +class Provider; + +class GroupView { + + friend class GroupHandle; + friend class Provider; + + public: + + struct Member { + uint64_t rank; + uint16_t provider_id; + std::string address; + }; + + GroupView() = default; + + GroupView(flock_group_view_t view) { + FLOCK_GROUP_VIEW_MOVE(&view, &m_view); + } + + ~GroupView() { + flock_group_view_clear(&m_view); + } + + GroupView(GroupView&& other) { + FLOCK_GROUP_VIEW_MOVE(&other.m_view, &m_view); + } + + GroupView& operator=(GroupView&& other) { + if(this == &other) return *this; + flock_group_view_clear(&m_view); + FLOCK_GROUP_VIEW_MOVE(&other.m_view, &m_view); + return *this; + } + + auto digest() const { + return m_view.digest; + } + + void lock() { + FLOCK_GROUP_VIEW_LOCK(&m_view); + } + + void unlock() { + FLOCK_GROUP_VIEW_UNLOCK(&m_view); + } + + void clear() { + flock_group_view_clear(&m_view); + } + + void addMember(uint64_t rank, const char* address, uint16_t provider_id) { + auto member = flock_group_view_add_member(&m_view, rank, provider_id, address); + if(!member) throw Exception{FLOCK_ERR_RANK_USED}; + } + + void removeMember(uint64_t rank) { + if(!flock_group_view_remove_member(&m_view, rank)) + throw Exception{FLOCK_ERR_NO_MEMBER}; + } + + Member findMember(uint64_t rank) const { + auto member = flock_group_view_find_member(&m_view, rank); + if(!member) throw Exception{FLOCK_ERR_NO_MEMBER}; + return Member{member->rank, member->provider_id, member->address}; + } + + std::vector members() const { + std::vector result; + result.reserve(m_view.members.size); + for(size_t i = 0; i < m_view.members.size; ++i) { + result.push_back(Member{ + m_view.members.data[i].rank, + m_view.members.data[i].provider_id, + m_view.members.data[i].address + }); + } + return result; + } + + size_t maxNumMembers() const { + return m_view.members.data[m_view.members.size-1].rank; + } + + size_t numLiveMembers() const { + return m_view.members.size; + } + + void setMetadata(const char* key, const char* value) { + flock_group_view_add_metadata(&m_view, key, value); + } + + void removeMetadata(const char* key) { + if(!flock_group_view_remove_metadata(&m_view, key)) + throw Exception{FLOCK_ERR_NO_METADATA}; + } + + std::map metadata() const { + std::map result; + for(size_t i = 0; i < m_view.metadata.size; ++i) { + result.insert( + std::make_pair( + m_view.metadata.data[i].key, + m_view.metadata.data[i].value)); + } + return result; + } + + std::string toString(margo_instance_id mid, uint64_t credentials = 0) const { + std::string result; + flock_return_t ret = flock_group_view_serialize( + mid, credentials, &m_view, + [](void* ctx, const char* content, size_t size) { + auto str = static_cast(ctx); + str->assign(content, size); + }, static_cast(&result)); + if(ret != FLOCK_SUCCESS) throw Exception{ret}; + return result; + } + + private: + + flock_group_view_t m_view = FLOCK_GROUP_VIEW_INITIALIZER; +}; + +} + +#endif diff --git a/include/flock/cxx/group.hpp b/include/flock/cxx/group.hpp new file mode 100644 index 0000000..31bebbd --- /dev/null +++ b/include/flock/cxx/group.hpp @@ -0,0 +1,198 @@ +/* + * (C) 2024 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#ifndef __FLOCK_GROUP_HPP +#define __FLOCK_GROUP_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace flock { + +class Client; + +class GroupHandle { + + friend class Client; + + public: + + GroupHandle() = default; + + GroupHandle(flock_group_handle_t gh, bool copy=true) + : m_gh(gh) { + if(copy && (m_gh != FLOCK_GROUP_HANDLE_NULL)) { + auto err = flock_group_handle_ref_incr(m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + } + + GroupHandle(flock_client_t client, + hg_addr_t addr, + uint16_t provider_id, + bool check = true) + { + auto err = flock_group_handle_create(client, + addr, provider_id, check, &m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + + GroupHandle(const GroupHandle& other) + : m_gh(other.m_gh) { + if(m_gh != FLOCK_GROUP_HANDLE_NULL) { + auto err = flock_group_handle_ref_incr(m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + } + + GroupHandle(GroupHandle&& other) + : m_gh(other.m_gh) { + other.m_gh = FLOCK_GROUP_HANDLE_NULL; + } + + GroupHandle& operator=(const GroupHandle& other) { + if(m_gh == other.m_gh || &other == this) + return *this; + if(m_gh != FLOCK_GROUP_HANDLE_NULL) { + auto err = flock_group_handle_release(m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + m_gh = other.m_gh; + if(m_gh != FLOCK_GROUP_HANDLE_NULL) { + auto err = flock_group_handle_ref_incr(m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + return *this; + } + + GroupHandle& operator=(GroupHandle&& other) { + if(m_gh == other.m_gh || &other == this) + return *this; + if(m_gh != FLOCK_GROUP_HANDLE_NULL) { + auto err = flock_group_handle_release(m_gh); + FLOCK_CONVERT_AND_THROW(err); + } + m_gh = other.m_gh; + other.m_gh = FLOCK_GROUP_HANDLE_NULL; + return *this; + } + + ~GroupHandle() { + if(m_gh != FLOCK_GROUP_HANDLE_NULL) { + flock_group_handle_release(m_gh); + } + } + + static GroupHandle FromFile( + flock_client_t client, + const char* filename, + uint32_t mode = 0) { + flock_group_handle_t gh; + auto err = flock_group_handle_create_from_file(client, filename, mode, &gh); + FLOCK_CONVERT_AND_THROW(err); + return GroupHandle{gh}; + } + + static GroupHandle FromSerialized( + flock_client_t client, + std::string_view serialized_view, + uint32_t mode = 0) { + flock_group_handle_t gh; + auto err = flock_group_handle_create_from_serialized( + client, serialized_view.data(), serialized_view.size(), mode, &gh); + FLOCK_CONVERT_AND_THROW(err); + return GroupHandle{gh}; + } + + operator std::string() const { + std::string result; + auto err = flock_group_serialize(m_gh, + [](void* uargs, const char* content, size_t size) -> void { + auto s = static_cast(uargs); + *s = std::string{content, size}; + }, &result); + FLOCK_CONVERT_AND_THROW(err); + return result; + } + + template + void serialize(const Function& function) const { + struct { + const Function& cb; + std::exception_ptr ex; + } context{function, nullptr}; + auto err = flock_group_serialize(m_gh, + [](void* uargs, const char* content, size_t size) -> void { + auto context_ptr = static_cast(uargs); + try { + (context_ptr->cb)(std::string_view{content, size}); + } catch(...) { + context_ptr->ex = std::current_exception(); + } + }, &context); + if(context.ex) std::rethrow_exception(context.ex); + FLOCK_CONVERT_AND_THROW(err); + } + + void serializeToFile(const char* filename) const { + auto err = flock_group_serialize_to_file(m_gh, filename); + FLOCK_CONVERT_AND_THROW(err); + } + + uint64_t digest() const { + uint64_t d; + auto err = flock_group_digest(m_gh, &d); + FLOCK_CONVERT_AND_THROW(err); + return d; + } + + GroupView view() const { + GroupView v; + auto err = flock_group_get_view(m_gh, &v.m_view); + FLOCK_CONVERT_AND_THROW(err); + return v; + } + + size_t maxNumMembers() const { + size_t count; + auto err = flock_group_size(m_gh, &count); + FLOCK_CONVERT_AND_THROW(err); + return count; + } + + size_t numLiveMembers() const { + size_t count; + auto err = flock_group_live_member_count(m_gh, &count); + FLOCK_CONVERT_AND_THROW(err); + return count; + } + + void update() const { + auto err = flock_group_update_view(m_gh, NULL); + FLOCK_CONVERT_AND_THROW(err); + } + + auto handle() const { + return m_gh; + } + + operator flock_group_handle_t() const { + return m_gh; + } + + private: + + flock_group_handle_t m_gh = FLOCK_GROUP_HANDLE_NULL; + +}; + +} + +#endif diff --git a/include/flock/cxx/server.hpp b/include/flock/cxx/server.hpp new file mode 100644 index 0000000..43d175c --- /dev/null +++ b/include/flock/cxx/server.hpp @@ -0,0 +1,143 @@ +/* + * (C) 2024 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#ifndef __FLOCK_CLIENT_HPP +#define __FLOCK_CLIENT_HPP + +#include +#include +#include + +namespace flock { + +class Observer { + + public: + + Observer(const Observer&) = delete; + Observer(Observer&&) = delete; + + virtual ~Observer() = default; + + virtual void onMembershipUpdate( + flock_update_t update, + uint64_t rank, + const char* address, + uint16_t provider_id) = 0; + + virtual void onMetadataUpdate( + const char* key, + const char* value) = 0; + +}; + +class Provider { + + public: + + Provider() = default; + + Provider(margo_instance_id mid, + uint16_t provider_id, + const char* config, + GroupView& initial_view, + ABT_pool pool = ABT_POOL_NULL, + uint64_t credentials = 0) { + m_mid = mid; + flock_provider_args args; + args.backend = nullptr; + args.credentials = credentials; + args.initial_view = &initial_view.m_view; + args.pool = pool; + auto err = flock_provider_register(mid, provider_id, config, &args, &m_provider); + FLOCK_CONVERT_AND_THROW(err); + margo_provider_push_finalize_callback( + mid, this, [](void* ctx) { + auto self = static_cast(ctx); + self->m_provider = FLOCK_PROVIDER_NULL; + }, this); + } + + ~Provider() { + if(m_provider != FLOCK_PROVIDER_NULL) { + margo_provider_pop_finalize_callback(m_mid, this); + flock_provider_destroy(m_provider); + } + } + + Provider(Provider&& other) + : m_provider(other.m_provider) { + margo_provider_pop_finalize_callback(other.m_mid, &other); + other.m_provider = FLOCK_PROVIDER_NULL; + margo_provider_push_finalize_callback( + m_mid, this, [](void* ctx) { + auto self = static_cast(ctx); + self->m_provider = FLOCK_PROVIDER_NULL; + }, this); + } + + Provider(const Provider&) = delete; + + Provider& operator=(const Provider&) = delete; + + Provider& operator=(Provider&& other) { + if(this == &other) return *this; + this->~Provider(); + m_provider = other.m_provider; + other.m_provider = FLOCK_PROVIDER_NULL; + margo_provider_pop_finalize_callback(other.m_mid, &other); + other.m_provider = FLOCK_PROVIDER_NULL; + margo_provider_push_finalize_callback( + m_mid, this, [](void* ctx) { + auto self = static_cast(ctx); + self->m_provider = FLOCK_PROVIDER_NULL; + }, this); + return *this; + } + + void addObserver(Observer* observer) { + auto membership_update_fn = + [](void* ctx, flock_update_t update, size_t rank, const char* address, uint16_t provider_id) { + static_cast(ctx)->onMembershipUpdate(update, rank, address, provider_id); + }; + auto metadata_update_fn = + [](void* ctx, const char* key, const char* value) { + static_cast(ctx)->onMetadataUpdate(key, value); + }; + auto err = flock_provider_add_update_callbacks( + m_provider, membership_update_fn, metadata_update_fn, observer); + FLOCK_CONVERT_AND_THROW(err); + } + + void removeObserver(Observer* observer) { + auto err = flock_provider_remove_update_callbacks(m_provider, observer); + FLOCK_CONVERT_AND_THROW(err); + } + + std::string config() const { + auto cfg = flock_provider_get_config(m_provider); + if(!cfg) throw Exception{FLOCK_ERR_OTHER}; + auto result = std::string{cfg}; + free(cfg); + return result; + } + + auto handle() const { + return m_provider; + } + + operator flock_provider_t() const { + return handle(); + } + + private: + + margo_instance_id m_mid = MARGO_INSTANCE_NULL; + flock_provider_t m_provider = FLOCK_PROVIDER_NULL; +}; + +} + +#endif diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000..1719652 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library (pyflock_client MODULE src/py-flock-client.cpp) +target_link_libraries (pyflock_client PRIVATE pybind11::module flock-client PRIVATE coverage_config) +pybind11_extension (pyflock_client) +pybind11_strip (pyflock_client) + +add_library (pyflock_server MODULE src/py-flock-server.cpp) +target_link_libraries (pyflock_server PRIVATE pybind11::module flock-server PRIVATE coverage_config) +pybind11_extension (pyflock_server) +pybind11_strip (pyflock_server) + +add_library (pyflock_common MODULE src/py-flock-common.cpp) +target_link_libraries (pyflock_common PRIVATE pybind11::module flock-client PRIVATE coverage_config) +pybind11_extension (pyflock_common) +pybind11_strip (pyflock_common) + +set (PY_VERSION ${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}) + +install (TARGETS pyflock_client pyflock_server pyflock_common + EXPORT flock-targets + ARCHIVE DESTINATION lib/python${PY_VERSION}/site-packages + LIBRARY DESTINATION lib/python${PY_VERSION}/site-packages) + +install (DIRECTORY mochi/bedrock + DESTINATION lib/python${PY_VERSION}/site-packages/mochi) diff --git a/python/src/.py-flock-common.cpp.swp b/python/src/.py-flock-common.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..5a35f8630a9d3beb1dd944280754847a6b086947 GIT binary patch literal 16384 zcmeI2TZ|J`7{`y`g%xm(CSG_jL)WbBy4%vl80f+(E(jY~SgwXdXlAFU?ZkFwn3=9y z&0pyw=bOt}I<{oT@QAQHnI`z$NXYz(n1AJLCwc!mLMmew#jG$x@TaUwMLAef zv+lJ5riRoUP1T(G@+DPzB&Ai&qN=0}UBXxET%V+t%c_!;G%ZBrND^fr%0OTSDrUZK z*+R0kKfQ$Mx?+okn{V14=n@&D3`7}-G7x1T%0QHXC<9Rjq6|bC_4uFTjEr5;nGRFKJ_yD{B3Sa|R z3Fd-h^9XqpjDbbq8~mQ(Ab1+cU^z&D)7KL65qK3;zY`)To~{LX;;rWYwk0np$LLeOctm)#0Kh%oOQ%I#-#_7*42z#mB;($ey!s@sMsygN8r%P|wrV0fc$2sptS zMttMg8wm=jbjt$cajq?wZ-A>y)ndrpE?n2lKbn2cjKa?(q|z-5?vG=2mf9U8xFmw} z8yF{`f9_So(L3=uCf%{sjy!?&AmHEPnN#~dMsbFn)(dlX;`pNYn#9va!&!Z9_I>v( zk8k^#a@;2q&bQi;6Uec1Os?@ds%BA`G>pdgX27cfcNo{88ZQaS9L;AnUDc>=RlM0dzu?Sd*6M0S+ac3OGa1|1cwb>r;<3D}8)l!LyIzN2P%_fQ6P;vHJTKK zmEcUq7A1T&)`)^?8<%rb&&oOew46vTp?8k1I@Lv`$?`c-m?A_LG8xOk%7*pbNrja@ zofc%n#JK5%L^V@Z6(^NzoD-IFy@~0KR?*6C!OyJaGF_wVs%|HyoXn|GJRz(UVh^&) z5v-p>3)U~C1v+6;#Xuji0W!Mtf#Hp-)9LKUrq$awticM`mN091^a|yK@HqA%xCUi~ z3PY<#w?frFGQ2vT%}SzX;I?lB0sPs3~b;B2{3wyuNYV^O8kP z8{}&0#o(7Xu~UXwN*3MDb&K14$ueYfpgg7e+;UC?fVyj^Vs8m1&)BH0NeMcK?nF5d;F8&D0mX=2DgLD*yCRU z(I?8le<=f#q>Cy!IbVCAHy+6B@jlrbXcS_Gq|2Ha%ZiO>FY{v``&}0A0+ld9!e~4l zwQvYB2TVA~4Ue+=d}mviAYmignGPWjlmr^5G0}Pfgbi#tw5PTz%lfxoU+siwrEO)e zm3t@=yCy;li8HZwJ=)bf{nRf{e*k~G;UEN=rgLdbvtn9~Bg8mENNRVru)66^#m;Wq z-N0`;K2~@23kPrOruTjyKCG{MU(;1Jv-J$|3^lMj`{w1-_?mzB2(9>mow{zaQ||tJ zNB-GbSCj55uwIV!Sq7|m$e=!%uUYR1UuNm0!L#6Y{J>s4`Sf2Od{65v-&X4~L|Uik zJv}crCTA&U9szG=r5QyDat?UM;+1MpC`TVggIB~BGQZj1wo8p2X0wt5yW!umTqD6y Tyyb1#S_BO;*Ve}tOp?C +#include +#include +#include +#include + +namespace py11 = pybind11; +using namespace pybind11::literals; + +struct margo_instance; +typedef struct margo_instance* margo_instance_id; +typedef py11::capsule pymargo_instance_id; + +#define MID2CAPSULE(__mid) py11::capsule((void*)(__mid), "margo_instance_id") +#define CAPSULE2MID(__caps) (margo_instance_id)(__caps) + +PYBIND11_MODULE(pyflock_client, m) { + m.doc() = "Flock client python extension"; +#if 0 + py11::register_exception(m, "Exception", PyExc_RuntimeError); + py11::class_(m, "Client") + .def(py11::init()) + .def_property_readonly("margo_instance_id", + [](const Client& client) { + return MID2CAPSULE(client.engine().get_margo_instance()); + }) + .def("make_service_handle", + &Client::makeServiceHandle, + "Create a ServiceHandle instance", + "address"_a, + "provider_id"_a=0) + .def("make_service_group_handle", + [](const Client& client, + const std::string& groupfile, + uint16_t provider_id) { + return client.makeServiceGroupHandle(groupfile, provider_id); + }, + "Create a ServiceHandle instance", + "group_file"_a, + "provider_id"_a=0) + .def("make_service_group_handle", + [](const Client& client, + const std::vector& addresses, + uint16_t provider_id) { + return client.makeServiceGroupHandle(addresses, provider_id); + }, + "Create a ServiceHandle instance", + "addresses"_a, + "provider_id"_a=0) + .def("make_service_group_handle", + [](const Client& client, + uint64_t gid, + uint16_t provider_id) { + return client.makeServiceGroupHandle(gid, provider_id); + }, + "Create a ServiceHandle instance", + "group_id"_a, + "provider_id"_a=0); + py11::class_(m, "ServiceHandle") + .def_property_readonly("client", &ServiceHandle::client) + .def_property_readonly("address", [](const ServiceHandle& sh) { + return static_cast(sh.providerHandle()); + }) + .def_property_readonly("provider_id", [](const ServiceHandle& sh) { + return sh.providerHandle().provider_id(); + }) + .def("get_config", + [](const ServiceHandle& sh) { + std::string config; + sh.getConfig(&config); + return config; + }) + .def("query_config", + [](const ServiceHandle& sh, const std::string& script) { + std::string result; + sh.queryConfig(script, &result); + return result; + }, "script"_a) + .def("load_module", + [](const ServiceHandle& sh, + const std::string& name, + const std::string& path) { + sh.loadModule(name, path); + }, "name"_a, "path"_a) + .def("add_provider", + [](const ServiceHandle& sh, + const std::string& description) { + uint16_t provider_id_out; + sh.addProvider(description, &provider_id_out); + return provider_id_out; + }, "description"_a) + .def("change_provider_pool", + [](const ServiceHandle& sh, + const std::string& provider_name, + const std::string& pool_name) { + sh.changeProviderPool(provider_name, pool_name); + }) + .def("add_client", + [](const ServiceHandle& sh, + const std::string& description) { + sh.addClient(description); + }, "description"_a) + .def("add_abtio_instance", + [](const ServiceHandle& sh, + const std::string& description) { + sh.addABTioInstance(description); + }, "description"_a = std::string("{}")) + .def("add_ssg_group", [](const ServiceHandle& sh, const std::string& config) { + sh.addSSGgroup(config); + }, + "description"_a) + .def("add_pool", [](const ServiceHandle& sh, const std::string& config) { + sh.addPool(config); + }, + "description"_a) + .def("remove_pool", [](const ServiceHandle& sh, const std::string& pool_name) { + sh.removePool(pool_name); + }, + "name"_a) + .def("add_xstream", [](const ServiceHandle& sh, const std::string& config) { + sh.addXstream(config); + }, + "description"_a) + .def("remove_xstream", [](const ServiceHandle& sh, const std::string& es_name) { + sh.removeXstream(es_name); + }, + "name"_a) + ; + py11::class_(m, "ServiceGroupHandle") + .def("refresh", &ServiceGroupHandle::refresh) + .def_property_readonly("size", &ServiceGroupHandle::size) + .def_property_readonly("client", &ServiceGroupHandle::client) + .def("__getitem__", &ServiceGroupHandle::operator[]) + .def("get_config", + [](const ServiceGroupHandle& sh) { + std::string config; + sh.getConfig(&config); + return config; + }) + .def("query_config", + [](const ServiceGroupHandle& sh, const std::string& script) { + std::string result; + sh.queryConfig(script, &result); + return result; + }, "script"_a) + ; +#endif +} diff --git a/python/src/py-flock-common.cpp b/python/src/py-flock-common.cpp new file mode 100644 index 0000000..bf3d090 --- /dev/null +++ b/python/src/py-flock-common.cpp @@ -0,0 +1,41 @@ +/* + * (C) 2018 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#include +#include +#include +#include + +namespace py11 = pybind11; +using namespace pybind11::literals; + +struct margo_instance; +typedef struct margo_instance* margo_instance_id; +typedef py11::capsule pymargo_instance_id; + +#define MID2CAPSULE(__mid) py11::capsule((void*)(__mid), "margo_instance_id") +#define CAPSULE2MID(__caps) (margo_instance_id)(__caps) + +PYBIND11_MODULE(pyflock_common, m) { + m.doc() = "Flock common python extension"; + py11::register_exception(m, "Exception", PyExc_RuntimeError); + + py11::class_(m, "Member") + .def_readonly("rank", &flock::GroupView::Member::rank) + .def_readonly("provider_id", &flock::GroupView::Member::provider_id) + .def_readonly("address", &flock::GroupView::Member::address); + + py11::class_(m, "GroupView") + .def(py11::init<>()) + .def_property_readonly("digest", &flock::GroupView::digest) + .def("clear", &flock::GroupView::clear) + .def("lock", &flock::GroupView::lock) + .def("unlock", &flock::GroupView::unlock) + .def("add_member", &flock::GroupView::addMember) + .def("remove_member", &flock::GroupView::removeMember) + .def("find_member", &flock::GroupView::findMember) + .def_property_readonly("members", &flock::GroupView::members) + ; +} diff --git a/python/src/py-flock-server.cpp b/python/src/py-flock-server.cpp new file mode 100644 index 0000000..d5ae1c5 --- /dev/null +++ b/python/src/py-flock-server.cpp @@ -0,0 +1,248 @@ +/* + * (C) 2018 The University of Chicago + * + * See COPYRIGHT in top-level directory. + */ +#include +#include + +namespace py11 = pybind11; +using namespace pybind11::literals; + +struct margo_instance; +typedef struct margo_instance* margo_instance_id; +typedef struct hg_addr* hg_addr_t; +typedef py11::capsule pymargo_instance_id; +typedef py11::capsule pyhg_addr_t; + +#define MID2CAPSULE(__mid) py11::capsule((void*)(__mid), "margo_instance_id") +#define CAPSULE2MID(__caps) (margo_instance_id)(__caps) + +#define ADDR2CAPSULE(__addr) py11::capsule((void*)(__addr), "hg_addr_t") +#define CAPSULE2ADDR(__caps) (hg_add_t)(__caps) + +PYBIND11_MODULE(pybedrock_server, m) { + m.doc() = "Flock server python extension"; +#if 0 + py11::register_exception(m, "Exception", PyExc_RuntimeError); + + py11::class_> named_dep(m, "NamedDependency"); + named_dep + .def_property_readonly("name", + [](std::shared_ptr nd) { return nd->getName(); }) + .def_property_readonly("type", + [](std::shared_ptr nd) { return nd->getType(); }) + .def_property_readonly("handle", + [](std::shared_ptr nd) { return nd->getHandle(); }) + ; + + py11::class_> (m, "ProviderDependency", named_dep) + .def_property_readonly("provider_id", + [](std::shared_ptr nd) { return nd->getProviderID(); }) + ; + + py11::class_> server(m, "Server"); + + py11::enum_(server, "ConfigType") + .value("JSON", ConfigType::JSON) + .value("JX9", ConfigType::JX9) + .export_values(); + + server + .def(py11::init([](const std::string& address, const std::string& config, + ConfigType configType, + const Jx9ParamMap& jx9Params) { + return std::make_shared(address, config, configType, jx9Params); + }), "address"_a, "config"_a="{}", "config_type"_a=ConfigType::JSON, + "jx9_params"_a=Jx9ParamMap{}) + .def("wait_for_finalize", + [](std::shared_ptr server) { + server->waitForFinalize(); + }) + .def("finalize", + [](std::shared_ptr server) { + server->finalize(); + }) + .def_property_readonly("margo_instance_id", + [](std::shared_ptr server) { + return MID2CAPSULE(server->getMargoManager().getMargoInstance()); + }) + .def_property_readonly("config", + [](std::shared_ptr server) { + return server->getCurrentConfig(); + }) + .def_property_readonly("margo_manager", + [](std::shared_ptr server) { + return server->getMargoManager(); + }) + .def_property_readonly("abtio_manager", + [](std::shared_ptr server) { + return server->getABTioManager(); + }) + .def_property_readonly("provider_manager", + [](std::shared_ptr server) { + return server->getProviderManager(); + }) + .def_property_readonly("client_manager", + [](std::shared_ptr server) { + return server->getClientManager(); + }) + .def_property_readonly("ssg_manager", + [](std::shared_ptr server) { + return server->getSSGManager(); + }) + ; + + py11::class_ (m, "MargoManager") + .def_property_readonly("margo_instance_id", [](const MargoManager& m) { + return MID2CAPSULE(m.getMargoInstance()); + }) + .def_property_readonly("default_handler_pool", &MargoManager::getDefaultHandlerPool) + .def_property_readonly("config", &MargoManager::getCurrentConfig) + .def("get_pool", [](const MargoManager& m, const std::string& pool_name) { + return m.getPool(pool_name); + }, "name"_a) + .def("get_pool", [](const MargoManager& m, uint32_t index) { + return m.getPool(index); + }, "index"_a) + .def("add_pool", &MargoManager::addPool, + "config"_a) + .def("remove_pool", [](MargoManager& m, const std::string& pool_name) { + return m.removePool(pool_name); + }, "name"_a) + .def("remove_pool", [](MargoManager& m, uint32_t index) { + return m.removePool(index); + }, "index"_a) + .def_property_readonly("num_pools", &MargoManager::getNumPools) + .def("get_xstream", [](const MargoManager& m, const std::string& es_name) { + return m.getXstream(es_name); + }, "name"_a) + .def("get_xstream", [](const MargoManager& m, uint32_t index) { + return m.getXstream(index); + }, "index"_a) + .def("add_xstream", &MargoManager::addXstream, + "config"_a) + .def("remove_xstream", [](MargoManager& m, const std::string& es_name) { + return m.removeXstream(es_name); + }, "name"_a) + .def("remove_xstream", [](MargoManager& m, uint32_t index) { + return m.removeXstream(index); + }, "index"_a) + .def_property_readonly("num_xstreams", &MargoManager::getNumXstreams) + ; + + py11::class_ (m, "SSGManager") + .def_property_readonly("config", [](const SSGManager& manager) { + return manager.getCurrentConfig().dump(); + }) + .def_property_readonly("num_groups", &SSGManager::getNumGroups) + .def("get_group", [](const SSGManager& ssg, const std::string& name) { + return ssg.getGroup(name); + }, "name_a") + .def("get_group", [](const SSGManager& ssg, size_t index) { + return ssg.getGroup(index); + }, "index_a") + .def("add_group", + [](SSGManager& ssg, + const std::string& name, + const py11::dict& config, + const std::shared_ptr& pool, + const std::string& bootstrap_method, + const std::string& group_file, + int64_t credential) { +#ifdef ENABLE_SSG + ssg_group_config_t cfg = SSG_GROUP_CONFIG_INITIALIZER; + cfg.ssg_credential = credential; +#define GET_SSG_FIELD(__field__) do { \ + if(config.contains(#__field__)) \ + cfg.swim_##__field__ = config[#__field__].cast(); \ + } while(0) + GET_SSG_FIELD(period_length_ms); + GET_SSG_FIELD(suspect_timeout_periods); + GET_SSG_FIELD(subgroup_member_count); + GET_SSG_FIELD(disabled); +#undef GET_SSG_FIELD + return ssg.addGroup(name, cfg, pool, bootstrap_method, group_file); +#else + throw Exception{"Bedrock was not compiled with SSG support"}; +#endif + }, "name"_a, "swim"_a=py11::dict{}, + "pool"_a=nullptr, "bootstrap"_a="init", + "group_file"_a="", "credential"_a=-1) + .def("resolve_address", [](const SSGManager& ssg, const std::string& address) { + return ADDR2CAPSULE(ssg.resolveAddress(address)); + }, "address"_a) + ; + + py11::class_ (m, "ABTioManager") + .def_property_readonly("config", [](const ABTioManager& manager) { + return manager.getCurrentConfig().dump(); + }) + .def_property_readonly("num_abtio_instances", &ABTioManager::numABTioInstances) + .def("get_abtio_instance", [](const ABTioManager& abtio, const std::string& name) { + return abtio.getABTioInstance(name); + }, "name"_a) + .def("get_abtio_instance", [](const ABTioManager& abtio, size_t index) { + return abtio.getABTioInstance(index); + }, "index"_a) + .def("add_abtio_instance", &ABTioManager::addABTioInstance) + ; + + py11::class_ (m, "ProviderManager") + .def_property_readonly("config", &ProviderManager::getCurrentConfig) + .def_property_readonly("num_providers", &ProviderManager::numProviders) + .def("get_provider", [](const ProviderManager& pm, size_t index) { + return pm.getProvider(index); + }, "index"_a) + .def("get_provider", [](const ProviderManager& pm, const std::string& name) { + return pm.getProvider(name); + }, "name"_a) + .def("lookup_provider", &ProviderManager::lookupProvider, + "spec"_a) + .def("deregister_provider", + &ProviderManager::deregisterProvider, + "spec"_a) + .def("add_provider", + &ProviderManager::addProviderFromJSON, + "description"_a) + .def("change_pool", + &ProviderManager::changeProviderPool, + "provider"_a, "pool"_a) + .def("migrate_provider", + &ProviderManager::migrateProvider, + "provider"_a, "dest_addr"_a, "dest_provider_id"_a, + "migration_config"_a, "remove_source"_a) + .def("snapshot_provider", + &ProviderManager::snapshotProvider, + "provider"_a, "dest_path"_a, "snapshot_config"_a, "remove_source"_a) + .def("restore_provider", + &ProviderManager::restoreProvider, + "provider"_a, "src_path"_a, "restore_config"_a) + ; + + py11::class_ (m, "ClientManager") + .def_property_readonly("config", &ClientManager::getCurrentConfig) + .def("get_client", [](const ClientManager& cm, const std::string& name) { + return cm.getClient(name); + }, + "name"_a) + .def("get_client", [](const ClientManager& cm, size_t index) { + return cm.getClient(index); + }, + "index"_a) + .def_property_readonly("num_clients", &ClientManager::numClients) + .def("remove_client", [](ClientManager& cm, const std::string& name) { + return cm.removeClient(name); + }, + "name"_a) + .def("remove_client", [](ClientManager& cm, size_t index) { + return cm.removeClient(index); + }, + "index"_a) + .def("get_client_or_create", &ClientManager::getOrCreateAnonymous, + "type"_a) + .def("add_client", &ClientManager::addClientFromJSON, + "description"_a) + ; +#endif +} diff --git a/spack.yaml b/spack.yaml index 9f4a121..a0d2189 100644 --- a/spack.yaml +++ b/spack.yaml @@ -5,6 +5,8 @@ spack: - mochi-margo - json-c - mochi-bedrock + - py-pybind11 + - py-mochi-margo - mpi concretizer: unify: true diff --git a/tests/spack.yaml b/tests/spack.yaml index f5218e9..74adbb7 100644 --- a/tests/spack.yaml +++ b/tests/spack.yaml @@ -6,6 +6,8 @@ spack: - json-c - mochi-bedrock - mpich + - py-pybind11 + - py-mochi-margo concretizer: unify: true reuse: true