diff --git a/examples/createInstance/include/createInstance.hpp b/examples/createInstance/include/createInstance.hpp new file mode 100644 index 00000000..36670aa4 --- /dev/null +++ b/examples/createInstance/include/createInstance.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +/** + * 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 = instanceManager.createInstanceTemplate(topology); + + for (size_t i = 0; i < instanceCount; i++) + { + auto instance = instanceManager.createInstance(*instanceTemplate); + printf("[Instance %lu] Create instance %lu\n", instanceManager.getCurrentInstance()->getId(), instance->getId()); + } +} + +/** + * 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 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..ec2b4f76 --- /dev/null +++ b/examples/createInstance/source/pthreads.cpp @@ -0,0 +1,52 @@ +#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 number of instances to create as argument"); } + + // Get instance count + size_t instancesToCreate = std::atoi(argv[1]); + + // Determine the root instance id + HiCR::Instance::instanceId_t rootInstanceId = pthread_self(); + + // Declare entrypoint + auto entrypoint = [&](HiCR::InstanceManager *parentInstanceManager) { + // Cast to pthread instance manager + auto p = dynamic_cast(parentInstanceManager); + + // Fail if the casting is not successful + if (p == nullptr) { HICR_THROW_RUNTIME("Can not cast instance manager to a pthread-specific one"); } + + // Create instance manager + auto createdInstanceManager = HiCR::backend::pthreads::InstanceManager(rootInstanceId, p->getEntrypoint()); + + // Run worker function + workerFc(createdInstanceManager); + }; + + // Create instance manager + auto instanceManager = HiCR::backend::pthreads::InstanceManager(rootInstanceId, entrypoint); + + // Discover local topology + auto topologyManager = HiCR::backend::hwloc::TopologyManager::createDefault(); + auto topology = topologyManager->queryTopology(); + + // Create the new instance + createInstances(instanceManager, instancesToCreate, topology); + + // Finalize instance manager + instanceManager.finalize(); + + printf("Terminating execution\n"); + + 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') diff --git a/include/hicr/backends/pthreads/instance.hpp b/include/hicr/backends/pthreads/instance.hpp new file mode 100644 index 00000000..3c7d4981 --- /dev/null +++ b/include/hicr/backends/pthreads/instance.hpp @@ -0,0 +1,61 @@ +/* + * 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 +#include +#include + +namespace HiCR::backend::pthreads +{ + +/** + * Implementation of the HiCR Instance +*/ +class Instance : public HiCR::Instance +{ + public: + + /** + * 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; + + bool isRootInstance() const override { return getId() != _rootInstanceId; }; + + 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 new file mode 100644 index 00000000..c27a7c46 --- /dev/null +++ b/include/hicr/backends/pthreads/instanceManager.hpp @@ -0,0 +1,168 @@ +/* + * 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 + +#include + +#include "instance.hpp" + +namespace HiCR::backend::pthreads +{ + +/** + * 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: + + /** + * 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) + { + // Create and set current instance in the base class + setCurrentInstance(std::make_shared(pthread_self(), _rootInstanceId)); + } + + ~InstanceManager() override = default; + + /** + * 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 + 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); } + + // Add to the pool of created pthreads + _createdThreads.insert(newInstanceId); + + // Create a new HiCR 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 + { + // Nothing to do here + } + + /** + * Wait for all created threads to finalize + */ + void finalize() override + { + 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: + + /** + * Wrapper to launch the entrypoint of the new instance + * + * \param[in] parentInstanceManager instance manager of the creator instance + */ + __INLINE__ static void *launchWrapper(void *parentInstanceManager) + { + // 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; + + /** + * 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/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 */ diff --git a/include/hicr/core/instanceManager.hpp b/include/hicr/core/instanceManager.hpp index d203617d..101c71d6 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 */ @@ -129,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()); } /** @@ -182,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"); } /** @@ -191,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: @@ -219,11 +212,6 @@ class InstanceManager * Pointer to current instance */ std::shared_ptr _currentInstance; - - /** - * Map of executable functions, representing potential RPC requests - */ - std::map _RPCTargetMap; }; } // namespace HiCR