From ffdbaac9062f783400011d26bd55fc37264ec6c5 Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Thu, 9 Oct 2025 09:46:11 +0200 Subject: [PATCH 1/7] refactor: remove rpc fields from instance manager --- include/hicr/core/instanceManager.hpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/include/hicr/core/instanceManager.hpp b/include/hicr/core/instanceManager.hpp index d203617d..0e73376a 100644 --- a/include/hicr/core/instanceManager.hpp +++ b/include/hicr/core/instanceManager.hpp @@ -57,16 +57,6 @@ class InstanceManager { public: - /** - * Type definition for an index for a listenable unit. - */ - using RPCTargetIndex_t = uint64_t; - - /** - * Type definition for a function that can be executed as RPC - */ - using RPCFunction_t = std::function; - /** * Type definition for an unsorted set of unique pointers to the detected instances */ @@ -219,11 +209,6 @@ class InstanceManager * Pointer to current instance */ std::shared_ptr _currentInstance; - - /** - * Map of executable functions, representing potential RPC requests - */ - std::map _RPCTargetMap; }; } // namespace HiCR From 596b0909807b3b4c1dcbee8525ffd28d71d3fbb0 Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Thu, 9 Oct 2025 09:46:27 +0200 Subject: [PATCH 2/7] feat: add first implementation of pthreads instance manager --- include/hicr/backends/pthreads/instance.hpp | 26 +++++++ .../backends/pthreads/instanceManager.hpp | 67 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 include/hicr/backends/pthreads/instance.hpp create mode 100644 include/hicr/backends/pthreads/instanceManager.hpp diff --git a/include/hicr/backends/pthreads/instance.hpp b/include/hicr/backends/pthreads/instance.hpp new file mode 100644 index 00000000..e868bb7e --- /dev/null +++ b/include/hicr/backends/pthreads/instance.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace HiCR::backend::pthreads +{ + +class Instance : public HiCR::Instance +{ + public: + + Instance(instanceId_t currentInstanceId, instanceId_t rootInstanceId) + : HiCR::Instance(currentInstanceId), + _rootInstanceId(rootInstanceId){}; + + ~Instance() = default; + + bool isRootInstance() const override { return getId() != _rootInstanceId; }; + + private: + + instanceId_t _rootInstanceId; +}; +} // namespace HiCR::backend::pthreads \ No newline at end of file diff --git a/include/hicr/backends/pthreads/instanceManager.hpp b/include/hicr/backends/pthreads/instanceManager.hpp new file mode 100644 index 00000000..66dfee4a --- /dev/null +++ b/include/hicr/backends/pthreads/instanceManager.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +#include "instance.hpp" + +namespace HiCR::backend::pthreads +{ + +class InstanceManager; + +/** + * Type for any entrypoint function + */ +typedef std::function entryPoint_t; + +class InstanceManager : public HiCR::InstanceManager +{ + public: + + InstanceManager(Instance::instanceId_t rootInstanceId, entryPoint_t entrypoint) + : HiCR::InstanceManager(), + _rootInstanceId(rootInstanceId), + _entrypoint(entrypoint) + { + setCurrentInstance(std::make_shared(pthread_self(), rootInstanceId)); + } + + ~InstanceManager() = default; + + std::shared_ptr createInstanceImpl(const HiCR::InstanceTemplate instanceTemplate) override + { + Instance::instanceId_t newInstanceId; + auto status = pthread_create(&newInstanceId, nullptr, launchWrapper, this); + if (status != 0) { HICR_THROW_RUNTIME("Could not create instance thread. Error: %d", status); } + return std::make_shared(newInstanceId, _rootInstanceId); + } + + std::shared_ptr addInstanceImpl(Instance::instanceId_t instanceId) override { return std::make_shared(instanceId, _rootInstanceId); } + + void terminateInstanceImpl(const std::shared_ptr instance) override + { + HICR_THROW_LOGIC("The Host backend does not currently support the termination of instances during runtime"); + } + + void finalize() override {} + void abort(int errorCode) override { exit(errorCode); } + + HiCR::Instance::instanceId_t getRootInstanceId() const override { return _rootInstanceId; } + + entryPoint_t getEntrypoint() const { return _entrypoint; } + + private: + + __INLINE__ static void *launchWrapper(void *im) + { + auto instanceManager = static_cast(im); + instanceManager->_entrypoint(instanceManager); + return 0; + } + + HiCR::Instance::instanceId_t _rootInstanceId; + entryPoint_t _entrypoint; +}; +} // namespace HiCR::backend::pthreads \ No newline at end of file From e350b5bad0475fcf03d8eef5ca0da74489a563fb Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Thu, 9 Oct 2025 09:46:37 +0200 Subject: [PATCH 3/7] examples: add createInstance example --- .../createInstance/include/createInstance.hpp | 16 ++++++ examples/createInstance/meson.build | 12 +++++ examples/createInstance/source/pthreads.cpp | 51 +++++++++++++++++++ examples/meson.build | 1 + 4 files changed, 80 insertions(+) create mode 100644 examples/createInstance/include/createInstance.hpp create mode 100644 examples/createInstance/meson.build create mode 100644 examples/createInstance/source/pthreads.cpp diff --git a/examples/createInstance/include/createInstance.hpp b/examples/createInstance/include/createInstance.hpp new file mode 100644 index 00000000..c303867a --- /dev/null +++ b/examples/createInstance/include/createInstance.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +void createInstances(HiCR::InstanceManager &im, size_t instanceCount, HiCR::Topology &t) +{ + auto instanceTemplate = im.createInstanceTemplate(t); + + for (size_t i = 0; i < instanceCount; i++) + { + auto instance = im.createInstance(*instanceTemplate); + printf("[Instance %lu] Create instance %lu\n", im.getCurrentInstance()->getId(), instance->getId()); + } +} \ No newline at end of file diff --git a/examples/createInstance/meson.build b/examples/createInstance/meson.build new file mode 100644 index 00000000..cfb0b74b --- /dev/null +++ b/examples/createInstance/meson.build @@ -0,0 +1,12 @@ +testSuite = [ 'examples', 'createInstance', 'local' ] +test_timeout = 60 + +includeDirs = include_directories(['include']) + +if 'hwloc' in enabledBackends and 'pthreads' in enabledBackends + pthreads = executable('pthreads', [ 'source/pthreads.cpp'], include_directories: includeDirs, dependencies: hicrBuildDep ) + + if get_option('buildTests') + test('pthreads', pthreads, args : [ '10' ], timeout: test_timeout, suite: testSuite ) + endif +endif \ No newline at end of file diff --git a/examples/createInstance/source/pthreads.cpp b/examples/createInstance/source/pthreads.cpp new file mode 100644 index 00000000..69c7c6db --- /dev/null +++ b/examples/createInstance/source/pthreads.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include +#include + +#include "../include/createInstance.hpp" + +int main(int argc, char const *argv[]) +{ + // Check argvs + if (argc != 2) { HICR_THROW_RUNTIME("Pass the instance count as argument"); } + + // Get instance count + auto instanceCount = std::atoi(argv[1]); + + // Determine the root instance id + HiCR::Instance::instanceId_t rootInstanceId = pthread_self(); + + // Create barrier + pthread_barrier_t barrier{}; + pthread_barrier_init(&barrier, nullptr, instanceCount); + + // Declare entrypoint + auto entrypoint = [&](HiCR::backend::pthreads::InstanceManager *creatorIm) { + auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, creatorIm->getEntrypoint()); + printf("[Instance %lu] Hello World\n", im.getCurrentInstance()->getId()); + pthread_barrier_wait(&barrier); + printf("[Instance %lu] fininshing execution\n", im.getCurrentInstance()->getId()); + }; + + // Create instance manager + auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint); + + // Discover local topology + auto tm = HiCR::backend::hwloc::TopologyManager::createDefault(); + auto t = tm->queryTopology(); + + // Create the new instance + createInstances(im, instanceCount, t); + + // Wait barrier + pthread_barrier_wait(&barrier); + + printf("Terminating execution\n"); + + // Finalize instance manager + im.finalize(); + + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index ca4ba281..511e746d 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,3 +1,4 @@ +subdir('createInstance') subdir('memcpy') subdir('kernel') subdir('topology') From 54ef724ba84e5ed11aaae0afa757556badf54635 Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Fri, 10 Oct 2025 10:06:12 +0200 Subject: [PATCH 4/7] feat: remove from instances when terminateInstance is called --- include/hicr/core/instanceManager.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/hicr/core/instanceManager.hpp b/include/hicr/core/instanceManager.hpp index 0e73376a..101c71d6 100644 --- a/include/hicr/core/instanceManager.hpp +++ b/include/hicr/core/instanceManager.hpp @@ -119,6 +119,9 @@ class InstanceManager { // Requesting the terminating of the instance to the specific backend terminateInstanceImpl(instance); + + // Remove from internal list + _instances.erase(std::remove_if(_instances.begin(), _instances.end(), [&](std::shared_ptr &i) { return i->getId() == instance->getId(); }), _instances.end()); } /** @@ -172,7 +175,7 @@ class InstanceManager */ virtual std::shared_ptr addInstanceImpl(HiCR::Instance::instanceId_t instanceId) { - HICR_THROW_LOGIC("The Host backend does not currently support the detection of new instances during runtime"); + HICR_THROW_LOGIC("This backend does not currently support the detection of new instances during runtime"); } /** @@ -181,7 +184,7 @@ class InstanceManager */ virtual void terminateInstanceImpl(const std::shared_ptr instance) { - HICR_THROW_LOGIC("The Host backend does not currently support the termination of instances during runtime"); + HICR_THROW_LOGIC("This backend does not currently support the termination of instances during runtime"); } protected: From e0c502e7ab78a4a7586e1a9a7f2f152c892506fb Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Fri, 10 Oct 2025 10:07:18 +0200 Subject: [PATCH 5/7] feat: add detection mechanism for pthreads --- examples/createInstance/meson.build | 2 +- examples/createInstance/source/pthreads.cpp | 59 ++++-- .../backends/pthreads/instanceManager.hpp | 87 +++++++- .../hicr/backends/pthreads/instancePool.hpp | 198 ++++++++++++++++++ 4 files changed, 315 insertions(+), 31 deletions(-) create mode 100644 include/hicr/backends/pthreads/instancePool.hpp diff --git a/examples/createInstance/meson.build b/examples/createInstance/meson.build index cfb0b74b..c57ddc69 100644 --- a/examples/createInstance/meson.build +++ b/examples/createInstance/meson.build @@ -7,6 +7,6 @@ if 'hwloc' in enabledBackends and 'pthreads' in enabledBackends pthreads = executable('pthreads', [ 'source/pthreads.cpp'], include_directories: includeDirs, dependencies: hicrBuildDep ) if get_option('buildTests') - test('pthreads', pthreads, args : [ '10' ], timeout: test_timeout, suite: testSuite ) + test('pthreads', pthreads, args : [ '2', '10' ], timeout: test_timeout, suite: testSuite ) endif endif \ No newline at end of file diff --git a/examples/createInstance/source/pthreads.cpp b/examples/createInstance/source/pthreads.cpp index 69c7c6db..9c89ba4c 100644 --- a/examples/createInstance/source/pthreads.cpp +++ b/examples/createInstance/source/pthreads.cpp @@ -1,51 +1,70 @@ #include #include +#include #include #include +#include #include "../include/createInstance.hpp" int main(int argc, char const *argv[]) { // Check argvs - if (argc != 2) { HICR_THROW_RUNTIME("Pass the instance count as argument"); } + if (argc != 3) { HICR_THROW_RUNTIME("Pass the instance count as argument"); } // Get instance count - auto instanceCount = std::atoi(argv[1]); + size_t instanceCount = std::atoi(argv[1]); + size_t instanceToCreate = std::atoi(argv[2]); // Determine the root instance id HiCR::Instance::instanceId_t rootInstanceId = pthread_self(); - // Create barrier - pthread_barrier_t barrier{}; - pthread_barrier_init(&barrier, nullptr, instanceCount); + // Create Instance pool + HiCR::backend::pthreads::InstancePool instancePool(0); // Declare entrypoint auto entrypoint = [&](HiCR::backend::pthreads::InstanceManager *creatorIm) { - auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, creatorIm->getEntrypoint()); + auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, creatorIm->getEntrypoint(), instancePool); printf("[Instance %lu] Hello World\n", im.getCurrentInstance()->getId()); - pthread_barrier_wait(&barrier); - printf("[Instance %lu] fininshing execution\n", im.getCurrentInstance()->getId()); }; - // Create instance manager - auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint); + // Define initial threads function + auto workload = [&]() { + // Create instance manager + auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint, instancePool); - // Discover local topology - auto tm = HiCR::backend::hwloc::TopologyManager::createDefault(); - auto t = tm->queryTopology(); + // Detect already started instances + im.detectInstances(instanceCount); - // Create the new instance - createInstances(im, instanceCount, t); + // Discover local topology + auto tm = HiCR::backend::hwloc::TopologyManager::createDefault(); + auto t = tm->queryTopology(); - // Wait barrier - pthread_barrier_wait(&barrier); + // Create the new instance + createInstances(im, instanceToCreate, t); - printf("Terminating execution\n"); + // Finalize instance manager + im.finalize(); + }; + + std::vector> initialThreads; + for (size_t i = 0; i < instanceCount - 1; i++) + { + // Create thread running the workload + auto thread = std::make_unique(workload); + + // Add to the vector of initial threads + initialThreads.push_back(std::move(thread)); + } - // Finalize instance manager - im.finalize(); + // Run the workload + workload(); + + // Wait for all the threads to join + for (auto &thread : initialThreads) { thread->join(); } + + printf("Terminating execution\n"); return 0; } diff --git a/include/hicr/backends/pthreads/instanceManager.hpp b/include/hicr/backends/pthreads/instanceManager.hpp index 66dfee4a..e1c9abfb 100644 --- a/include/hicr/backends/pthreads/instanceManager.hpp +++ b/include/hicr/backends/pthreads/instanceManager.hpp @@ -5,6 +5,7 @@ #include #include "instance.hpp" +#include "instancePool.hpp" namespace HiCR::backend::pthreads { @@ -16,36 +17,99 @@ class InstanceManager; */ typedef std::function entryPoint_t; -class InstanceManager : public HiCR::InstanceManager +class InstanceManager final : public HiCR::InstanceManager { public: - InstanceManager(Instance::instanceId_t rootInstanceId, entryPoint_t entrypoint) + InstanceManager(Instance::instanceId_t rootInstanceId, entryPoint_t entrypoint, InstancePool &instancePool) : HiCR::InstanceManager(), _rootInstanceId(rootInstanceId), - _entrypoint(entrypoint) + _entrypoint(entrypoint), + _instancePool(instancePool) { - setCurrentInstance(std::make_shared(pthread_self(), rootInstanceId)); + auto currentInstance = std::make_shared(pthread_self(), _rootInstanceId); + setCurrentInstance(currentInstance); } - ~InstanceManager() = default; + ~InstanceManager() override = default; + + void detectInstances(size_t initialInstanceCount) + { + // Get current instance + auto currentInstance = getCurrentInstance(); + + // Lock the pool + _instancePool.lock(); + + // Add the instance to the pool + _instancePool.insertInstance(currentInstance); + + // Update the barrier + _instancePool.updateBarrier(initialInstanceCount); + + // Unlock the pool + _instancePool.unlock(); + + // Wait for all the threads to add their own instance + _instancePool.barrier(); + + // Add all the instances + for (auto &i : _instancePool.getInstances()) + { + // Skip current instance + if (i->getId() != currentInstance->getId()) { addInstance(i); } + } + } std::shared_ptr createInstanceImpl(const HiCR::InstanceTemplate instanceTemplate) override { - Instance::instanceId_t newInstanceId; - auto status = pthread_create(&newInstanceId, nullptr, launchWrapper, this); + // Storage for the new instance id + pthread_t newInstanceId; + + // Launch a new pthread executing the entrypoint + auto status = pthread_create(&newInstanceId, nullptr, launchWrapper, this); if (status != 0) { HICR_THROW_RUNTIME("Could not create instance thread. Error: %d", status); } - return std::make_shared(newInstanceId, _rootInstanceId); + + // Add to the pool of created pthreads + _createdThreads.insert(newInstanceId); + + // Create a new HiCR instance + auto instance = std::make_shared(newInstanceId, _rootInstanceId); + + // Lock the pool + _instancePool.lock(); + + // Add the new instance + _instancePool.insertInstance(instance); + + // Update the barrier + _instancePool.updateBarrier(_instancePool.getInstances().size()); + + // Unlock + _instancePool.unlock(); + + return instance; } std::shared_ptr addInstanceImpl(Instance::instanceId_t instanceId) override { return std::make_shared(instanceId, _rootInstanceId); } void terminateInstanceImpl(const std::shared_ptr instance) override { - HICR_THROW_LOGIC("The Host backend does not currently support the termination of instances during runtime"); + // Lock the pool + _instancePool.lock(); + + // Delete instance + _instancePool.deleteInstance(instance); + + // Unlock + _instancePool.unlock(); + } + + void finalize() override + { + for (auto threadId : _createdThreads) { pthread_join(threadId, nullptr); } } - void finalize() override {} void abort(int errorCode) override { exit(errorCode); } HiCR::Instance::instanceId_t getRootInstanceId() const override { return _rootInstanceId; } @@ -63,5 +127,8 @@ class InstanceManager : public HiCR::InstanceManager HiCR::Instance::instanceId_t _rootInstanceId; entryPoint_t _entrypoint; + InstancePool &_instancePool; + + std::unordered_set _createdThreads; }; } // namespace HiCR::backend::pthreads \ No newline at end of file diff --git a/include/hicr/backends/pthreads/instancePool.hpp b/include/hicr/backends/pthreads/instancePool.hpp new file mode 100644 index 00000000..a49ca78f --- /dev/null +++ b/include/hicr/backends/pthreads/instancePool.hpp @@ -0,0 +1,198 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * 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 + * + * http://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. + */ + +/** + * @file instancePool.hpp + * @brief This file implements the pool that holds currently alive pthreads instances + * @author L. Terracciano + * @date 30/9/2025 + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace HiCR::backend::pthreads +{ + +/** + * Implementation of the Pthreads instance pool space to exchange global memory slots among HiCR instances. + * It holds a shared space among threads involved in the communication where one can exchange, retrieve, and destroy global memory slots + * It can be created only by \ref InstancePoolFactory + * + * This backend uses pthread-based mutexes and barriers to prevent concurrent access violations + */ +class InstancePool +{ + // /** + // * The factory is a friend class that can call the constructor + // */ + // friend class InstancePoolFactory; + + /** + * Identifier for instance pool +*/ + using instancePoolId_t = uint64_t; + + public: + + /** + * Private constructor. Can be called only by \ref InstancePoolFactory + * + * \param[in] id Identifier for the instance of instance pool + */ + InstancePool(const instancePoolId_t id) + : _id(id) + { + // Init mutex + pthread_mutex_init(&_mutex, nullptr); + + // Set barrier count to 1. There is at least one instance in the system + _barrierCount = 1; + + // Init barrier with new barrier count + pthread_barrier_init(&_barrier, nullptr, _barrierCount); + } + + ~InstancePool() + { + // Destroy mutex + pthread_mutex_destroy(&_mutex); + + // Destroy barrier + pthread_barrier_destroy(&_barrier); + } + + // Disable object copy + InstancePool(const InstancePool &) = delete; + InstancePool &operator=(const InstancePool &) = delete; + InstancePool(InstancePool &&) = delete; + InstancePool &operator=(InstancePool &&) = delete; + + /** + * Add a new instance to the pool + * + * \param[in] instance instance to add + * + * \note this call is not thread-safe + */ + __INLINE__ void insertInstance(std::shared_ptr instance) { _instances.push_back(instance); } + + /** + * Remove an instance from the pool + * + * \param[in] instance instance to remove + * + * \note this call is not thread-safe + */ + __INLINE__ void deleteInstance(std::shared_ptr instance) + { + _instances.erase(std::remove_if(_instances.begin(), _instances.end(), [&](std::shared_ptr &i) { return i->getId() == instance->getId(); }), _instances.end()); + } + + /** + * Get instances in the pool + * + * \return a copy of the instances vector + * + * \note this call is not thread-safe + */ + __INLINE__ const InstanceManager::instanceList_t getInstances() const { return _instances; } + + /** + * Update the barrier with a new count. It destroys and creates a new barrier, only + * if \ref barrierCount is different than the current one. Otherwise the current barrier is kept. + * + * \param[in] barrierCount the new count to be used for the barrier + * + * \note this call is not thread-safe + */ + __INLINE__ void updateBarrier(size_t barrierCount) + { + // Fail if the barrier count does not involve any thread + if (barrierCount < 0) { HICR_THROW_RUNTIME("Can not have a barrier with barrier count %lu", barrierCount); } + + // Do nothing if the new barrier count coincides with the current one + if (_barrierCount == barrierCount) { return; } + + // Update barrier count + _barrierCount = barrierCount; + + // Destroy old barrier + pthread_barrier_destroy(&_barrier); + + // Init barrier with new barrierCount + pthread_barrier_init(&_barrier, nullptr, _barrierCount); + } + + /** + * Lock the instance pool + */ + __INLINE__ void lock() { pthread_mutex_lock(&_mutex); } + + /** + * Unlock the instance pool + */ + __INLINE__ void unlock() { pthread_mutex_unlock(&_mutex); } + + /** + * A barrier that synchronizes all threads in the HiCR instance + */ + __INLINE__ void barrier() { pthread_barrier_wait(&_barrier); } + + /** + * Id getter + * + * \return Identifier of the instance pool instance + */ + __INLINE__ instancePoolId_t getId() const { return _id; } + + private: + + /** + * Instance Pool ID + */ + const instancePoolId_t _id; + + /** + * Barrier to synchronize all the HiCR instances in the pool + */ + pthread_barrier_t _barrier{}; + + /** + * Mutex to enable thread safety in the class. + * Mutability allows const getter functions to lock the mutex, because this does not modify the logical + * state of the instance pool + */ + mutable pthread_mutex_t _mutex{}; + + /** + * Barrier count + */ + size_t _barrierCount; + + /** + * List of instances in the pool + */ + InstanceManager::instanceList_t _instances; +}; +} // namespace HiCR::backend::pthreads From 6c2dd3c60778affb2bb74c607c886337190baefa Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Fri, 10 Oct 2025 10:07:35 +0200 Subject: [PATCH 6/7] docs: improve comments on pthreads shared memory class --- include/hicr/backends/pthreads/sharedMemory.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hicr/backends/pthreads/sharedMemory.hpp b/include/hicr/backends/pthreads/sharedMemory.hpp index d59751a7..adb428ff 100644 --- a/include/hicr/backends/pthreads/sharedMemory.hpp +++ b/include/hicr/backends/pthreads/sharedMemory.hpp @@ -65,6 +65,7 @@ class SharedMemory pthread_mutex_destroy(&_mutex); } + // Disable object copy SharedMemory(const SharedMemory &) = delete; SharedMemory &operator=(const SharedMemory &) = delete; SharedMemory(SharedMemory &&) = delete; @@ -214,7 +215,7 @@ class SharedMemory pthread_barrier_t _barrier{}; /** - * A mutex to make sure threads do not bother each other during certain operations. + * Mutex to enable thread safety in the class. * Mutability allows const getter functions to lock the mutex, because this does not modify the logical * state of the shared memory */ From 81c5c231a4970971229cb6f831c4c4bc0a12b3ad Mon Sep 17 00:00:00 2001 From: Luca Terracciano Date: Fri, 10 Oct 2025 10:50:56 +0200 Subject: [PATCH 7/7] feat: pthread instance manager supports only creation and not detection of new instances --- .../createInstance/include/createInstance.hpp | 24 ++- examples/createInstance/meson.build | 2 +- examples/createInstance/source/pthreads.cpp | 58 ++--- include/hicr/backends/pthreads/instance.hpp | 39 +++- .../backends/pthreads/instanceManager.hpp | 168 +++++++++------ .../hicr/backends/pthreads/instancePool.hpp | 198 ------------------ 6 files changed, 178 insertions(+), 311 deletions(-) delete mode 100644 include/hicr/backends/pthreads/instancePool.hpp diff --git a/examples/createInstance/include/createInstance.hpp b/examples/createInstance/include/createInstance.hpp index c303867a..36670aa4 100644 --- a/examples/createInstance/include/createInstance.hpp +++ b/examples/createInstance/include/createInstance.hpp @@ -4,13 +4,27 @@ #include #include -void createInstances(HiCR::InstanceManager &im, size_t instanceCount, HiCR::Topology &t) +/** + * Create new HiCR instances + * + * \param[in] instanceManager + * \param[in] instanceCount + * \param[in] topology +*/ +void createInstances(HiCR::InstanceManager &instanceManager, size_t instanceCount, HiCR::Topology &topology) { - auto instanceTemplate = im.createInstanceTemplate(t); + auto instanceTemplate = instanceManager.createInstanceTemplate(topology); for (size_t i = 0; i < instanceCount; i++) { - auto instance = im.createInstance(*instanceTemplate); - printf("[Instance %lu] Create instance %lu\n", im.getCurrentInstance()->getId(), instance->getId()); + auto instance = instanceManager.createInstance(*instanceTemplate); + printf("[Instance %lu] Create instance %lu\n", instanceManager.getCurrentInstance()->getId(), instance->getId()); } -} \ No newline at end of file +} + +/** + * Function that all the created instances should execute + * + * \param[in] instanceManager +*/ +void workerFc(HiCR::InstanceManager &instanceManager) { printf("[Instance %lu] Hello World\n", instanceManager.getCurrentInstance()->getId()); } \ No newline at end of file diff --git a/examples/createInstance/meson.build b/examples/createInstance/meson.build index c57ddc69..cfb0b74b 100644 --- a/examples/createInstance/meson.build +++ b/examples/createInstance/meson.build @@ -7,6 +7,6 @@ if 'hwloc' in enabledBackends and 'pthreads' in enabledBackends pthreads = executable('pthreads', [ 'source/pthreads.cpp'], include_directories: includeDirs, dependencies: hicrBuildDep ) if get_option('buildTests') - test('pthreads', pthreads, args : [ '2', '10' ], timeout: test_timeout, suite: testSuite ) + test('pthreads', pthreads, args : [ '10' ], timeout: test_timeout, suite: testSuite ) endif endif \ No newline at end of file diff --git a/examples/createInstance/source/pthreads.cpp b/examples/createInstance/source/pthreads.cpp index 9c89ba4c..ec2b4f76 100644 --- a/examples/createInstance/source/pthreads.cpp +++ b/examples/createInstance/source/pthreads.cpp @@ -4,65 +4,47 @@ #include #include -#include #include "../include/createInstance.hpp" int main(int argc, char const *argv[]) { // Check argvs - if (argc != 3) { HICR_THROW_RUNTIME("Pass the instance count as argument"); } + if (argc != 2) { HICR_THROW_RUNTIME("Pass the number of instances to create as argument"); } // Get instance count - size_t instanceCount = std::atoi(argv[1]); - size_t instanceToCreate = std::atoi(argv[2]); + size_t instancesToCreate = std::atoi(argv[1]); // Determine the root instance id HiCR::Instance::instanceId_t rootInstanceId = pthread_self(); - // Create Instance pool - HiCR::backend::pthreads::InstancePool instancePool(0); - // Declare entrypoint - auto entrypoint = [&](HiCR::backend::pthreads::InstanceManager *creatorIm) { - auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, creatorIm->getEntrypoint(), instancePool); - printf("[Instance %lu] Hello World\n", im.getCurrentInstance()->getId()); - }; - - // Define initial threads function - auto workload = [&]() { - // Create instance manager - auto im = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint, instancePool); - - // Detect already started instances - im.detectInstances(instanceCount); + auto entrypoint = [&](HiCR::InstanceManager *parentInstanceManager) { + // Cast to pthread instance manager + auto p = dynamic_cast(parentInstanceManager); - // Discover local topology - auto tm = HiCR::backend::hwloc::TopologyManager::createDefault(); - auto t = tm->queryTopology(); + // Fail if the casting is not successful + if (p == nullptr) { HICR_THROW_RUNTIME("Can not cast instance manager to a pthread-specific one"); } - // Create the new instance - createInstances(im, instanceToCreate, t); + // Create instance manager + auto createdInstanceManager = HiCR::backend::pthreads::InstanceManager(rootInstanceId, p->getEntrypoint()); - // Finalize instance manager - im.finalize(); + // Run worker function + workerFc(createdInstanceManager); }; - std::vector> initialThreads; - for (size_t i = 0; i < instanceCount - 1; i++) - { - // Create thread running the workload - auto thread = std::make_unique(workload); + // Create instance manager + auto instanceManager = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint); - // Add to the vector of initial threads - initialThreads.push_back(std::move(thread)); - } + // Discover local topology + auto topologyManager = HiCR::backend::hwloc::TopologyManager::createDefault(); + auto topology = topologyManager->queryTopology(); - // Run the workload - workload(); + // Create the new instance + createInstances(instanceManager, instancesToCreate, topology); - // Wait for all the threads to join - for (auto &thread : initialThreads) { thread->join(); } + // Finalize instance manager + instanceManager.finalize(); printf("Terminating execution\n"); diff --git a/include/hicr/backends/pthreads/instance.hpp b/include/hicr/backends/pthreads/instance.hpp index e868bb7e..3c7d4981 100644 --- a/include/hicr/backends/pthreads/instance.hpp +++ b/include/hicr/backends/pthreads/instance.hpp @@ -1,3 +1,26 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * 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 + * + * http://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. + */ + +/** + * @file instance.hpp + * @brief This file implements the instance class for the Pthreads backend + * @author L. Terracciano + * @date 10/10/2025 + */ + #pragma once #include @@ -7,12 +30,21 @@ namespace HiCR::backend::pthreads { +/** + * Implementation of the HiCR Instance +*/ class Instance : public HiCR::Instance { public: - Instance(instanceId_t currentInstanceId, instanceId_t rootInstanceId) - : HiCR::Instance(currentInstanceId), + /** + * Constructor + * + * \param[in] instanceId the id of the instance + * \param[in] rootInstanceId the id of root + */ + Instance(instanceId_t instanceId, instanceId_t rootInstanceId) + : HiCR::Instance(instanceId), _rootInstanceId(rootInstanceId){}; ~Instance() = default; @@ -21,6 +53,9 @@ class Instance : public HiCR::Instance private: + /** + * Id of HiCR root instance + */ instanceId_t _rootInstanceId; }; } // namespace HiCR::backend::pthreads \ No newline at end of file diff --git a/include/hicr/backends/pthreads/instanceManager.hpp b/include/hicr/backends/pthreads/instanceManager.hpp index e1c9abfb..c27a7c46 100644 --- a/include/hicr/backends/pthreads/instanceManager.hpp +++ b/include/hicr/backends/pthreads/instanceManager.hpp @@ -1,3 +1,26 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * 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 + * + * http://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. + */ + +/** + * @file instanceManager.hpp + * @brief This file implements the instance manager class for the Pthreads backend + * @author L. Terracciano + * @date 10/10/2025 + */ + #pragma once #include @@ -5,62 +28,46 @@ #include #include "instance.hpp" -#include "instancePool.hpp" namespace HiCR::backend::pthreads { -class InstanceManager; - /** - * Type for any entrypoint function - */ + * Type for the instance entrypoint function + */ typedef std::function entryPoint_t; +/** + * Implementation of HiCR InstanceManager class. It creates new HiCR Instance using pthreads +*/ class InstanceManager final : public HiCR::InstanceManager { public: - InstanceManager(Instance::instanceId_t rootInstanceId, entryPoint_t entrypoint, InstancePool &instancePool) + /** + * Constructor + * + * \param[in] rootInstanceId Id of HiCR root Instance + * \param[in] entrypoint function executed by the Instances when created + */ + InstanceManager(Instance::instanceId_t rootInstanceId, entryPoint_t entrypoint) : HiCR::InstanceManager(), _rootInstanceId(rootInstanceId), - _entrypoint(entrypoint), - _instancePool(instancePool) + _entrypoint(entrypoint) { - auto currentInstance = std::make_shared(pthread_self(), _rootInstanceId); - setCurrentInstance(currentInstance); + // Create and set current instance in the base class + setCurrentInstance(std::make_shared(pthread_self(), _rootInstanceId)); } ~InstanceManager() override = default; - void detectInstances(size_t initialInstanceCount) - { - // Get current instance - auto currentInstance = getCurrentInstance(); - - // Lock the pool - _instancePool.lock(); - - // Add the instance to the pool - _instancePool.insertInstance(currentInstance); - - // Update the barrier - _instancePool.updateBarrier(initialInstanceCount); - - // Unlock the pool - _instancePool.unlock(); - - // Wait for all the threads to add their own instance - _instancePool.barrier(); - - // Add all the instances - for (auto &i : _instancePool.getInstances()) - { - // Skip current instance - if (i->getId() != currentInstance->getId()) { addInstance(i); } - } - } - + /** + * Create a new instance inside a pthread + * + * \param[in] instanceTemplate instance template used to create the instance + * + * \return a HiCR instance + */ std::shared_ptr createInstanceImpl(const HiCR::InstanceTemplate instanceTemplate) override { // Storage for the new instance id @@ -74,61 +81,88 @@ class InstanceManager final : public HiCR::InstanceManager _createdThreads.insert(newInstanceId); // Create a new HiCR instance - auto instance = std::make_shared(newInstanceId, _rootInstanceId); - - // Lock the pool - _instancePool.lock(); - - // Add the new instance - _instancePool.insertInstance(instance); - - // Update the barrier - _instancePool.updateBarrier(_instancePool.getInstances().size()); - - // Unlock - _instancePool.unlock(); - - return instance; + return std::make_shared(newInstanceId, _rootInstanceId); } + /** + * Add an instance. + * + * \param[in] instanceId Id of the instance + * + * \return a HiCR instance + */ std::shared_ptr addInstanceImpl(Instance::instanceId_t instanceId) override { return std::make_shared(instanceId, _rootInstanceId); } + /** + * Terminate an instance. Nothing to do other than waiting for the pthread to finish + * + * \param[in] instance instance to terminate + */ void terminateInstanceImpl(const std::shared_ptr instance) override { - // Lock the pool - _instancePool.lock(); - - // Delete instance - _instancePool.deleteInstance(instance); - - // Unlock - _instancePool.unlock(); + // Nothing to do here } + /** + * Wait for all created threads to finalize + */ void finalize() override { - for (auto threadId : _createdThreads) { pthread_join(threadId, nullptr); } + for (auto thread : _createdThreads) { pthread_join(thread, nullptr); } } + /** + * Abort execution + * + * \param[in] errorCode exit code + */ void abort(int errorCode) override { exit(errorCode); } + /** + * Getter for root instance id + * + * \return root instance id + */ HiCR::Instance::instanceId_t getRootInstanceId() const override { return _rootInstanceId; } + /** + * Getter for the entrypoint. Useful if the intention is to + * propagate the same entrypoint across instance managers + * + * \return the entrypoint function + */ entryPoint_t getEntrypoint() const { return _entrypoint; } private: - __INLINE__ static void *launchWrapper(void *im) + /** + * Wrapper to launch the entrypoint of the new instance + * + * \param[in] parentInstanceManager instance manager of the creator instance + */ + __INLINE__ static void *launchWrapper(void *parentInstanceManager) { - auto instanceManager = static_cast(im); - instanceManager->_entrypoint(instanceManager); + // Cast to a Pthread InstanceManager + auto p = static_cast(parentInstanceManager); + + // Run the entrypoint + p->_entrypoint(p); return 0; } + /** + * Id of the HiCR root Instance + */ HiCR::Instance::instanceId_t _rootInstanceId; - entryPoint_t _entrypoint; - InstancePool &_instancePool; + /** + * Function that each newly created instance runs + */ + entryPoint_t _entrypoint; + + /** + * Pool of threads created by the Instance Manager + */ std::unordered_set _createdThreads; }; } // namespace HiCR::backend::pthreads \ No newline at end of file diff --git a/include/hicr/backends/pthreads/instancePool.hpp b/include/hicr/backends/pthreads/instancePool.hpp deleted file mode 100644 index a49ca78f..00000000 --- a/include/hicr/backends/pthreads/instancePool.hpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2025 Huawei Technologies Co., Ltd. - * - * 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 - * - * http://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. - */ - -/** - * @file instancePool.hpp - * @brief This file implements the pool that holds currently alive pthreads instances - * @author L. Terracciano - * @date 30/9/2025 - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace HiCR::backend::pthreads -{ - -/** - * Implementation of the Pthreads instance pool space to exchange global memory slots among HiCR instances. - * It holds a shared space among threads involved in the communication where one can exchange, retrieve, and destroy global memory slots - * It can be created only by \ref InstancePoolFactory - * - * This backend uses pthread-based mutexes and barriers to prevent concurrent access violations - */ -class InstancePool -{ - // /** - // * The factory is a friend class that can call the constructor - // */ - // friend class InstancePoolFactory; - - /** - * Identifier for instance pool -*/ - using instancePoolId_t = uint64_t; - - public: - - /** - * Private constructor. Can be called only by \ref InstancePoolFactory - * - * \param[in] id Identifier for the instance of instance pool - */ - InstancePool(const instancePoolId_t id) - : _id(id) - { - // Init mutex - pthread_mutex_init(&_mutex, nullptr); - - // Set barrier count to 1. There is at least one instance in the system - _barrierCount = 1; - - // Init barrier with new barrier count - pthread_barrier_init(&_barrier, nullptr, _barrierCount); - } - - ~InstancePool() - { - // Destroy mutex - pthread_mutex_destroy(&_mutex); - - // Destroy barrier - pthread_barrier_destroy(&_barrier); - } - - // Disable object copy - InstancePool(const InstancePool &) = delete; - InstancePool &operator=(const InstancePool &) = delete; - InstancePool(InstancePool &&) = delete; - InstancePool &operator=(InstancePool &&) = delete; - - /** - * Add a new instance to the pool - * - * \param[in] instance instance to add - * - * \note this call is not thread-safe - */ - __INLINE__ void insertInstance(std::shared_ptr instance) { _instances.push_back(instance); } - - /** - * Remove an instance from the pool - * - * \param[in] instance instance to remove - * - * \note this call is not thread-safe - */ - __INLINE__ void deleteInstance(std::shared_ptr instance) - { - _instances.erase(std::remove_if(_instances.begin(), _instances.end(), [&](std::shared_ptr &i) { return i->getId() == instance->getId(); }), _instances.end()); - } - - /** - * Get instances in the pool - * - * \return a copy of the instances vector - * - * \note this call is not thread-safe - */ - __INLINE__ const InstanceManager::instanceList_t getInstances() const { return _instances; } - - /** - * Update the barrier with a new count. It destroys and creates a new barrier, only - * if \ref barrierCount is different than the current one. Otherwise the current barrier is kept. - * - * \param[in] barrierCount the new count to be used for the barrier - * - * \note this call is not thread-safe - */ - __INLINE__ void updateBarrier(size_t barrierCount) - { - // Fail if the barrier count does not involve any thread - if (barrierCount < 0) { HICR_THROW_RUNTIME("Can not have a barrier with barrier count %lu", barrierCount); } - - // Do nothing if the new barrier count coincides with the current one - if (_barrierCount == barrierCount) { return; } - - // Update barrier count - _barrierCount = barrierCount; - - // Destroy old barrier - pthread_barrier_destroy(&_barrier); - - // Init barrier with new barrierCount - pthread_barrier_init(&_barrier, nullptr, _barrierCount); - } - - /** - * Lock the instance pool - */ - __INLINE__ void lock() { pthread_mutex_lock(&_mutex); } - - /** - * Unlock the instance pool - */ - __INLINE__ void unlock() { pthread_mutex_unlock(&_mutex); } - - /** - * A barrier that synchronizes all threads in the HiCR instance - */ - __INLINE__ void barrier() { pthread_barrier_wait(&_barrier); } - - /** - * Id getter - * - * \return Identifier of the instance pool instance - */ - __INLINE__ instancePoolId_t getId() const { return _id; } - - private: - - /** - * Instance Pool ID - */ - const instancePoolId_t _id; - - /** - * Barrier to synchronize all the HiCR instances in the pool - */ - pthread_barrier_t _barrier{}; - - /** - * Mutex to enable thread safety in the class. - * Mutability allows const getter functions to lock the mutex, because this does not modify the logical - * state of the instance pool - */ - mutable pthread_mutex_t _mutex{}; - - /** - * Barrier count - */ - size_t _barrierCount; - - /** - * List of instances in the pool - */ - InstanceManager::instanceList_t _instances; -}; -} // namespace HiCR::backend::pthreads