LCOV - code coverage report
Current view: top level - Src/Task - SpTask.hpp (source / functions) Hit Total Coverage
Test: Coverage example Lines: 148 181 81.8 %
Date: 2021-12-02 17:21:05 Functions: 3429 7584 45.2 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////
       2             : // Spetabaru - Berenger Bramas MPCDF - 2017
       3             : // Under LGPL Licence, please you must read the LICENCE file.
       4             : ///////////////////////////////////////////////////////////////////////////
       5             : #ifndef SPTASK_HPP
       6             : #define SPTASK_HPP
       7             : 
       8             : #include <tuple>
       9             : #include <unordered_map>
      10             : #include <typeinfo>
      11             : 
      12             : #include "SpAbstractTask.hpp"
      13             : #include "Data/SpDataHandle.hpp"
      14             : #include "Utils/SpUtils.hpp"
      15             : #include "Utils/small_vector.hpp"
      16             : 
      17             : #ifdef __GNUG__
      18             : #include <cxxabi.h>
      19             : #endif
      20             : 
      21             : class SpAbstractTaskGraph;
      22             : 
      23             : template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
      24             : class SpTask : public SpAbstractTaskWithReturn<RetType> {
      25             :     using Parent = SpAbstractTask;
      26             :     using ParentReturn = SpAbstractTaskWithReturn<RetType>;
      27             : 
      28             :     //! Number of parameters in the task function prototype
      29             :     static const long int NbParams = std::tuple_size<DataDependencyTupleTy>::value;
      30             : 
      31             :     //! Internal value for undefined dependences
      32       18770 :     static constexpr long int UndefinedKey(){
      33       18770 :         return -1;
      34             :     }
      35             : 
      36             :     //! Data handles
      37             :     std::array<SpDataHandle*,NbParams> dataHandles;
      38             :     //! Dependences' keys for each handle
      39             :     std::array<long int,NbParams> dataHandlesKeys;
      40             : 
      41             :     //! Extra handles
      42             :     small_vector<SpDataHandle*> dataHandlesExtra;
      43             :     //! Extra handles's dependences keys
      44             :     small_vector<long int> dataHandlesKeysExtra;
      45             : 
      46             :     //! DataDependency objects
      47             :     DataDependencyTupleTy tupleParams;
      48             :     
      49             :     //! Callables
      50             :     CallableTupleTy callables;
      51             : 
      52             :     ///////////////////////////////////////////////////////////////////////////////
      53             :     /// Methods to call the task function with a conversion from handle to data
      54             :     ///////////////////////////////////////////////////////////////////////////////
      55             : 
      56             :     //! Expand the tuple with the index and call getView
      57             :     template <class CallableTy, std::size_t... Is>
      58         875 :     static RetType SpTaskCoreWrapper(CallableTy &callable, DataDependencyTupleTy &dataDep, std::index_sequence<Is...>){
      59         875 :         return std::invoke(callable.getCallableRef(), std::get<Is>(dataDep).getView()...);
      60             :     }
      61             : 
      62             :     //! Dispatch use if RetType is not void (will set parent value with the return from function)
      63             :     template <class SRetType, class CallableTy>
      64         126 :     static void executeCore(SpAbstractTaskWithReturn<SRetType>* taskObject, CallableTy &callable, DataDependencyTupleTy &dataDep) {
      65         126 :         taskObject->setValue(SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<std::tuple_size<DataDependencyTupleTy>::value>{}));
      66         126 :     }
      67             : 
      68             :     //! Dispatch use if RetType is void (will not set parent value with the return from function)
      69             :     template <class CallableTy>
      70         749 :     static void executeCore(SpAbstractTaskWithReturn<void>* /*taskObject*/, CallableTy &callable, DataDependencyTupleTy &dataDep) {
      71         749 :         SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<NbParams>{});
      72         749 :     }
      73             : 
      74             :     //! Called by parent abstract task class
      75         875 :     void executeCore([[maybe_unused]] SpCallableType ct) final {
      76         875 :         executeCore(this, std::get<0>(callables), tupleParams);
      77         875 :     }
      78             : 
      79             : public:
      80             :     //! Constructor from a task function
      81             :     template <typename... T>
      82        1073 :     explicit SpTask(SpAbstractTaskGraph* const inAtg, const SpTaskActivation initialActivationState, 
      83             :                     const SpPriority &inPriority,
      84             :                     DataDependencyTupleTy &&inDataDepTuple,
      85             :                     CallableTupleTy &&inCallableTuple, T... t) 
      86             :         : SpAbstractTaskWithReturn<RetType>(inAtg, initialActivationState, inPriority),
      87             :         tupleParams(inDataDepTuple),
      88        1073 :         callables(std::move(inCallableTuple)) {
      89             :         ((void) t, ...);
      90        1073 :         std::fill_n(dataHandles.data(), NbParams, nullptr);
      91        1073 :         std::fill_n(dataHandlesKeys.data(), NbParams, UndefinedKey());
      92             : 
      93             : #ifdef __GNUG__
      94             :         // if GCC then we ask for a clean type as default task name
      95             :         int status;
      96        1073 :         char *demangledName = abi::__cxa_demangle(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name(), 0, 0, &status);
      97        1073 :         if(status == 0){
      98        1073 :             assert(demangledName);
      99        1073 :             Parent::setTaskName(demangledName);
     100             :         }
     101             :         else{
     102           0 :             Parent::setTaskName(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name());
     103             :         }
     104        1073 :         free(demangledName);
     105             : #else
     106             :         Parent::setTaskName(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name());
     107             : #endif
     108        1073 :     }
     109             : 
     110        1073 :     DataDependencyTupleTy& getDataDependencyTupleRef() {
     111        1073 :         return tupleParams;
     112             :     }
     113             : 
     114             :     //! Set the dependence at position HandleIdx in the prototype
     115             :     template <long int HandleIdx>
     116        1872 :     void setDataHandle(SpDataHandle* inData, const long int inHandleKey){
     117             :         static_assert(HandleIdx < NbParams, "Cannot set more handle the NbParams");
     118        1872 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " setDataHandle " <<  HandleIdx << " at " << inData;
     119        1872 :         assert(dataHandles[HandleIdx] == nullptr);
     120        1872 :         dataHandles[HandleIdx] = inData;
     121        1872 :         dataHandlesKeys[HandleIdx] = inHandleKey;
     122        1872 :     }
     123             : 
     124             :     //! Add a dependence (extra), can be done at runtime
     125             :     template <long int HandleIdx>
     126         514 :     void addDataHandleExtra(SpDataHandle* inData, const long int inHandleKey){
     127         514 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " addDataHandleExtra at " << inData;
     128         514 :         assert(dataHandles[HandleIdx] != nullptr);
     129         514 :         dataHandlesExtra.emplace_back(inData);
     130         514 :         dataHandlesKeysExtra.emplace_back(inHandleKey);
     131         514 :     }
     132             : 
     133             : 
     134             :     //! For speculation in order to update the memory pointer
     135             :     //! that will be used when the function will be called
     136             :     template <long int HandleIdx, class ParamType>
     137         290 :     void updatePtr(const long int position, ParamType* ptr){
     138         290 :         std::get<HandleIdx>(tupleParams).updatePtr(position, ptr);
     139         290 :     }
     140             : 
     141             :     //! The number of parameters in the task prototype
     142           0 :     long int getNbParams() final {
     143           0 :         return NbParams;
     144             :     }
     145             : 
     146             :     //! Return true if all dependences are ready
     147             :     //! Dependence are not use after this call, it is just a check
     148        2319 :     bool dependencesAreReady() const final{
     149        4618 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     150        3470 :             assert(dataHandles[idxDeps]);
     151        3470 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     152        3470 :             if(dataHandles[idxDeps]->canBeUsedByTask(this, dataHandlesKeys[idxDeps]) == false){
     153        1171 :                 SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady FALSE, at index " << idxDeps << " " << dataHandles[idxDeps]
     154        1171 :                                   << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
     155        1171 :                 return false;
     156             :             }
     157        2299 :             SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady TRUE, at index " << idxDeps << " " << dataHandles[idxDeps] << "\n";
     158             :         }
     159        1148 :         if(dataHandlesExtra.size()){
     160         794 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     161         678 :                 if(dataHandlesExtra[idxDeps]->canBeUsedByTask(this, dataHandlesKeysExtra[idxDeps]) == false){
     162          75 :                 SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady FALSE, at index extra " << idxDeps << " " << dataHandlesExtra[idxDeps]
     163          75 :                                   << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>() << "\n";
     164          75 :                     return false;
     165             :                 }
     166             :             }
     167             :         }
     168        1073 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady TRUE";
     169        1073 :         return true;
     170             :     }
     171             : 
     172             :     //! Tell the dependences that they are used by the current task
     173        1073 :     void useDependences(std::unordered_set<SpDataHandle*>* exceptionList) final{
     174        1073 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences";
     175        2944 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     176        1872 :             assert(dataHandles[idxDeps]);
     177        1872 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     178        1872 :             if((exceptionList == nullptr || exceptionList->find(dataHandles[idxDeps]) == exceptionList->end())){
     179        1872 :                 dataHandles[idxDeps]->setUsedByTask(this, dataHandlesKeys[idxDeps]);
     180        3743 :                 SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences at index " << idxDeps << " " << dataHandles[idxDeps]
     181        1872 :                                   << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
     182             :             }
     183             :         }
     184        1072 :         if(dataHandlesExtra.size()){
     185         630 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     186         514 :                 if( exceptionList == nullptr || exceptionList->find(dataHandles[idxDeps]) == exceptionList->end()){
     187         514 :                     dataHandlesExtra[idxDeps]->setUsedByTask(this, dataHandlesKeysExtra[idxDeps]);
     188        1028 :                     SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences at index " << idxDeps << " " << dataHandlesExtra[idxDeps]
     189         514 :                                       << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>() << "\n";
     190             :                 }
     191             :             }
     192             :         }
     193        1072 :     }
     194             : 
     195             :     //! Tell the dependences that they are no longer use by the current task,
     196             :     //! and fill with potential candidate.
     197             :     //! The algorithm will first release all the dependences, such that
     198             :     //! when filling with potentialReady we are able to find tasks that have more
     199             :     //! than one dependence in common with the current task.
     200        1073 :     void releaseDependences(small_vector_base<SpAbstractTask*>* potentialReady) final {
     201             :         // Arrays of boolean flags indicating for each released dependency whether the "after release" pointed to
     202             :         // dependency slot in the corresponding data handle contains any unfullfilled memory access
     203             :         // requests.
     204             :         std::array<bool, NbParams> curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles;
     205        2146 :         small_vector<bool> curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra(dataHandlesExtra.size());
     206             :         
     207        1073 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences";
     208        2945 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     209        1872 :             assert(dataHandles[idxDeps]);
     210        1872 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     211        1872 :             curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles[idxDeps] = dataHandles[idxDeps]->releaseByTask(this, dataHandlesKeys[idxDeps]);
     212        3744 :             SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences FALSE, at index " << idxDeps << " " << dataHandles[idxDeps]
     213        1871 :                               << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
     214             :         }
     215        1073 :         if(dataHandlesExtra.size()){
     216         630 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     217         514 :                 curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra[idxDeps] = dataHandlesExtra[idxDeps]->releaseByTask(this, dataHandlesKeysExtra[idxDeps]);
     218        1027 :                 SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences FALSE, at index " << idxDeps << " " << dataHandlesExtra[idxDeps]
     219         514 :                                   << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>();
     220             :             }
     221             :         }
     222             : 
     223        2945 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     224        1872 :             assert(dataHandles[idxDeps]);
     225        1872 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     226        1872 :             if(curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles[idxDeps]){
     227        1063 :                 dataHandles[idxDeps]->fillCurrentTaskList(potentialReady);
     228             :             }
     229             :         }
     230        1073 :         if(dataHandlesExtra.size()){
     231         630 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     232         514 :                 if(curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra[idxDeps]){
     233          38 :                     dataHandlesExtra[idxDeps]->fillCurrentTaskList(potentialReady);
     234             :                 }
     235             :             }
     236             :         }
     237        1073 :     }
     238             : 
     239             :     //! Fill with the tasks that depend on the current task
     240        1370 :     virtual void getDependences(small_vector_base<SpAbstractTask*>* allDeps) const final {
     241        3847 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     242        2477 :             assert(dataHandles[idxDeps]);
     243        2477 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     244        2477 :             dataHandles[idxDeps]->getDependences(allDeps, dataHandlesKeys[idxDeps]);
     245             :         }
     246        1370 :         if(dataHandlesExtra.size()){
     247         932 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     248         748 :                 dataHandlesExtra[idxDeps]->getDependences(allDeps, dataHandlesKeysExtra[idxDeps]);
     249             :             }
     250             :         }
     251        1370 :     }
     252             : 
     253             :     //! Fill with the tasks that current task depend on
     254           0 :     virtual void getPredecessors(small_vector_base<SpAbstractTask*>* allPredecessors) const final {
     255           0 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     256           0 :             assert(dataHandles[idxDeps]);
     257           0 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     258           0 :             dataHandles[idxDeps]->getPredecessors(allPredecessors, dataHandlesKeys[idxDeps]);
     259             :         }
     260           0 :         if(dataHandlesExtra.size()){
     261           0 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     262           0 :                 dataHandlesExtra[idxDeps]->getPredecessors(allPredecessors, dataHandlesKeysExtra[idxDeps]);
     263             :             }
     264             :         }
     265           0 :     }
     266             : 
     267             :     //! Return true if at least one dependence is in mode inMode
     268        2319 :     bool hasMode(const SpDataAccessMode inMode) const final{
     269        2319 :         SpDebugPrint() << "SpTask -- " << Parent::getId() << " hasMode";
     270        6520 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     271        4263 :             assert(dataHandles[idxDeps]);
     272        4263 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     273        4263 :             if(dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]) == inMode){
     274          62 :                 return true;
     275             :             }
     276             :         }
     277        2257 :         if(dataHandlesExtra.size()){
     278        2195 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     279        1766 :                 if(dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]) == inMode){
     280           0 :                     return true;
     281             :                 }
     282             :             }
     283             :         }
     284        2257 :         return false;
     285             :     }
     286             : 
     287           0 :     small_vector<std::pair<SpDataHandle*,SpDataAccessMode>> getDataHandles() const final{
     288           0 :         small_vector<std::pair<SpDataHandle*,SpDataAccessMode>> data;
     289           0 :         data.reserve(NbParams + dataHandlesExtra.size());
     290             : 
     291           0 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     292           0 :             assert(dataHandles[idxDeps]);
     293           0 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     294           0 :             data.emplace_back(dataHandles[idxDeps], dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]));
     295             :         }
     296           0 :         if(dataHandlesExtra.size()){
     297           0 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     298           0 :                 data.emplace_back(dataHandlesExtra[idxDeps], dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]));
     299             :             }
     300             :         }
     301           0 :         return data;
     302             :     }
     303             :     
     304             :     //! Check that dependences are correct
     305             :     //! this is a simple check to ensure that no dependences are
     306             :     //! incompatible mode (like having a variable both in read and write)
     307        1073 :     bool areDepsCorrect() const{
     308        2146 :         std::unordered_map<SpDataHandle*,SpDataAccessMode> existingDeps;
     309             :         
     310        2945 :         for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
     311        1872 :             assert(dataHandles[idxDeps]);
     312        1872 :             assert(dataHandlesKeys[idxDeps] != UndefinedKey());
     313        1872 :             auto testDep = existingDeps.find(dataHandles[idxDeps]);
     314        1872 :             const SpDataAccessMode testMode = dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]);
     315        1872 :             if(testDep == existingDeps.end()){
     316        1872 :                 existingDeps[dataHandles[idxDeps]] = testMode;
     317             :             }
     318           0 :             else if(testMode == SpDataAccessMode::READ && testDep->second == SpDataAccessMode::READ){
     319             :             }
     320           0 :             else if(testMode == SpDataAccessMode::PARALLEL_WRITE && testDep->second == SpDataAccessMode::PARALLEL_WRITE){
     321             :             }
     322             :             else{
     323           0 :                 return false;
     324             :             }
     325        1872 :             existingDeps[dataHandles[idxDeps]] = testMode;
     326             :         }
     327             :         
     328        1073 :         if(dataHandlesExtra.size()){
     329         630 :             for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
     330         514 :                 auto testDep = existingDeps.find(dataHandlesExtra[idxDeps]);
     331         514 :                 const SpDataAccessMode testMode = dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]);
     332         514 :                 if(testDep == existingDeps.end()){
     333         514 :                     existingDeps[dataHandlesExtra[idxDeps]] = testMode;
     334             :                 }
     335           0 :                 else if(testMode == SpDataAccessMode::READ && testDep->second == SpDataAccessMode::READ){
     336             :                 }
     337           0 :                 else if(testMode == SpDataAccessMode::PARALLEL_WRITE && testDep->second == SpDataAccessMode::PARALLEL_WRITE){
     338             :                 }
     339             :                 else{
     340           0 :                     return false;                
     341             :                 }
     342         514 :                 existingDeps[dataHandlesExtra[idxDeps]] = testMode;
     343             :             }
     344             :         }
     345             :         
     346        1073 :         return true;
     347             :     }
     348             : 
     349           0 :     bool hasCallableOfType([[maybe_unused]] const SpCallableType sct) const override final {
     350             :         if constexpr(std::tuple_size_v<CallableTupleTy> == 1) {
     351           0 :             return std::tuple_element_t<0, CallableTupleTy>::callable_type == sct; 
     352             :         } else {
     353             :             return true;
     354             :         }
     355             :     }
     356             :     
     357         125 :     std::string getTaskBodyString() override {
     358             :         
     359         250 :         std::ostringstream os;
     360             :         
     361         340 :         for(size_t i=0; i < NbParams; i++) {
     362         215 :             os << SpModeToStr(dataHandles[i]->getModeByTask(dataHandlesKeys[i])) << " " << dataHandles[i]->getRawPtr() << std::endl;
     363             :         }
     364             :         
     365         125 :         for(size_t i=0; i < dataHandlesExtra.size(); i++) {
     366           0 :             os << SpModeToStr(dataHandlesExtra[i]->getModeByTask(dataHandlesKeysExtra[i])) << " " << dataHandlesExtra[i]->getRawPtr() << std::endl;
     367             :         }
     368         250 :         return os.str();
     369             :     }
     370             : };
     371             : 
     372             : template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
     373             : class SpSelectTask : public SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>
     374             : {
     375             :     using Parent = SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>;
     376             :     
     377             :     // flag indicating if the select task is carrying surely written values over
     378             :     bool isCarrSurWrittValuesOver;
     379             :     
     380             : public:
     381         147 :     explicit SpSelectTask(SpAbstractTaskGraph* const inAtg, const SpTaskActivation initialActivationState, 
     382             :                           const SpPriority& inPriority,
     383             :                           DataDependencyTupleTy &&inDataDepTuple,
     384             :                           CallableTupleTy &&inCallableTuple, bool iCSWVO)
     385             :                         : Parent(inAtg, initialActivationState, inPriority,
     386         147 :                           std::move(inDataDepTuple), std::move(inCallableTuple)), isCarrSurWrittValuesOver(iCSWVO) {}
     387             :     
     388          57 :     void setEnabledDelegate(const SpTaskActivation inIsEnable) override final {
     389          57 :         if((inIsEnable == SpTaskActivation::DISABLE && !isCarrSurWrittValuesOver)
     390           6 :             || inIsEnable == SpTaskActivation::ENABLE) {
     391          51 :             this->setEnabled(inIsEnable);
     392             :         }
     393          57 :     }
     394             : };
     395             : 
     396             : #endif

Generated by: LCOV version 1.14