HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_TaskExclusive.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_TaskExclusive.h (UT Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #pragma once
12 
13 #ifndef __UT_TaskExclusive__
14 #define __UT_TaskExclusive__
15 
16 #include <UT/UT_UniquePtr.h>
17 #include <SYS/SYS_AtomicInt.h>
18 
19 #include <oneapi/tbb/collaborative_call_once.h>
20 
21 #include <utility>
22 
23 /// This is a lock-free implementation for exclusive task execution. That is,
24 /// a task which needs to be performed once (i.e. std::once). However, this
25 /// construct will allow TBB to recycle blocked tasks so they can be used for
26 /// other processing.
27 ///
28 /// This can be used as an alternate to a lock. If code underneath a lock
29 /// calls into TBB, this can lead to a deadlock since TBB can steal a child
30 /// task to complete a parent task outside the lock. This typically requires a
31 /// separate task arena. A lock will also block a thread, preventing it from
32 /// participating in other tasks.
33 ///
34 /// UT_TaskExclusive provides a good alternative, ensuring that only one thread
35 /// will execute the functor, and all other threads which wait for the functor
36 /// to finish will be allowed to participate in other computation (even to help
37 /// computing parallel tasks in the functor).
38 ///
39 /// The class is templated on a functor which is used to actually perform the
40 /// execution. The template functor needs to have a operator()() method.
41 ///
42 /// For example, given a single Object which has a deferredInitialize() method
43 /// that may get called from multiple threads: @code
44 /// class Object
45 /// {
46 /// UT_TaskExclusive<Object> myExclusive;
47 ///
48 /// public:
49 /// // Users call deferredInitialize when they want the object to be
50 /// // initialized. However, it's possible the object may need to be
51 /// // initialized by multiple threads. The object uses UT_TaskExclusive
52 /// // to ensure doInitialization() is only executed one time.
53 /// void deferredInitialize()
54 /// {
55 /// myExclusive.execute(*this);
56 /// }
57 /// void operator()()
58 /// {
59 /// // This method will only be called once, even if
60 /// // multiple threads invoke the deferredInitialize() method
61 /// // simultaneously.
62 /// doInitialization();
63 /// }
64 /// }
65 /// @endcode
66 /// If you have multiple methods that should only be called one time, you can
67 /// always create a nested object functor.
68 template <class T>
70 {
71 public:
73  {
74  reset();
75  }
76 
77  /// Execute the function. This will guarantee the function has been
78  /// run before the execute() function returns. However, no locking will be
79  /// done.
80  ///
81  /// If multiple threads try to call the function simultaneously, only one
82  /// function will run, while the other will yield its cycles to other
83  /// parallel tasks. When the first task completes, both threads will
84  /// return.
85  void execute(T &func)
86  {
87  oneapi::tbb::collaborative_call_once(
88  *myCollaborativeFlag,
89  [&]
90  {
91  func();
92  myIsDone.store(true);
93  }
94  );
95  }
96 
97  /// Executes the function in this thread without any locking
98  /// or protection. Useful if the caller has already setup the
99  /// appropriate lock.
101  {
102  if (!myIsDone.load())
103  {
104  func();
105  myIsDone.store(true);
106  }
107  }
108 
109  /// Resetting the exclusive task should only be done when there's no
110  /// possibility that any threads are trying to execute or relying on the
111  /// results of the computation.
112  void reset()
113  {
114  myCollaborativeFlag =
115  UTmakeUnique<oneapi::tbb::collaborative_once_flag>();
116  myIsDone.store(false);
117  }
118 
119  /// Test whether the function has been executed. This is thread-safe, but
120  /// doesn't count on other threads which may be in the process of running
121  /// it.
122  bool hasRun() const
123  {
124  return myIsDone.load();
125  }
126 
127 private:
129  SYS_AtomicInt<bool> myIsDone;
130 
131 };
132 #endif
133 
void store(T val, SYS_MemoryOrder order=SYS_MEMORY_ORDER_SEQ_CST)
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
T load(SYS_MemoryOrder order=SYS_MEMORY_ORDER_SEQ_CST) const
void executeNoThread(T &func)
GLenum func
Definition: glcorearb.h:783
bool hasRun() const
void execute(T &func)