/home/runner/work/HiCR/HiCR/include/hicr/backends/pthreads/processingUnit.hpp Source File

HiCR: /home/runner/work/HiCR/HiCR/include/hicr/backends/pthreads/processingUnit.hpp Source File
HiCR
processingUnit.hpp
Go to the documentation of this file.
1/*
2 * Copyright 2025 Huawei Technologies Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
24#pragma once
25
26#include <csignal>
27#include <set>
28#include <sched.h>
29#include <fcntl.h>
30#include <sys/mman.h>
31#include <sys/stat.h>
32#include <pthread.h>
33#include <hicr/core/definitions.hpp>
39
40#ifndef _GNU_SOURCE
42 #define _GNU_SOURCE
43#endif
44
45namespace HiCR::backend::pthreads
46{
47class ComputeManager;
48}
49
50namespace HiCR::backend::pthreads
51{
52
56#define HICR_SUSPEND_SIGNAL SIGUSR1
57
61#define HICR_RESUME_SIGNAL SIGUSR2
62
63class ProcessingUnit;
64
71{
73
74 public:
75
76 [[nodiscard]] __INLINE__ std::string getType() override { return "POSIX Thread"; }
77
83 __INLINE__ static void updateAffinity(const std::set<hwloc::ComputeResource::logicalProcessorId_t> &affinity)
84 {
85 cpu_set_t cpuset;
86 CPU_ZERO(&cpuset);
87 for (const auto c : affinity) CPU_SET_S(c, sizeof(cpu_set_t), &cpuset);
88
89 // Attempting to use the pthread interface first
90 int status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
91
92 // If failed, attempt to use the sched interface
93 if (status != 0) status = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset);
94
95 // Throw exception if none of them worked
96 if (status != 0) HICR_THROW_RUNTIME("Problem assigning affinity.");
97 }
98
104 __INLINE__ static std::set<HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t> getAffinity()
105 {
106 auto affinity = std::set<HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t>();
107 cpu_set_t cpuset;
108
109 // Attempting to use the pthread interface first
110 int status = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
111
112 // If failed, attempt to use the sched interface
113 if (status != 0) status = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset);
114
115 // Throw exception if none of them worked
116 if (status != 0) HICR_THROW_RUNTIME("Problem obtaining affinity.");
117 for (int i = 0; i < CPU_SETSIZE; i++)
118 if (CPU_ISSET(i, &cpuset)) affinity.insert(i);
119
120 return affinity;
121 }
122
128 __INLINE__ ProcessingUnit(const std::shared_ptr<HiCR::ComputeResource> &computeResource)
129 : HiCR::ProcessingUnit(computeResource)
130 {
131 // Getting up-casted pointer for the processing unit
132 auto c = dynamic_pointer_cast<HiCR::backend::hwloc::ComputeResource>(computeResource);
133
134 // Checking whether the execution unit passed is compatible with this backend
135 if (c == nullptr) HICR_THROW_LOGIC("The passed compute resource is not supported by this processing unit type\n");
136
137 // Creating initialization barrier
138 _initializationBarrier = std::make_unique<pthread_barrier_t>();
139 };
140
141 private:
142
146 pthread_t _pthreadId = 0;
147
151 std::unique_ptr<HiCR::ExecutionState> _executionState;
152
156 std::unique_ptr<pthread_barrier_t> _initializationBarrier;
157
163 __INLINE__ static void *launchWrapper(void *p)
164 {
165 // Gathering thread object
166 auto thread = static_cast<pthreads::ProcessingUnit *>(p);
167
168 // Getting associated compute unit reference
169 auto computeResource = dynamic_pointer_cast<HiCR::backend::hwloc::ComputeResource>(thread->getComputeResource());
170
171 // Setting signal to hear for suspend/resume
172 signal(HICR_SUSPEND_SIGNAL, ProcessingUnit::catchSuspendSignal);
173 signal(HICR_RESUME_SIGNAL, ProcessingUnit::catchResumeSignal);
174
175 // Setting initial thread affinity
176 thread->updateAffinity(std::set<hwloc::ComputeResource::logicalProcessorId_t>({computeResource->getProcessorId()}));
177
178 // Yielding execution to allow affinity to refresh
179 sched_yield();
180
181 // The thread has now been properly initialized
182 pthread_barrier_wait(thread->_initializationBarrier.get());
183
184 // Calling main loop
185 thread->_executionState->resume();
186
187 // No returns
188 return nullptr;
189 }
190
196 __INLINE__ static void catchSuspendSignal(int sig)
197 {
198 int status = 0;
199 int signalSet = 0;
200 sigset_t suspendSet;
201
202 // Waiting for that signal to arrive
203 status = sigaddset(&suspendSet, HICR_RESUME_SIGNAL);
204 if (status != 0) HICR_THROW_RUNTIME("Could not set resume signal thread\n");
205
206 status = sigwait(&suspendSet, &signalSet);
207 if (status != 0) HICR_THROW_RUNTIME("Could not suspend thread\n");
208 }
209
215 __INLINE__ static void catchResumeSignal(int sig) {}
216
217 __INLINE__ void initialize()
218 {
219 // Nothing to do for the initialization
220 }
221
222 __INLINE__ void suspend()
223 {
224 auto status = pthread_kill(_pthreadId, HICR_SUSPEND_SIGNAL);
225 if (status != 0) HICR_THROW_RUNTIME("Could not suspend thread %lu\n", _pthreadId);
226 }
227
228 __INLINE__ void resume()
229 {
230 auto status = pthread_kill(_pthreadId, HICR_RESUME_SIGNAL);
231 if (status != 0) HICR_THROW_RUNTIME("Could not resume thread %lu\n", _pthreadId);
232 }
233
234 __INLINE__ void start(std::unique_ptr<HiCR::ExecutionState> &executionState)
235 {
236 // Initializing barrier
237 pthread_barrier_init(_initializationBarrier.get(), nullptr, 2);
238
239 // Obtaining execution state
240 _executionState = std::move(executionState);
241
242 // Launching thread function wrapper
243 auto status = pthread_create(&_pthreadId, nullptr, launchWrapper, this);
244 if (status != 0) HICR_THROW_RUNTIME("Could not create thread %lu\n", _pthreadId);
245
246 // Waiting for proper initialization of the thread
247 pthread_barrier_wait(_initializationBarrier.get());
248
249 // Destroying barrier
250 pthread_barrier_destroy(_initializationBarrier.get());
251 }
252
253 __INLINE__ void terminate()
254 {
255 // Nothing to do actively, just wait for the thread to finalize in its own accord
256 }
257
258 __INLINE__ void await()
259 {
260 // Waiting for thread after execution
261 pthread_join(_pthreadId, nullptr);
262 }
263};
264
265} // namespace HiCR::backend::pthreads
This file implements the execution state class for the Boost backend.
This file implements the execution unit class for the Boost backend.
This file implements the compute resource class for the HWLoc-based backend.
#define HICR_RESUME_SIGNAL
Definition processingUnit.hpp:61
#define HICR_SUSPEND_SIGNAL
Definition processingUnit.hpp:56
Definition processingUnit.hpp:49
Definition computeManager.hpp:45
Definition processingUnit.hpp:71
static __INLINE__ void updateAffinity(const std::set< hwloc::ComputeResource::logicalProcessorId_t > &affinity)
Definition processingUnit.hpp:83
__INLINE__ std::string getType() override
Definition processingUnit.hpp:76
__INLINE__ ProcessingUnit(const std::shared_ptr< HiCR::ComputeResource > &computeResource)
Definition processingUnit.hpp:128
static __INLINE__ std::set< HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t > getAffinity()
Definition processingUnit.hpp:104
Provides a definition for a HiCR ProcessingUnit class.
Provides a failure model and corresponding exception classes.
#define HICR_THROW_RUNTIME(...)
Definition exceptions.hpp:74
#define HICR_THROW_LOGIC(...)
Definition exceptions.hpp:67