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