//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. // // Copyright 2016 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2016 UT-Battelle, LLC. // Copyright 2016 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_cont_TryExecute_h #define vtk_m_cont_TryExecute_h #include #include #include #include #include namespace vtkm { namespace cont { namespace detail { template struct TryExecuteRunIfValid; template struct TryExecuteRunIfValid { VTKM_CONT static bool Run(Functor&, const vtkm::cont::RuntimeDeviceTracker&) { return false; } }; template struct TryExecuteRunIfValid { VTKM_IS_DEVICE_ADAPTER_TAG(Device); VTKM_CONT static bool Run(Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker) { if (tracker.CanRunOn(Device())) { try { return functor(Device()); } catch (vtkm::cont::ErrorBadAllocation& e) { std::cerr << "caught ErrorBadAllocation " << e.GetMessage() << std::endl; //currently we only consider OOM errors worth disabling a device for //than we fallback to another device tracker.ReportAllocationFailure(Device(), e); } catch (vtkm::cont::ErrorBadType& e) { //should bad type errors should stop the execution, instead of //deferring to another device adapter? std::cerr << "caught ErrorBadType : " << e.GetMessage() << std::endl; } catch (vtkm::cont::ErrorBadValue& e) { //should bad value errors should stop the filter, instead of deferring //to another device adapter? std::cerr << "caught ErrorBadValue : " << e.GetMessage() << std::endl; } catch (vtkm::cont::Error& e) { //general errors should be caught and let us try the next device adapter. std::cerr << "exception is: " << e.GetMessage() << std::endl; } catch (std::exception& e) { std::cerr << "caught standard exception: " << e.what() << std::endl; } catch (...) { std::cerr << "unknown exception caught" << std::endl; } } // If we are here, then the functor was either never run or failed. return false; } }; template struct TryExecuteImpl { // Warning, these are a references. Make sure referenced objects do not go // out of scope. FunctorType& Functor; vtkm::cont::RuntimeDeviceTracker Tracker; bool Success; VTKM_CONT TryExecuteImpl( FunctorType& functor, vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) : Functor(functor) , Tracker(tracker) , Success(false) { } template VTKM_CONT bool operator()(Device) { if (!this->Success) { using DeviceTraits = vtkm::cont::DeviceAdapterTraits; this->Success = detail::TryExecuteRunIfValid::Run( this->Functor, this->Tracker); } return this->Success; } private: void operator=(const TryExecuteImpl&) = delete; }; } // namespace detail /// \brief Try to execute a functor on a list of devices until one succeeds. /// /// This function takes a functor and a list of devices. It then tries to run /// the functor for each device (in the order given in the list) until the /// execution succeeds. /// /// The functor parentheses operator should take exactly one argument, which is /// the \c DeviceAdapterTag to use. The functor should return a \c bool that is /// \c true if the execution succeeds, \c false if it fails. If an exception is /// thrown from the functor, then the execution is assumed to have failed. /// /// This function also optionally takes a \c RuntimeDeviceTracker, which will /// monitor for certain failures across calls to TryExecute and skip trying /// devices with a history of failure. /// /// This function returns \c true if the functor succeeded on a device, /// \c false otherwise. /// /// If no device list is specified, then \c VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG /// is used. /// template VTKM_CONT bool TryExecute(const Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker, DeviceList) { detail::TryExecuteImpl internals(functor, tracker); vtkm::ListForEach(internals, DeviceList()); return internals.Success; } template VTKM_CONT bool TryExecute(Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker, DeviceList) { detail::TryExecuteImpl internals(functor, tracker); vtkm::ListForEach(internals, DeviceList()); return internals.Success; } template VTKM_CONT bool TryExecute(const Functor& functor, DeviceList) { return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList()); } template VTKM_CONT bool TryExecute(Functor& functor, DeviceList) { return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList()); } template VTKM_CONT bool TryExecute( const Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) { return vtkm::cont::TryExecute(functor, tracker, VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG()); } template VTKM_CONT bool TryExecute( Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) { return vtkm::cont::TryExecute(functor, tracker, VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG()); } } } // namespace vtkm::cont #endif //vtk_m_cont_TryExecute_h