Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-10-25 09:46:43

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     // Takes ownership of the underlying task and uses the current
0044     // arena.
0045     explicit WaitingTaskWithArenaHolder(WaitingTaskHolder&& iTask);
0046 
0047     ~WaitingTaskWithArenaHolder();
0048 
0049     WaitingTaskWithArenaHolder(WaitingTaskWithArenaHolder const& iHolder);
0050 
0051     WaitingTaskWithArenaHolder(WaitingTaskWithArenaHolder&& iOther);
0052 
0053     WaitingTaskWithArenaHolder& operator=(const WaitingTaskWithArenaHolder& iRHS);
0054 
0055     WaitingTaskWithArenaHolder& operator=(WaitingTaskWithArenaHolder&& iRHS);
0056 
0057     // This spawns the task. The arena is needed to get the task spawned
0058     // into the correct arena of threads. Use of the arena allows doneWaiting
0059     // to be called from a thread outside the arena of threads that will manage
0060     // the task. doneWaiting can be called from a non-TBB thread.
0061     void doneWaiting(std::exception_ptr iExcept);
0062 
0063     // This next function is useful if you know from the context that
0064     // m_arena (which is set when the constructor was executes) is the
0065     // same arena in which you want to execute the doneWaiting function.
0066     // It allows an optimization which avoids the enqueue step in the
0067     // doneWaiting function.
0068     //
0069     // Be warned though that in general this function cannot be used.
0070     // Spawning a task outside the correct arena could create a new separate
0071     // arena with its own extra TBB worker threads if this function is used
0072     // in an inappropriate context (and silently such that you might not notice
0073     // the problem quickly).
0074     WaitingTaskHolder makeWaitingTaskHolderAndRelease();
0075 
0076     bool taskHasFailed() const noexcept;
0077 
0078     bool hasTask() const noexcept;
0079 
0080     /** since oneapi::tbb::task_group is thread safe, we can return it non-const from here since
0081         the object is not really part of the state of the holder
0082      */
0083     CMS_SA_ALLOW oneapi::tbb::task_group* group() const { return m_group; }
0084 
0085   private:
0086     // ---------- member data --------------------------------
0087     WaitingTask* m_task;
0088     oneapi::tbb::task_group* m_group;
0089     std::shared_ptr<oneapi::tbb::task_arena> m_arena;
0090   };
0091 
0092   template <typename F>
0093   auto make_lambda_with_holder(WaitingTaskWithArenaHolder h, F&& f) {
0094     return [holder = std::move(h), func = std::forward<F>(f)]() mutable {
0095       try {
0096         func(holder);
0097       } catch (...) {
0098         holder.doneWaiting(std::current_exception());
0099       }
0100     };
0101   }
0102 
0103   template <typename F>
0104   auto make_waiting_task_with_holder(WaitingTaskWithArenaHolder h, F&& f) {
0105     return make_waiting_task(
0106         [holder = h, func = make_lambda_with_holder(h, std::forward<F>(f))](std::exception_ptr const* excptr) mutable {
0107           if (excptr) {
0108             holder.doneWaiting(*excptr);
0109             return;
0110           }
0111           func();
0112         });
0113   }
0114 }  // namespace edm
0115 #endif