From e121d5d14aadd33b28261c6db008ea5c5471fd67 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer <martin.hierholzer@desy.de> Date: Thu, 5 Mar 2020 09:30:49 +0100 Subject: [PATCH] conclude #120: update implementation and known bugs. --- doc/spec_initialValuePropagation.md | 61 +++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/doc/spec_initialValuePropagation.md b/doc/spec_initialValuePropagation.md index b2f12d62..a2598c7f 100644 --- a/doc/spec_initialValuePropagation.md +++ b/doc/spec_initialValuePropagation.md @@ -78,6 +78,7 @@ This specification goes beyond ApplicationCore. It has impact on other ChimeraTK - Each `ChimeraTK::NDRegisterAccessor` must implement 1. separately. - Each `ChimeraTK::NDRegisterAccessor` must implement 2. separately. All accessors should already have a `ChimeraTK::VersionNumber` data member called `currentVersion` or similar, it simply needs to be constructed with a `nullptr` as an argument. +- `ChimeraTK::NDRegisterAccessor` must throw exceptions *only* in `TransferElement::postRead()` and `TransferElement::postWrite()`. No exceptions may be thrown in `TransferElement::doReadTransfer()` etc. (all transfer implementations). See also @ref exceptionHandlingDesign. ### ApplicationModule ### @@ -90,35 +91,36 @@ This specification goes beyond ApplicationCore. It has impact on other ChimeraTK - API documentation must contain 7. - Implements 8. (hence takes part in 5.a, 6.b, 7 and 10 implicitly): - All inputs of the module must be read in the `ChimeraTK::ApplicationModule::mainLoopWrapper()` before the call to `mainLoop()`. - - The type of the read decided via `ChimeraTK::VariableNetworkNode::hasInitialValue()`, which implements 8.b. [tbd: new name] + - The type of the read decided via `ChimeraTK::VariableNetworkNode::initialValueType()`, which implements 8.b. ### ThreadedFanOut ### - Implement 3, implementation will be already covered by normal flag propagation - Needs to implement 9. (hence takes part in 5.a, 6, 7 and 10 implicitly): - - structure the fan out's "mainLoop" like this: - - read initial values (9.a via `ChimeraTK::VariableNetworkNode::hasInitialValue()` [tbd: new name]) + - structure the fan out's "mainLoop"-equivalent (`ThreadedFanOut::run()`) like this: + - read initial values (9.a via `ChimeraTK::VariableNetworkNode::initialValueType()`) - begin loop - write outputs - read input - cycle loop - - 9.d is covered by the `ChimeraTK::ExceptionHandlingDecorator`. ### TriggerFanOut ### -- Needs to implement 3, implementation will be already covered by normal flag propagation +- Implement 3, implementation will be already covered by normal flag propagation - Needs to implement 9. (hence takes part in 5.a, 6, 7 and 10 implicitly): - - structure the fan out's "mainLoop" like this: - - read initial values (9.a via `ChimeraTK::VariableNetworkNode::hasInitialValue()` [tbd: new name]) + - In contrast to the `ThreadedFanOut`, the `TriggerFanOut` has only poll-type data inputs which are all coming from the same device. Data inputs cannot come from non-devices. + - Structure the fan out's "mainLoop"-equivalent (`TriggerFanOut::run()`) like this: + - read initial values of trigger input (9.a via `ChimeraTK::VariableNetworkNode::initialValueType()`) - begin loop + - read inputs via `ChimeraTK::TransferGroup` - write outputs - - read input - cycle loop - - 9.d **not** covered by the `ChimeraTK::ExceptionHandlingDecorator` here (as the decorator is not effective with `ChimeraTK::TransferGroup`s), needs to be properly implemented [tbd: if all DeviceAccess backends are obliged to throw exceptions *only* in `postRead()`, the decorator could be effective also for `TransferGroup`s and we would also solve some issues with exceptions during asynchronous transfers and `readAny()`] + - 9.d is covered by the `ChimeraTK::ExceptionHandlingDecorator`. It is important that `ChimeraTK::NDRegisterAccessor` throws only in `TransferElement::postRead()` (see implementation section for the `NDRegisterAccessor`), since otherwise the decorator cannot catch the exceptions due to the `TransferGroup`. ### DeviceModule ### -- All points are also covered by @ref exceptionHandlingDesign. +All points are also covered by @ref exceptionHandlingDesign. + - Takes part in 6.a: - `ChimeraTK::DeviceModule::writeAfterOpen/writeRecoveryOpen` [tbd: new name for unified list] is a list of accessors to be written after the device is opened/recovered. - Takes part in 6.b: @@ -139,11 +141,10 @@ This specification goes beyond ApplicationCore. It has impact on other ChimeraTK - @ref exceptionHandlingDesign states that non-blocking read operations like `ChimeraTK::TransferElement::readLatest()` should never block due to an exception. - Hence a special treatment is required in this case: - `ChimeraTK::ExceptionHandlingDecorator::readLatest()` should block until the device is opened and initialised if (and only if) the accessor still has the `ChimeraTK::VersionNumber(nullptr)` - which means it has not yet been read. - - Since `readLatest()` is always the first `read`-type function called, it is acceptable if all `read`-type functions implement this behaviour. ### VariableNetworkNode ### -- Implements the decision tree mentioned in 8.b. in `ChimeraTK::VariableNetworkNode::hasInitialValue()` [tbd: new name] +- Implements the decision tree mentioned in 8.b. in `ChimeraTK::VariableNetworkNode::initialValueType()` ### Application ### (This section refers to the class `ChimeraTK::Application`, not to the user-defined application.) @@ -155,6 +156,10 @@ This specification goes beyond ApplicationCore. It has impact on other ChimeraTK - Must implement 5.a - Needs to be done in all adapters separately +### Non-memeber functions in ApplicationCore ### + +- Convenience function to call the non-blocking write function of the `ChimeraTK::ExceptionHandlingDecorator`, if the accessor is such a decorator. Otherwise the normal write function is called, since might never block due to exception handling anyways. + ## Known bugs ## @@ -166,20 +171,52 @@ This specification goes beyond ApplicationCore. It has impact on other ChimeraTK - 1. is not implemented for Device implementations (only the `UnidirectionalProcessArray` is correct at the moment). - 2. is not implemented for Device implementations (only the `UnidirectionalProcessArray` is correct at the moment). +- Exceptions are currently thrown in the wrong place (see implementation section for the NDRegisterAccessor). A possible implementation to help backends complying with this rule would be: + - Introduce non-virtual `TransferElement::readTransfer()` etc, i.e. all functions like `do[...]Transfer[...]()` should have non-virtual pendants without `do`. + - These new functions will call the actual `do[...]Transfer[...]()` function, but place a try-catch-block around to catch all ChimeraTK exceptions + - The exceptions are stored and operation is continued. In case of boolean return values (`doReadTransferNonBlocking()`, doReadTransferLatest()` and `doWriteTransfer()`), the value for success must be returned (`true` for read and `false` for write), to make sure the corresponding post-action is executed. + - With `TransferElement::postRead()` resp. `TransferElement::postWrite()` non-virtual wrappers for the post-actions already exist. In these functions, the stored exception should be thrown. + - All decorators and decorator-like accessors must be changed to call always the (new or existing) non-virtual functions in their virtual `do[...]` functions. This applies to both the transfer functions and the pre/post actions (for the latter it should be already the case). + - It is advisable to add an assert that no unthrown exception is present before storing a new exception, to prevent that exceptions might get lost due to errors in the business logic. + +### ApplicationModule / EntityOwner ### + +- 3. is not properly implemented, `faultCounter` starts at 0. The correct implementation should increase the counter in `EntityOwner::registerAccessor()` and decrease it in `EntityOwner::unregisterAccessor()` (sorry for the confusing function names...). ### TriggerFanOut ### - 3. is not correctly implemented, it needs to be done on a per-variable level. +- It currently implements its own exception handling (including the `Device::isOpened()` check), but in a wrong way. After the `NDRegisterAccessor` has been fixed, this needs to be removed. + +### DeviceModule ### + +Probably all points are duplicates with @ref exceptionHandlingDesign. + +- Merge `ChimeraTK::DeviceModule::writeAfterOpen/writeRecoveryOpen` lists. +- Implement mechanism to block read/write operations in other threads until after the initialsation is done. ### ExceptionHandlingDecorator ### +Some points are duplicates with @ref exceptionHandlingDesign. + - It waits until the device is opened, but not until after the initialisation is done. +- Provide non-blocking function. +- Implement special treatment for first `readLatest()` operation to always block in the very first call until the device is available. + - Since `readLatest()` is always the first `read`-type function called, it is acceptable if all `read`-type functions implement this behaviour. Choose whatever is easier to implement, update the implementation section of this specification to match the chosen implementation. + +### VariableNetworkNode ### + +- Rename `ChimeraTK::VariableNetworkNode::hasInitialValue()` into `ChimeraTK::VariableNetworkNode::initialValueType()` +- Remove data member storing the presence of an initial value, this is now always the case. Also change `ChimeraTK::VariableNetworkNode::initialValueType()` accordingly. ### ControlSystemAdapter ### - EPICS-Adapter might not properly implement 5.a, needs to be checked. Especially it might not be guaranteed that all variables are written (and not only those registered in the autosave plugin). - The `ChimeraTK::UnidirectionalProcessArray` uses always a default start value of `DataValidity::ok`, but overwrites this with `DataValidity::faulty` for the receivers in the factory function `ChimeraTK::UnidirectionalProcessArray::createSynchronizedProcessArray()` (both implementations, see UnidirectionalProcessArray.h). This can be solved more elegant after the DeviceAccess interface change described above. +### Non-memeber functions ### + +- Implement the missing convenience function ### Documentation ### -- GitLab