Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-01-11 03:38:16

0001 #ifndef FWCore_Concurrency_WaitingTaskWithArenaHolder_h
0002 #define FWCore_Concurrency_WaitingTaskWithArenaHolder_h
0003 // -*- C++ -*-
0004 //
0005 // Package:     FWCore/Concurrency
0006 // Class  :     WaitingTaskWithArenaHolder
0007 //
0008 /**\class edm::WaitingTaskWithArenaHolder
0009 
0010  Description: This holds a WaitingTask and can be passed to something
0011  the WaitingTask is waiting for. That allows that something to call
0012  doneWaiting to let the WaitingTask know it can run. The use of the
0013  arena allows one to call doneWaiting from a thread external to the
0014  arena where the task should run. The external thread might be a non-TBB
0015  thread.
0016 */
0017 //
0018 // Original Author:  W. David Dagenhart
0019 //         Created:  9 November 2017
0020 //
0021 
0022 #include <exception>
0023 #include <memory>
0024 
0025 #include "oneapi/tbb/task_arena.h"
0026 #include "oneapi/tbb/task_group.h"
0027 
0028 #include "FWCore/Utilities/interface/thread_safety_macros.h"
0029 
0030 namespace edm {
0031   class WaitingTask;
0032   class WaitingTaskHolder;
0033 
0034   class WaitingTaskWithArenaHolder {
0035   public:
0036     WaitingTaskWithArenaHolder();
0037 
0038     // Note that the arena will be the one containing the thread
0039     // that runs this constructor. This is the arena where you
0040     // eventually intend for the task to be spawned.
0041     explicit WaitingTaskWithArenaHolder(oneapi::tbb::task_group&, WaitingTask* iTask);
0042 
0043     // Captures the current arena.
0044     explicit WaitingTaskWithArenaHolder(WaitingTaskHolder iTask);
0045 
0046     ~WaitingTaskWithArenaHolder();
0047 
0048     WaitingTaskWithArenaHolder(WaitingTaskWithArenaHolder const& iHolder);
0049 
0050     WaitingTaskWithArenaHolder(WaitingTaskWithArenaHolder&& iOther);
0051 
0052     WaitingTaskWithArenaHolder& operator=(const WaitingTaskWithArenaHolder& iRHS);
0053 
0054     WaitingTaskWithArenaHolder& operator=(WaitingTaskWithArenaHolder&& iRHS);
0055 
0056     // This spawns the task. The arena is needed to get the task spawned
0057     // into the correct arena of threads. Use of the arena allows doneWaiting
0058     // to be called from a thread outside the arena of threads that will manage
0059     // the task. doneWaiting can be called from a non-TBB thread.
0060     void doneWaiting(std::exception_ptr iExcept);
0061 
0062     // Use in the case where you need to inform the parent task of a
0063     // failure before some other child task which may be run later
0064     // reports a different, but related failure. You must later call
0065     // doneWaiting in the same thread passing the same exception.
0066     void presetTaskAsFailed(std::exception_ptr iExcept) noexcept;
0067 
0068     // This next function is useful if you know from the context that
0069     // m_arena (which is set when the constructor was executes) is the
0070     // same arena in which you want to execute the doneWaiting function.
0071     // It allows an optimization which avoids the enqueue step in the
0072     // doneWaiting function.
0073     //
0074     // Be warned though that in general this function cannot be used.
0075     // Spawning a task outside the correct arena could create a new separate
0076     // arena with its own extra TBB worker threads if this function is used
0077     // in an inappropriate context (and silently such that you might not notice
0078     // the problem quickly).
0079     WaitingTaskHolder makeWaitingTaskHolderAndRelease();
0080 
0081     bool taskHasFailed() const noexcept;
0082 
0083     bool hasTask() const noexcept;
0084 
0085     /** since oneapi::tbb::task_group is thread safe, we can return it non-const from here since
0086         the object is not really part of the state of the holder
0087      */
0088     CMS_SA_ALLOW oneapi::tbb::task_group* group() const { return m_group; }
0089 
0090   private:
0091     // ---------- member data --------------------------------
0092     WaitingTask* m_task;
0093     oneapi::tbb::task_group* m_group;
0094     std::shared_ptr<oneapi::tbb::task_arena> m_arena;
0095   };
0096 
0097   template <typename F>
0098   auto make_lambda_with_holder(WaitingTaskWithArenaHolder h, F&& f) {
0099     return [holder = std::move(h), func = std::forward<F>(f)]() mutable {
0100       try {
0101         func(holder);
0102       } catch (...) {
0103         holder.doneWaiting(std::current_exception());
0104       }
0105     };
0106   }
0107 
0108   template <typename F>
0109   auto make_waiting_task_with_holder(WaitingTaskWithArenaHolder h, F&& f) {
0110     return make_waiting_task(
0111         [holder = h, func = make_lambda_with_holder(h, std::forward<F>(f))](std::exception_ptr const* excptr) mutable {
0112           if (excptr) {
0113             holder.doneWaiting(*excptr);
0114             return;
0115           }
0116           func();
0117         });
0118   }
0119 }  // namespace edm
0120 #endif