/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 <memory>
31#include <sys/mman.h>
32#include <sys/stat.h>
33#include <pthread.h>
34#include <hicr/core/definitions.hpp>
40
41#ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44#endif
45
46namespace HiCR::backend::pthreads
47{
48class ComputeManager;
49}
50
51namespace HiCR::backend::pthreads
52{
53
57#define HICR_SUSPEND_SIGNAL SIGUSR1
58
62#define HICR_RESUME_SIGNAL SIGUSR2
63
64class ProcessingUnit;
65
72{
74
75 public:
76
77 [[nodiscard]] __INLINE__ std::string getType() override { return "POSIX Thread"; }
78
84 __INLINE__ static void updateAffinity(const std::set<hwloc::ComputeResource::logicalProcessorId_t> &affinity)
85 {
86 cpu_set_t cpuset;
87 CPU_ZERO(&cpuset);
88 for (const auto c : affinity) CPU_SET_S(c, sizeof(cpu_set_t), &cpuset);
89
90 // Attempting to use the pthread interface first
91 int status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
92
93 // If failed, attempt to use the sched interface
94 if (status != 0) status = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset);
95
96 // Throw exception if none of them worked
97 if (status != 0) HICR_THROW_RUNTIME("Problem assigning affinity.");
98 }
99
105 __INLINE__ static std::set<HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t> getAffinity()
106 {
107 auto affinity = std::set<HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t>();
108 cpu_set_t cpuset;
109
110 // Attempting to use the pthread interface first
111 int status = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
112
113 // If failed, attempt to use the sched interface
114 if (status != 0) status = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset);
115
116 // Throw exception if none of them worked
117 if (status != 0) HICR_THROW_RUNTIME("Problem obtaining affinity.");
118 for (int i = 0; i < CPU_SETSIZE; i++)
119 if (CPU_ISSET(i, &cpuset)) affinity.insert(i);
120
121 return affinity;
122 }
123
129 __INLINE__ ProcessingUnit(const std::shared_ptr<HiCR::ComputeResource> &computeResource)
130 : HiCR::ProcessingUnit(computeResource)
131 {
132 // Getting up-casted pointer for the processing unit
133 auto c = std::dynamic_pointer_cast<HiCR::backend::hwloc::ComputeResource>(computeResource);
134
135 // Checking whether the execution unit passed is compatible with this backend
136 if (c == nullptr) HICR_THROW_LOGIC("The passed compute resource is not supported by this processing unit type\n");
137
138 // Creating initialization barrier
139 _initializationBarrier = std::make_unique<pthread_barrier_t>();
140 };
141
142 private:
143
147 pthread_t _pthreadId = 0;
148
152 std::unique_ptr<HiCR::ExecutionState> _executionState;
153
157 std::unique_ptr<pthread_barrier_t> _initializationBarrier;
158
164 __INLINE__ static void *launchWrapper(void *p)
165 {
166 // Gathering thread object
167 auto thread = static_cast<pthreads::ProcessingUnit *>(p);
168
169 // Getting associated compute unit reference
170 auto computeResource = std::dynamic_pointer_cast<HiCR::backend::hwloc::ComputeResource>(thread->getComputeResource());
171
172 // Setting signal to hear for suspend/resume
173 signal(HICR_SUSPEND_SIGNAL, ProcessingUnit::catchSuspendSignal);
174 signal(HICR_RESUME_SIGNAL, ProcessingUnit::catchResumeSignal);
175
176 // Setting initial thread affinity
177 thread->updateAffinity(std::set<hwloc::ComputeResource::logicalProcessorId_t>({computeResource->getProcessorId()}));
178
179 // Yielding execution to allow affinity to refresh
180 sched_yield();
181
182 // The thread has now been properly initialized
183 pthread_barrier_wait(thread->_initializationBarrier.get());
184
185 // Calling main loop
186 thread->_executionState->resume();
187
188 // No returns
189 return nullptr;
190 }
191
197 __INLINE__ static void catchSuspendSignal(int sig)
198 {
199 int status = 0;
200 int signalSet = 0;
201 sigset_t suspendSet;
202
203 // Waiting for that signal to arrive
204 status = sigaddset(&suspendSet, HICR_RESUME_SIGNAL);
205 if (status != 0) HICR_THROW_RUNTIME("Could not set resume signal thread\n");
206
207 status = sigwait(&suspendSet, &signalSet);
208 if (status != 0) HICR_THROW_RUNTIME("Could not suspend thread\n");
209 }
210
216 __INLINE__ static void catchResumeSignal(int sig) {}
217
218 __INLINE__ void initialize()
219 {
220 // Nothing to do for the initialization
221 }
222
223 __INLINE__ void suspend()
224 {
225 auto status = pthread_kill(_pthreadId, HICR_SUSPEND_SIGNAL);
226 if (status != 0) HICR_THROW_RUNTIME("Could not suspend thread %lu\n", _pthreadId);
227 }
228
229 __INLINE__ void resume()
230 {
231 auto status = pthread_kill(_pthreadId, HICR_RESUME_SIGNAL);
232 if (status != 0) HICR_THROW_RUNTIME("Could not resume thread %lu\n", _pthreadId);
233 }
234
235 __INLINE__ void start(std::unique_ptr<HiCR::ExecutionState> &executionState)
236 {
237 // Initializing barrier
238 pthread_barrier_init(_initializationBarrier.get(), nullptr, 2);
239
240 // Obtaining execution state
241 _executionState = std::move(executionState);
242
243 // Launching thread function wrapper
244 auto status = pthread_create(&_pthreadId, nullptr, launchWrapper, this);
245 if (status != 0) HICR_THROW_RUNTIME("Could not create thread %lu\n", _pthreadId);
246
247 // Waiting for proper initialization of the thread
248 pthread_barrier_wait(_initializationBarrier.get());
249
250 // Destroying barrier
251 pthread_barrier_destroy(_initializationBarrier.get());
252 }
253
254 __INLINE__ void terminate()
255 {
256 // Nothing to do actively, just wait for the thread to finalize in its own accord
257 }
258
259 __INLINE__ void await()
260 {
261 // Waiting for thread after execution
262 pthread_join(_pthreadId, nullptr);
263 }
264};
265
266} // 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:62
#define HICR_SUSPEND_SIGNAL
Definition processingUnit.hpp:57
Definition processingUnit.hpp:49
Definition computeManager.hpp:40
Definition processingUnit.hpp:72
static __INLINE__ void updateAffinity(const std::set< hwloc::ComputeResource::logicalProcessorId_t > &affinity)
Definition processingUnit.hpp:84
__INLINE__ std::string getType() override
Definition processingUnit.hpp:77
__INLINE__ ProcessingUnit(const std::shared_ptr< HiCR::ComputeResource > &computeResource)
Definition processingUnit.hpp:129
static __INLINE__ std::set< HiCR::backend::hwloc::ComputeResource::logicalProcessorId_t > getAffinity()
Definition processingUnit.hpp:105
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