HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PDGE_Evaluator.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  * COMMENTS:
7  */
8 
9 #ifndef __PDGE_EVALUATOR_H__
10 #define __PDGE_EVALUATOR_H__
11 
12 #include "PDGE_API.h"
13 
14 #include "PDGE_Dependency.h"
15 #include "PDGE_DependencyOwner.h"
16 #include "PDGE_EvaluationOptions.h"
17 #include "PDGE_Resolutions.h"
18 
19 #include <UT/UT_ConcurrentQueue.h>
20 #include <UT/UT_ParallelUtil.h>
21 #include <UT/UT_StringHolder.h>
22 #include <UT/UT_TaskGroup.h>
23 #include <UT/UT_TaskScope.h>
24 #include <UT/UT_TBBSpinLock.h>
25 #include <UT/UT_UniquePtr.h>
26 
27 #include <utility>
28 
29 /*
30  * Evaluates a dependency graph in parallel. The evaluator is provided with a
31  * list of initial PDGE_Dependency objects that can be resolved immediately.
32  * This kicks off recursive evaluation of the graph.
33  *
34  * Internally this class runs work in the background using a UT_TaskGroup.
35  * It provides utility methods to cancel evaluation of the dependency graph,
36  * and supports various configuration options thatcan be passed in via a
37  * PDGE_EvaluationOption struct.
38  *
39  * There are three ways to submit PDGE_Resolution lists to the evaluator:
40  *
41  * queueInitial will add initial evaluations that have no dependencies. It
42  * can only be called when evaluation has not yet begun, to add root nodes
43  * in the dependency graph.
44  *
45  * queueResolve will queue evaluations from a background thread and run them
46  * asynchronously. The caller does not block on the resolutions being
47  * processed. Internally, this will run a functor in the task group that runs
48  * the regular resolve method.
49  *
50  * resolve will do a blocking parallelReduce over the supplied resolutions and
51  * iteratively evaluate them and any other dependencies that are able to run
52  * as a result.
53  */
55 {
56 public:
57  /// The state of the evaluator object
58  enum State
59  {
60  /// The evaluator is uninitialize and evaluation has not yet begun
62 
63  /// The evaluator is evaluating
65 
66  /// The evaluator is canceling the current evaluation
68 
69  /// The evaluator has completed, either because it ran normally or
70  /// because it was canceled.
72 
73  /// This object is invalid because it was cleaned up during shutdown
75  };
76 
77 public:
79  ~PDGE_Evaluator() noexcept override;
80 
81  /// Returns the memory usage of this object
82  int64 getMemoryUsage(bool inclusive) const override;
83 
84  /// Returns the current state of the evaluator. This method is not thread-
85  /// safe when called off the main thread if the main thread may be calling
86  /// cancel().
87  inline State evaluationState() const
88  { return myState; }
89 
90  /// Returns the evaluation options used for the current cook
91  inline const PDGE_EvaluationOptions&
93  { return myOptions; }
94 
95  /// Evaluates the dependency graph. If blocking is true, this method
96  /// waits until the evaluation is completed. When blocking the functors
97  /// created by the evaluator will be created in a task scope that is
98  /// parented to the calling code's current task scope. Otherwise, if the
99  /// cook is not blocking, an empty task scope will be used and evaluating
100  /// dependencies will be unable to share task locks held by the calling
101  /// code.
102  void evaluate(const PDGE_EvaluationOptions& options);
103 
104  /// Cancels the current evaluation. If this object is not evaluating, this
105  /// method does nothing.
106  void cancelEvaluator();
107 
108  /// Resets the object back to its default state. This method cannot be
109  /// called unless the evaluator is the Completed or Uninitialized state.
110  void resetEvaluator();
111 
112  /// Sets this evaluator as invalid, which disables any additional work
113  /// creation. It also waits for the contained task group to finish any
114  /// pending work, so that it can be cleanly destroyed.
115  void invalidateEvaluator();
116 
117 
118  /// Adds a root dependency -- a dependency that must be completed before
119  /// this evaluator can be marked as a Completed.
120  void addRootDependency(PDGE_Dependency* dependency);
121 
122  /// Returns true if this evaluator depends on the specified dependency.
123  inline bool hasRootDependency(PDGE_Dependency* root) const
124  { return myRootDependency.hasDependency(root); }
125 
126  /// Adds an initial dependency resolution, which will be processed as
127  /// soon as evaluation begins.
128  void queueInitial(PDGE_Dependency* dependency);
129 
130  /// Queues a dependency resolution array, which will be processed in the
131  /// background with the task group owned by this object.. This method
132  /// should be used when queueing resolutions from a different TBB task or
133  /// a thread that is NOT already being run through the task group
134  /// associated with this evaluator object.
135  void queueResolve(PDGE_Resolutions& resolutions);
136 
137  /// Immediately processes a dependency resolution array on the calling
138  /// thread. This should only be called within the task hierarchy of the
139  /// task group associated with the evaluator object. Otherwise, for
140  /// external code that wishes to dynamically submit additional work, the
141  /// queueResolve method should be used instead.
142  void resolve(const PDGE_Resolutions& resolutions);
143 
144  /// Immediately processes partial evaluations in a dependency resolution
145  /// array on the calling thread. Adds additional non-partial resolutions
146  /// that become unblocked to the array.
147  void partial(PDGE_Resolutions& resolutions);
148 
149 
150  /// Returns the debug name for this evaluator object
151  UT_StringHolder debugName() const override;
152 
153  /// Called when all dependencies on this object's root dependency have
154  /// resolved. This indicates that the cook is complete.
156  const PDGE_Evaluator& evaluator,
157  PDGE_Dependency* dependency) override;
158 
159  /// Runs a functor with the evaluator's task group and returns
160  /// immediately. The functor will eventually run in the background.
161  template <typename WrappedFunction, typename... Args>
162  inline void runFunctor(Args&&... args)
163  {
164  if (myOptions.mySerial)
165  {
166  WrappedFunction functor(
167  std::forward<Args>(args)...);
168  functor();
169  }
170  else
171  {
172  ScopedFunctor<WrappedFunction> functor(
173  false,
174  std::forward<Args>(args)...);
175  functor.setTaskScope(myTaskScope);
176  myTaskGroup->run(std::move(functor));
177  }
178  }
179 
180 protected:
181  bool preEvaluate();
182  void postEvaluate();
183 
184  virtual bool preEvaluation()
185  { return true; }
186  virtual bool tickEvaluation()
187  { return false; }
188  virtual void postEvaluation(State state, bool canceled)
189  { }
190 
191 private:
192  /*
193  * Helper class that wraps a callable object in a UT_TaskScope. When the
194  * functor is invoked a task scope is constructed around the call to the
195  * wrapped functor. If this object has a task scope set, the scope is
196  * created with myScope as a its parent, otherwise it's created with the
197  * current scope as the parent. This ensures correct behavior if the
198  * functor is created on one thread, but invoked on a different one --
199  * for example, if it's queued for execution with UT_TaskGroup::run or if
200  * it's passed to a UTparallelFor.
201  *
202  * This class provies both a const and non-const operator() implemenation
203  * that create a task scope, and call WrappedFunctor::operator() with
204  * forwarded arguments.
205  *
206  * The ScopedFunctor subclasses from WrappedFunctor so that any other
207  * members from WrappedFunctor are transparently available via this object.
208  */
209  template <typename WrappedFunctor>
210  class ScopedFunctor : public WrappedFunctor
211  {
212  public:
213  template <typename... Args>
214  ScopedFunctor(bool blocking, Args&&... args)
215  : WrappedFunctor(std::forward<Args>(args)...)
216  , myScope(nullptr)
217  {
218  if (blocking)
219  myScope = UT_TaskScope::getCurrent();
220  }
221 
222  template <typename... Args>
223  inline void operator()(Args&&... args)
224  {
225  UT_TaskScope task_scope(taskScope());
226  return WrappedFunctor::operator()(
227  std::forward<Args>(args)...);
228  }
229 
230  template <typename... Args>
231  inline void operator()(Args&&... args) const
232  {
233  UT_TaskScope task_scope(taskScope());
234  return WrappedFunctor::operator()(
235  std::forward<Args>(args)...);
236  }
237 
238  inline void setTaskScope(const UT_TaskScope* parent_scope)
239  { myScope = parent_scope; }
240 
241  protected:
242  const UT_TaskScope* taskScope() const
243  {
244  if (myScope)
245  return myScope;
246  return UT_TaskScope::getCurrent();
247  }
248  protected:
249  const UT_TaskScope* myScope;
250  };
251 
252  /*
253  * Specialized version of ScopedFunctor<T> that is suitable for use with
254  * a UTparallelReduce. We need a special splitting constructor to satisify
255  * the requirements of the parallel reduce, which also needs to copy our
256  * task scope pointer to the new functor instance.
257  */
258  template <typename WrappedFunctor>
259  class ScopedReduce : public ScopedFunctor<WrappedFunctor>
260  {
261  public:
262  using Base = ScopedFunctor<WrappedFunctor>;
263  using Base::Base;
264 
265  ScopedReduce(const ScopedReduce<WrappedFunctor>& other, UT_Split split)
266  : Base(false, other, split)
267  {
268  Base::setTaskScope(other.myScope);
269  }
270  };
271 
272  using TaskGroupPtr = UT_UniquePtr<UT_TaskGroup>;
273  using ResolveQueue = UT_ConcurrentQueue<PDGE_Resolutions>;
274 
275 private:
276  PDGE_Dependency myRootDependency;
277  PDGE_Resolutions myInitialResolves;
278 
279  ResolveQueue myResolveQueue;
280 
281  TaskGroupPtr myTaskGroup;
282  const UT_TaskScope* myTaskScope;
283 
284  PDGE_EvaluationOptions myOptions;
285  State myState;
286  UT_TBBSpinLock myCancelLock;
287 };
288 
289 #endif
State
The state of the evaluator object.
tbb::split UT_Split
Definition: GA_PolyCounts.h:24
static const UT_TaskScope * getCurrent()
Definition: UT_TaskScope.h:96
void runFunctor(Args &&...args)
virtual bool preEvaluation()
The evaluator is evaluating.
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
bool hasRootDependency(PDGE_Dependency *root) const
Returns true if this evaluator depends on the specified dependency.
long long int64
Definition: SYS_Types.h:116
The evaluator is uninitialize and evaluation has not yet begun.
This object is invalid because it was cleaned up during shutdown.
virtual UT_StringHolder debugName() const =0
virtual void postEvaluation(State state, bool canceled)
virtual bool tickEvaluation()
#define PDGE_API
Definition: PDGE_API.h:23
const PDGE_EvaluationOptions & evaluationOptions() const
Returns the evaluation options used for the current cook.
The evaluator is canceling the current evaluation.
virtual PDGE_Dependency::State evalResolve(PDGE_Resolutions &, const PDGE_Evaluator &, PDGE_Dependency *)
Called when a dependency owned by this object is resolved.
**If you just want to fire and args
Definition: thread.h:609
#define const
Definition: zconf.h:214
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)