HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_Thread.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_Thread.h ( UT Library, C++)
7  *
8  * COMMENTS: Generic thread class.
9  * The owner of the thread can do things like:
10  *
11  * killThread() - Stop execution of thread
12  * waitThread() - Wait until thread finishes execution
13  * suspendThread() - Suspend execution of thread
14  * restartThread() - Restart a stopped thread
15  *
16  * TODO: It might be nice to have a way to get the exit status of a thread.
17  */
18 
19 #ifndef __UT_Thread__
20 #define __UT_Thread__
21 
22 #include "UT_API.h"
23 #include "UT_Array.h"
24 #include "UT_Assert.h"
25 #include "UT_UniquePtr.h"
26 
27 #include <SYS/SYS_Deprecated.h>
29 #include <SYS/SYS_Types.h>
30 
31 #include <stdlib.h>
32 
33 #include <thread>
34 #include <tuple>
35 #include <functional>
36 
37 #if defined(WIN32)
38 # include <intrin.h>
39  typedef int ut_thread_id_t;
40 #elif defined(USE_PTHREADS)
41 # include <sched.h>
42 # include <pthread.h>
43  typedef pthread_t ut_thread_id_t;
44 #else
45  #error Unsupported Platform for UT_Thread
46 #endif
47 
48 #define UT_INVALID_THREAD_ID ((ut_thread_id_t)0)
49 
50 // some stack size defines
51 #define UT_THREAD_DEFAULT_STACK_SIZE (8U*1024U*1024U)
52 #define UT_THREAD_SMALL_STACK_SIZE (1U*1024U*1024U)
53 
54 typedef void *(*UTthreadFunc)(void*);
55 
56 // forward declarations
57 class UT_TaskScope;
58 
60 {
61 public:
62  // The destructor will wait until the thread is idle before it completes
63  // If you wish to kill the thread, call killThread() first.
64  virtual ~UT_Thread();
65 
66  // This enum specifies the current state for a persistent thread. The
67  // thread will typically be running or idle. If the thread is idle, it's
68  // behaviour will be determined by the SpinState.
69  enum State
70  {
72  ThreadRunning
73  };
74 
75  // // The thread status determines how the thread will behave once the
76  // callback function is completed:
77  // ThreadSingleRun - The thread cannot be restarted
78  // ThreadLowUsage - The thread will yield cycles while idle
79  //
80  enum SpinMode
81  {
84  };
85 
86  /// Allocate a new thread
87  /// @param spin_mode Use ThreadSingleRun to have it exit when the thread
88  /// callback is finished. Otherwise, ThreadLowUsage
89  /// will cause the thread to loop back and wait for
90  /// more startThread() calls to run different thread
91  /// callbacks in the same thread.
92  /// @param uses_tbb Leave at true unless you absolutely know that no
93  /// TBB tasks will be spawned in thread callbacks.
94  static UT_Thread *allocThread(SpinMode spin_mode, bool uses_tbb=true);
95 
96  static int getNumProcessors();
97 
98  /// This is only valid in debug builds
99  static int activeThreadCount();
100 
101  /// Reset the number of threads that is used by Houdini. This will reread
102  /// the HOUDINI_MAXTHREADS setting.
103  /// @note There should be no active tasks when this is called.
104  /// @note Only call this from the MAIN THREAD!
105  static void resetNumProcessors();
106 
107  // getMyThreadId() is inlined for speed if we're using pthreads.
108 #if defined(USE_PTHREADS)
109  static ut_thread_id_t getMyThreadId() { return pthread_self(); }
110 #else
111  static ut_thread_id_t getMyThreadId();
112 #endif
113 
114  static ut_thread_id_t getMainThreadId();
115  static int getMainSequentialThreadId();
116  static inline int isMainThread()
117  {
118  return getMyThreadId() == getMainThreadId();
119  }
120 
121  /// Returns true if the current thread is a UT_Thread.
122  /// Returns false if the current thread is either the main thread
123  /// or a TBB thread.
124  static bool isUTThreadCurrent();
125 
126  /// Returns true iff the current thread is allowed to create more tasks.
127  /// This is sometimes disabled, to avoid needing to create a UT_TaskArena
128  /// for small cases that won't get much benefit from threading.
129  /// This should be checked by anything using tbb::parallel_for,
130  /// tbb::parallel_invoke, or anything else creating TBB tasks.
131  static bool isThreadingEnabled();
132 
133  /// This is used to disable (false) threading for the current thread,
134  /// to avoid needing to create a UT_TaskArena for small cases that won't
135  /// get much benefit from threading. It returns if it was enabled before.
136  /// It is also used to re-enable (true) threading for the current thread.
137  static bool setThreadingEnabled(bool will_be_enabled);
138 
140  {
141  public:
143  : myPreviouslyEnabled(setThreadingEnabled(false))
144  {}
146  {
147  if (myPreviouslyEnabled)
148  setThreadingEnabled(true);
149  }
150  private:
151  const bool myPreviouslyEnabled;
152  };
153 
154  // CPU pauses the task for a given number of cycles
155  static inline void pause(uint cycles)
156  {
157  for(uint i = 0; i < cycles; i++)
158 #if defined(USE_PTHREADS)
159 #if defined(ARM64)
160  __asm__ __volatile__("yield;");
161 #else
162  __asm__ __volatile__("pause;");
163 #endif
164 #else
165  _mm_pause();
166 #endif
167  }
168  // Yields the task to the scheduler.
169 #if defined(USE_PTHREADS)
170  static inline void yield(bool higher_only=false)
171  {
172  if (higher_only)
173  {
174  ::sched_yield();
175  }
176  else
177  {
178  // Sleep for 100ns. That's 10,000,000 sleep
179  // cycles a second (in case you don't have a
180  // calculator :-)
181  struct timespec ts = {0,100};
182  ::nanosleep(&ts, 0);
183  }
184  }
185 #else
186  static void yield(bool higher_only=false);
187 #endif
188 
189  /// This function has been deprecated. Use SYS_SequentialThreadIndex::get()
190  /// or SYSgetSTID instead.
191  static int SYS_DEPRECATED(12.5) getMySequentialThreadIndex()
192  { return SYS_SequentialThreadIndex::get(); }
193 
194  /// Configure the global number of tasks used by the system
195  /// - The default value of 0 uses the number of logical cores on the system
196  /// - A negative value wraps it from the number of logical cores.
197  /// eg. -1 will use all cores except for 1.
198  /// - If the negative value exceeds the number of logical cores, it is
199  /// clamped to a value of 1.
200  /// @note Only call this in the main thread when there are no tasks active.
201  /// @note This function is NOT thread-safe.
202  static void configureMaxThreads(int maxthreads = 0);
203 
204  /// Configure the default stack size for threads
205  /// - A value of 0 uses the stack size of the main thread
206  /// - A value larger than 0 will use that specific stack size
207  /// @note Only call this in the main thread when there are no tasks active.
208  /// @note This function is NOT thread-safe.
209  static void configureThreadStackSize(int stacksize);
210 
211  /// Returns true if configureMaxThreads() has been called at least once
212  static bool isMaxThreadsConfigured();
213 
214  /// Sets the current thread to minimum priority according to the rules
215  /// of the platform. This function fails if called on a thread that is
216  /// not a running UT_Thread.
217  /// Returns true if the operation was successful, otherwise returns false.
218  static bool minimizeThisThreadPriority();
219 
220 #if defined(MBSD)
221  /// Sets the quality of service (QoS) class of a thread. This is used by
222  /// the macOS scheduler to prioritize certain tasks.
223  /// @note Calling this is optional, however if it is called, it must be
224  /// called before startThread()
225  /// @note This method is only available on macOS
226  void setQoS(qos_class_t qos);
227 
228  /// Returns the quality of service (QoS) class of a thread.
229  /// @note This method is only available on macOS
230  /// @see setQoS()
231  qos_class_t getQoS() const;
232 #endif
233 
235  {
236  public:
239  };
240 
241  /// Return function pointer to terminate task scheduler that is activated
242  /// by configureMaxThreads(). This function should called prior to exit()
243  /// in order to avoid possible deadlocks when the process exits. Note that
244  /// multiple calls to the termination function are handled by only
245  /// terminating the first time. After that, no task scheduling is allowed.
246  using TerminateFunc = void (*)();
247  static TerminateFunc getTaskSchedulerExitCallback();
248 
249  // Start the thread running. If the thread is not in idle state, the
250  // thread will wait until it's in idle before starting. If the thread
251  // doesn't exist yet, it will be created.
252  virtual bool startThread(UTthreadFunc func, void *data,
253  int stacksize) = 0;
254 
255  // Use the global thread stack size set by configureMaxThreads()
256  bool startThread(UTthreadFunc func, void *data);
257 
258  // This method is called when the thread function is first entered.
259  // By default it does nothing but some sub-classes may need this.
260  virtual void threadStarted();
261 
262  // This method is called when the thread function is returned from.
263  // By default it sets the state to idle.
264  virtual void threadEnded();
265 
266 
267  // Some thread architectures have very expensive resources (i.e. sproc()
268  // threads). While these threads spin (are idle), they consume system
269  // resources. This method will let the user know whether the threads are
270  // resource hogs (so that if they spin for a long time, they could
271  // possibley be cleaned up).
272  virtual int isResourceHog() const;
273 
274  // For persistent threads (which get restarted)
275  virtual State getState();
276  virtual SpinMode getSpinMode();
277  virtual void waitForState(State desired) = 0;
278  virtual void setSpinMode(SpinMode spin_mode);
279 
280  // Terminate the thread process
281  virtual void killThread() = 0;
282 
283  // If it's possible to perform these tasks, the return code will be 1. If
284  // not, the return code will be 0.
285  virtual int suspendThread() = 0;
286  virtual int restartThread() = 0;
287 
288  int isActive()
289  { return waitThread(0); }
290 
291  /// NOTE: This level doesn't own any data apart from itself.
292  virtual int64 getMemoryUsage(bool inclusive) const = 0;
293 
294 protected:
295  // System dependent internal functions.
296  // waitThread() returns 1 if the thread is still active (i.e. exists) and
297  // should return 0 if the thread doesn't exist. If waitThread detects
298  // that the thread no longer exists, it should do appropriate cleanup.
299  virtual int waitThread(int block=1) = 0;
300 
301  // Quick check to see that the thread is really active
302  virtual int isValid();
303 
304  // This method can be used to kill an idle process.
305  void killIdle();
306 
307  static void *threadWrapper(void *data);
308 
309  // Internally used to change the state safely.
310  virtual void setState(State state) = 0;
311 
312  volatile State myState;
315  void *myCBData;
316 
318  bool myUsesTBB;
319 
320 #if defined(MBSD)
321  // The quality of service (QoS) of this thread for the macOS scheduler
322  qos_class_t myQoS;
323 #endif
324 
325  UT_Thread(SpinMode spin_mode, bool uses_tbb);
326 };
327 
328 // For debugging, the following uses a single thread (i.e. is not
329 // multi-threaded)
331 {
332 public:
333  UT_NullThread();
334  ~UT_NullThread() override;
335 
336  bool startThread(UTthreadFunc func, void *data,
337  int stacksize) override;
338  void killThread() override;
339  int waitThread(int block) override;
340  void waitForState(State) override;
341 
342  int suspendThread() override;
343  int restartThread() override;
344 
345  int64 getMemoryUsage(bool inclusive) const override
346  {
347  int64 mem = inclusive ? sizeof(*this) : 0;
348  // NOTE: We don't know how much memory Windows uses,
349  // so we can't count it.
350  return mem;
351  }
352 
353 protected:
354  void setState(State state) override;
355 };
356 
357 
359 {
360 public:
361  UT_ThreadSet(int nthreads=-1, int null_thread_if_1_cpu = 0);
362  ~UT_ThreadSet();
363 
365  {
366  myFunc = func;
367  }
368  void setUserData(void *user_data_array, size_t structlen)
369  {
370  myUserData = user_data_array;
371  myUserDataInc = structlen;
372  }
373  void setUserData(void *user_data)
374  {
375  myUserData = user_data;
376  myUserDataInc = 0;
377  }
378 
379  void reuse(UT_Thread::SpinMode spin_mode);
380  void go();
381  int wait(int block=1);
382 
383  int getNumThreads() const { return myThreadCount; }
384  UT_Thread *getThread(int which);
385  UT_Thread *operator[](int which)
386  {
387  UT_ASSERT_P(which < myThreadCount);
388  return myThreads[which];
389  }
390 
391 protected:
395  void *myUserData;
397 };
398 
400 {
401 public:
403  {
404  NON_BLOCKING = 0, // Only assign thread if one is available
405  BLOCKING = 1, // Block until a thread is free.
406  DYNAMIC = 2 // If no threads are availble, create a new one.
407  };
408 
409  // similar to UT_ThreadSet, but a bit simpler. Called UT_ThreadFarm
410  // because it farms out the next available thread. You also don't need to
411  // match the number of data chunks to the number of threads.
412  // ie.
413  // farm = new UT_ThreadFarm(4);
414  // while(!done) {
415  // thread = farm->nextThread();
416  // thread->startThread(entrypoint, mydata);
417  // }
418  // farm->wait();
419 
420  UT_ThreadFarm(int nthreads=-1);
421  ~UT_ThreadFarm();
422 
423  // waits for the next available thread, (or returns null if none are
424  // available and block = 0). thread_index will contain the thread index
425  // if you pass it a non-null pointer.
426  UT_Thread *nextThread(int *thread_index =0,
427  AssignmentStyle style = BLOCKING);
428 
429  // waits until all threads are finished (or, returns 0 if not finished and
430  // block = 0).
431  int wait(int block = 1);
432 
433  // deletes threads in the thread farm. if kill=1 the threads are killed before
434  // cleanup, otherwise wait(1) is called.
435  void cleanup(int kill = 0);
436 
437  int getEntries() const { return myThreadCount; }
439  {
440  UT_ASSERT_P(index < myThreadCount);
441  return myThreads[index];
442  }
443 
444 protected:
445  void addThreads(int thread_count);
446 
449 };
450 
451 // Gradual backoff when there's thread contention.
453 {
454 public:
455  UT_ThreadBackoff() : myCycles(1) {}
456 
457  static const uint cycles_for_noop = 4;
458  static const uint cycles_for_pause = cycles_for_noop * 4;
459  static const uint cycles_for_yield_higher = cycles_for_pause * 2;
460  static const uint cycles_for_yield_all = cycles_for_yield_higher * 2;
461 
462  // Same thresholds as hboost::detail::yield(), but different behaviour
463  void wait()
464  {
465  if (myCycles > cycles_for_yield_all)
466  {
467  // Yield the thread completely, to any and all comers.
468  UT_Thread::yield(false);
469  return;
470  }
471 
472  if (myCycles <= cycles_for_noop)
473  {
474  // Noop.
475  }
476  else if (myCycles <= cycles_for_pause)
477  {
478  UT_Thread::pause(myCycles);
479  }
480  else if (myCycles <= cycles_for_yield_higher)
481  {
482  UT_Thread::yield(true);
483  }
484  myCycles += (myCycles+1)>>1;
485  }
486 
487  void reset()
488  {
489  myCycles = 1;
490  }
491 
492 private:
493  uint myCycles;
494 };
495 
496 namespace UT
497 {
498 namespace detail
499 {
501 {
502 public:
503  ThreadInit(bool use_tbb);
504  ~ThreadInit();
505 
506  ThreadInit(const ThreadInit &) = delete;
507  ThreadInit &operator=(const ThreadInit &) = delete;
508 
509 private:
510  class InitTBB;
511  UT_UniquePtr<InitTBB> myInitTBB;
512 };
513 } // namespace detail
514 } // namespace UT
515 
516 template <bool UseTBB = true>
517 class UT_StdThread : public std::thread
518 {
519 public:
520  UT_StdThread() = default;
521  template <typename Func, typename... Args>
522  UT_StdThread(Func &&func, Args &&... args)
523  : std::thread(
524  WrapFunctor<Func, Args...>(std::forward<Func>(func)),
525  std::forward<Args>(args)...)
526  {
527  }
528 
529  UT_StdThread(const UT_StdThread&) = delete;
530  UT_StdThread& operator=(const UT_StdThread&) = delete;
531  UT_StdThread(UT_StdThread&&) = default;
532  UT_StdThread& operator=(UT_StdThread&&) = default;
533 
534 private:
535  template <typename Func, typename... Args>
536  class WrapFunctor
537  {
538  public:
539  WrapFunctor(Func&& func)
540  : myFunc(std::move(func))
541  {
542  }
543 
544  decltype(auto) operator()(Args&&... args) const
545  {
546  UT::detail::ThreadInit scope(UseTBB);
547  return myFunc(std::forward<Args>(args)...);
548  }
549  private:
550  Func myFunc;
551  };
552 };
553 
554 template <bool UseTBB = true>
556 {
557 public:
559 
560  explicit UT_StdThreadGroup(int nthreads = -1)
561  {
562  if (nthreads < 1)
563  nthreads = UT_Thread::getNumProcessors();
564 
565  myThreads.setSize(nthreads);
566  }
567 
568  UT_StdThreadGroup(const UT_StdThreadGroup&) = delete;
570 
571  thread_t& get(int idx)
572  {
573  return myThreads(idx);
574  }
575  const thread_t& get(int idx) const
576  {
577  return myThreads(idx);
578  }
580  {
581  return myThreads[idx];
582  }
583  const thread_t& operator[](int idx) const
584  {
585  return myThreads[idx];
586  }
587  bool joinable() const
588  {
589  for (auto&& t : myThreads)
590  {
591  if (!t.joinable())
592  return false;
593  }
594  return true;
595  }
596  bool joinable(int idx) const
597  {
598  return get(idx).joinable();
599  }
600  void join()
601  {
602  for (auto&& t : myThreads)
603  {
604  if (t.joinable())
605  t.join();
606  }
607  }
608 private:
609  UT_Array<thread_t> myThreads;
610 };
611 
612 // This function has been deprecated. Use SYSgetSTID instead.
613 static inline int SYS_DEPRECATED(12.5)
614 UTgetSTID()
615 {
617 }
618 
619 #endif
volatile State myState
Definition: UT_Thread.h:312
int getNumThreads() const
Definition: UT_Thread.h:383
void setUserData(void *user_data)
Definition: UT_Thread.h:373
#define SYS_DEPRECATED(__V__)
int64 getMemoryUsage(bool inclusive) const override
NOTE: This level doesn't own any data apart from itself.
Definition: UT_Thread.h:345
virtual int restartThread()=0
int myThreadCount
Definition: UT_Thread.h:392
void
Definition: png.h:1083
void *(* UTthreadFunc)(void *)
Definition: UT_Thread.h:54
UTthreadFunc myCallback
Definition: UT_Thread.h:314
UT_StdThreadGroup & operator=(const UT_StdThreadGroup &)=delete
UT_StdThread & operator=(const UT_StdThread &)=delete
UT_Thread * operator[](int index)
Definition: UT_Thread.h:438
SpinMode mySpinMode
Definition: UT_Thread.h:313
thread_t & operator[](int idx)
Definition: UT_Thread.h:579
UT_StdThread(Func &&func, Args &&...args)
Definition: UT_Thread.h:522
void * myCBData
Definition: UT_Thread.h:315
#define UT_API
Definition: UT_API.h:14
GLdouble GLdouble t
Definition: glew.h:1403
UT_Thread * operator[](int which)
Definition: UT_Thread.h:385
int getEntries() const
Definition: UT_Thread.h:437
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:33
UT_Thread ** myThreads
Definition: UT_Thread.h:393
virtual void setState(State state)=0
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:170
bool joinable() const
Definition: UT_Thread.h:587
int myThreadCount
Definition: UT_Thread.h:447
static int getNumProcessors()
virtual void waitForState(State desired)=0
void setFunc(UTthreadFunc func)
Definition: UT_Thread.h:364
virtual bool startThread(UTthreadFunc func, void *data, int stacksize)=0
long long int64
Definition: SYS_Types.h:116
virtual void killThread()=0
UT_StdThreadGroup(int nthreads=-1)
Definition: UT_Thread.h:560
int64 myUserDataInc
Definition: UT_Thread.h:396
const UT_TaskScope * myTaskScope
Definition: UT_Thread.h:317
bool myUsesTBB
Definition: UT_Thread.h:318
virtual int suspendThread()=0
GLboolean * data
Definition: glcorearb.h:130
*tasks wait()
**Note that the tasks the is the thread number *for the or if it s being executed by a non pool thread(this *can happen in cases where the whole pool is occupied and the calling *thread contributes to running the work load).**Thread pool.Have fun
static int isMainThread()
Definition: UT_Thread.h:116
GLenum func
Definition: glcorearb.h:782
UTthreadFunc myFunc
Definition: UT_Thread.h:394
const thread_t & operator[](int idx) const
Definition: UT_Thread.h:583
GLuint index
Definition: glcorearb.h:785
void yield() noexcept
Definition: thread.h:93
UT_Thread ** myThreads
Definition: UT_Thread.h:448
static void pause(uint cycles)
Definition: UT_Thread.h:155
**If you just want to fire and args
Definition: thread.h:615
int isActive()
Definition: UT_Thread.h:288
static void yield(bool higher_only=false)
virtual int waitThread(int block=1)=0
void * myUserData
Definition: UT_Thread.h:395
bool joinable(int idx) const
Definition: UT_Thread.h:596
UT_StdThread()=default
unsigned int uint
Definition: SYS_Types.h:45
void setUserData(void *user_data_array, size_t structlen)
Definition: UT_Thread.h:368