ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
TransferGroup.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "TransferGroup.h"
5
7#include "Exception.h"
8#include "TransferElement.h"
10
11#include <iostream>
12
13namespace ChimeraTK {
14
15 /********************************************************************************************************************/
16
17 void TransferGroup::runPostReads(const std::set<boost::shared_ptr<TransferElement>>& elements,
18 const std::exception_ptr& firstDetectedRuntimeError) {
19 for(const auto& elem : elements) {
20 // check for exceptions on any of the element's low level elements
21 for(const auto& lowLevelElem : elem->getHardwareAccessingElements()) {
22 // In case there are multiple exceptions we take the last one, but this does not matter. They are all
23 // ChimeraTK::runtime_errors and the first detected runtime error, which is re-thrown, has already been
24 // determined by previously.
25 if(lowLevelElem->_activeException) {
26 // copy the runtime error from low level element into the high-level element so it is processed in post-read
27 elem->_activeException = lowLevelElem->_activeException;
28 }
29 }
30
31 try {
32 // call with updateDataBuffer = false if there has been any ChimeraTK::runtime_error in the transfer phase,
33 // true otherwise
34 elem->postReadAndHandleExceptions(TransferType::read, firstDetectedRuntimeError == nullptr);
35 }
38 }
39 }
40 }
41
42 /********************************************************************************************************************/
43
45 // reset exception flags
47 it.second = false;
48 }
49
50 std::exception_ptr firstDetectedRuntimeError{nullptr};
51
52 for(const auto& elem : _highLevelElements) {
53 elem->preReadAndHandleExceptions(TransferType::read);
54 if((elem->_activeException != nullptr) && (firstDetectedRuntimeError == nullptr)) {
55 firstDetectedRuntimeError = elem->_activeException;
56 }
57 }
58
59 // Call preRead on the copy decorators so that we can call postRead later with
60 // readTransactionInProgress flag set to true and do the actual copying in the
61 // decorator's doPostRead
62 for(const auto& elem : _copyDecorators) {
63 elem->preReadAndHandleExceptions(TransferType::read);
64 if((elem->_activeException != nullptr) && (firstDetectedRuntimeError == nullptr)) {
65 firstDetectedRuntimeError = elem->_activeException;
66 }
67 }
68
69 if(firstDetectedRuntimeError == nullptr) {
70 // only execute the transfers if there has been no exception yet
71 for(const auto& it : _lowLevelElementsAndExceptionFlags) {
72 const auto& elem = it.first;
73 elem->handleTransferException([&] { elem->readTransfer(); });
74 if((elem->_activeException != nullptr) && (firstDetectedRuntimeError == nullptr)) {
75 firstDetectedRuntimeError = elem->_activeException;
76 }
77 }
78 }
79
80 // Exceptions from copy decorators are ignored. The same exception will be thrown by their target accessors in the
81 // second call to runPostReads() anyway. In case the target is decorated with an ExceptionHandlingDecorator (in
82 // ApplicationCore), the copy decorators exception would not be handled by an ExceptionHandlingDecorator and hence
83 // terminate the application.
85 runPostReads(_copyDecorators, firstDetectedRuntimeError);
87 runPostReads(_highLevelElements, firstDetectedRuntimeError);
88
89 // re-throw exceptions in the order of occurrence
90
91 // The runtime error which was seen as _activeException in the pre or transfer phase might have been handled in
92 // postRead, and thus become invalid (for instance because it is suppressed by the
93 // ApplicationCore::ExceptionHandlingDecorator). Only re-throw here if an exception has been re-thrown in the
94 // postRead step. FIXME(?): If the firstDetectedRuntimeError has been handled, but another runtime_error has been
95 // thrown, this logic will throw the wrong exception here. It could be prevented with a complicated logic that loops
96 // all lowLevelElements in the order that was used to execute the transfer, for each of them it loops all its
97 // associated high- level element, and for each high-level element it loops all the low level elements again because
98 // some might and others might not have exceptions, and if there was an exception for that high-level element on any
99 // of its low level elements, postRead must be called with an exception set each time. (I guess this sentence in
100 // close to not understandable, that's why I am not trying to implement it). In practice this will not happen
101 // because either all elements will have an ExceptionHandlingDecorator, or none.
102 if(_nRuntimeErrors != 0) {
103 assert(firstDetectedRuntimeError != nullptr); // postRead must only rethrow, so there must be a detected
104 // runtime_error
106 std::rethrow_exception(firstDetectedRuntimeError);
107 }
108 } // namespace ChimeraTK
109
110 /********************************************************************************************************************/
111
113 for(auto& it : _lowLevelElementsAndExceptionFlags) {
114 it.second = false;
115 }
116
117 std::exception_ptr firstDetectedRuntimeError{nullptr};
118 for(const auto& elem : _highLevelElements) {
119 elem->preWriteAndHandleExceptions(TransferType::write, versionNumber);
120 if((elem->_activeException != nullptr) && (firstDetectedRuntimeError == nullptr)) {
121 firstDetectedRuntimeError = elem->_activeException;
122 }
123 }
124
125 if(firstDetectedRuntimeError == nullptr) {
126 for(const auto& it : _lowLevelElementsAndExceptionFlags) {
127 const auto& elem = it.first;
128 elem->handleTransferException([&] { elem->writeTransfer(versionNumber); });
129 if((elem->_activeException != nullptr) && (firstDetectedRuntimeError == nullptr)) {
130 firstDetectedRuntimeError = elem->_activeException;
131 }
132 }
133 }
134
135 _nRuntimeErrors = 0;
136 for(const auto& elem : _highLevelElements) {
137 // check for exceptions on any of the element's low level elements
138 for(const auto& lowLevelElem : elem->getHardwareAccessingElements()) {
139 // In case there are multiple exceptions we take the last one, but this does not matter. They are all
140 // ChimeraTK::runtime_errors and the first detected runtime error, which is re-thrown, has already been
141 // determined by previously.
142 if(lowLevelElem->_activeException) {
143 // copy the runtime error from low level element into the high-level element so it is processed in post-read
144 elem->_activeException = lowLevelElem->_activeException;
145 }
146 }
147
148 try {
149 elem->postWrite(TransferType::write, versionNumber);
150 }
153 }
154 }
155
156 if(_nRuntimeErrors != 0) {
157 assert(firstDetectedRuntimeError != nullptr);
159 std::rethrow_exception(firstDetectedRuntimeError);
160 }
161 }
162
163 /********************************************************************************************************************/
164
166 return isReadable() && !isWriteable();
167 }
168
169 /********************************************************************************************************************/
170
177
178 /********************************************************************************************************************/
179
186
187 /********************************************************************************************************************/
188
189 void TransferGroup::addAccessorImpl(TransferElementAbstractor& accessor, bool isTemporary) {
190 // check if accessor is already in a transfer group
191 if(accessor.getHighLevelImplElement()->_isInTransferGroup) {
192 throw ChimeraTK::logic_error("The given accessor is already in a TransferGroup and cannot be added "
193 "to another.");
194 }
195
196 // Only accessors without wait_for_new_data can be used in a transfer group.
199 "A TransferGroup can only be used with transfer elements that don't have aAccessMode::wait_for_new_data.");
200 }
201
202 // set flag on the accessors that it is now in a transfer group
203 accessor.getHighLevelImplElement()->_isInTransferGroup = true;
204
205 // Apply possible replacements by calling replaceTransferElement() in all possible combinations, i.e. pass all
206 // internal elements in this group to replaceTransferElement() called on all high-level elements in the group. Keep
207 // in mind that replaceTransferElement() is supposed to pass the call down to internal implementations, so this will
208 // effectively try all combinations.
209 //
210 // Start with the abstractor, to make sure we replace at the highest level if possible, but only if this isn't a
211 // temporary abstractor. We need to do this in a separate loop first, so we are not using deeper levels inside the
212 // abstractor as a replacement somewhere else after the implementation in the abstractor might have been replaced.
213 if(not isTemporary) {
214 for(const auto& hlElem1 : _highLevelElements) {
215 auto list = hlElem1->getInternalElements();
216 list.push_front(hlElem1);
217
218 for(const auto& replacement : list) {
219 accessor.replaceTransferElement(replacement);
220 }
221 }
222 }
223
224 // Now build a list of all high-level elements to consider, including the implementation in the abstactor which
225 // might have been replaced above.
226 auto highLevelElementsWithNewAccessor = _highLevelElements;
227 highLevelElementsWithNewAccessor.insert(accessor.getHighLevelImplElement());
228
229 // try replacing all internal elements in all high-level elements
230 for(const auto& hlElem1 : highLevelElementsWithNewAccessor) {
231 auto list = hlElem1->getInternalElements();
232 list.push_front(hlElem1);
233
234 for(const auto& replacement : list) {
235 // try on all high-level elements already stored in the list
236 accessor.getHighLevelImplElement()->replaceTransferElement(replacement);
237 for(const auto& hlElem : _highLevelElements) {
238 hlElem->replaceTransferElement(replacement); // note: this does nothing, if the replacement cannot
239 // be used by the hlElem!
240 }
241 }
242 }
243
244 // store the accessor in the list of high-level elements
245 // this must be done only now, since it may have been replaced during the
246 // replacement process
248
249 // update the list of hardware-accessing elements, since we might just have
250 // made some of them redundant since we are using a set to store the elements,
251 // duplicates are intrinsically avoided.
253 for(const auto& hlElem : _highLevelElements) {
254 for(const auto& hwElem : hlElem->getHardwareAccessingElements()) {
255 _lowLevelElementsAndExceptionFlags.insert({hwElem, false});
256 }
257 }
258
259 // update the list of CopyRegisterDecorators
260 _copyDecorators.clear();
261 for(const auto& hlElem : _highLevelElements) {
262 if(boost::dynamic_pointer_cast<ChimeraTK::CopyRegisterDecoratorTrait>(hlElem) != nullptr) {
263 _copyDecorators.insert(hlElem);
264 }
265 for(const auto& hwElem : hlElem->getInternalElements()) {
266 if(boost::dynamic_pointer_cast<ChimeraTK::CopyRegisterDecoratorTrait>(hwElem) != nullptr) {
267 _copyDecorators.insert(hwElem);
268 }
269 }
270 }
271
272 // read-write flag may need to be updated
274 }
275
276 /********************************************************************************************************************/
277
278 namespace detail {
281 struct TransferGroupTransferElementAbstractor : TransferElementAbstractor {
282 explicit TransferGroupTransferElementAbstractor(boost::shared_ptr<TransferElement> impl)
283 : TransferElementAbstractor(std::move(impl)) {}
284
285 void replaceTransferElement(boost::shared_ptr<TransferElement> newElement) {
286 _impl->replaceTransferElement(std::move(newElement));
287 }
288 };
289 } // namespace detail
290
291 /********************************************************************************************************************/
292
293 void TransferGroup::addAccessor(const boost::shared_ptr<TransferElement>& accessor) {
295 auto x = detail::TransferGroupTransferElementAbstractor(accessor);
296 addAccessorImpl(x, true);
297 }
298
299 /********************************************************************************************************************/
300
302 addAccessorImpl(accessor, false);
303 }
304
305 /********************************************************************************************************************/
306
308 _isReadable = true;
309 _isWriteable = true;
310 for(const auto& elem : _highLevelElements) {
311 if(!elem->isReadable()) _isReadable = false;
312 if(!elem->isWriteable()) _isWriteable = false;
313 }
315 }
316
317 /********************************************************************************************************************/
318
320 std::cout << "=== Accessors added to this group: " << std::endl;
321 for(const auto& elem : _highLevelElements) {
322 std::cout << " - " << elem->getName() << std::endl;
323 }
324 std::cout << "=== Low-level transfer elements in this group: " << std::endl;
325 for(auto& elem : _lowLevelElementsAndExceptionFlags) {
326 std::cout << " - " << elem.first->getName() << std::endl;
327 }
328 std::cout << "===" << std::endl;
329 }
330
331 /********************************************************************************************************************/
332
333} /* namespace ChimeraTK */
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition AccessMode.cc:20
Base class for register accessors abstractors independent of the UserType.
const boost::shared_ptr< TransferElement > & getHighLevelImplElement()
Obtain the highest level implementation TransferElement.
void replaceTransferElement(const boost::shared_ptr< TransferElement > &newElement)
Search for all underlying TransferElements which are considered identical (see mayReplaceOther()) wit...
TransferElementAbstractor()=default
Create an uninitialised abstractor - just for late initialisation.
AccessModeFlags getAccessModeFlags() const
Return the AccessModeFlags for this TransferElement.
boost::shared_ptr< TransferElement > _impl
Untyped pointer to implementation.
std::set< boost::shared_ptr< TransferElement > > _copyDecorators
List of all CopyRegisterDecorators in the group.
void addAccessor(TransferElementAbstractor &accessor)
Add a register accessor to the group.
bool _isReadable
Cached value whether all elements are readable.
bool _isWriteable
Cached value whether all elements are writeable.
std::set< boost::shared_ptr< TransferElement > > _highLevelElements
List of high-level TransferElements in this group which are directly used by the user.
bool _cachedReadableWriteableIsValid
Flag whether to update the cached information.
bool isReadOnly()
Check if transfer group is read-only.
std::map< boost::shared_ptr< TransferElement >, bool > _lowLevelElementsAndExceptionFlags
List of low-level TransferElements in this group, which are directly responsible for the hardware acc...
bool isWriteable()
Check if transfer group is writeable.
void write(VersionNumber versionNumber={})
Trigger write transfer for all accessors in the group.
bool isReadable()
Check if transfer group is readable.
void dump()
Print information about the accessors in this group to screen, which might help to understand which t...
void updateIsReadableWriteable()
Helper function to update the cached state variables.
void runPostReads(const std::set< boost::shared_ptr< TransferElement > > &elements, const std::exception_ptr &firstDetectedRuntimeError)
void read()
Trigger read transfer for all accessors in the group.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
STL namespace.