gwnavruntime/kernel/SF_Threads.h Source File

SF_Threads.h
Go to the documentation of this file.
1 /*
2  * Copyright 2016 Autodesk, Inc. All rights reserved.
3  * Use of this software is subject to the terms of the Autodesk license agreement and any attachments or Appendices thereto provided at the time of installation or download,
4  * or which otherwise accompanies this software in either electronic or hard copy form, or which is signed by you and accepted by Autodesk.
5  */
6 
7 #pragma once
8 
13 
14 #include <thread>
15 
16 // Defines the infinite wait delay timeout
17 #define KY_WAIT_INFINITE 0xFFFFFFFF
18 
19 namespace Kaim
20 {
21 
22 // ****** Declared classes
23 
24 // To be defined in the project configuration options
25 
26 // Declared with thread support only:
27 class Waitable;
28 class AcquireInterface;
29 class Mutex;
30 class WaitCondition;
31 class Event;
32 class Semaphore;
33 
34 // The rest of the classes are only defined when thread support is enabled.
35 
36 // ***** Waitable and Acquisition Interface
37 
38 /*
39  AcquireInterface provides a two-stage acquisition interface for a resource. This interface is used
40  primarily to acquire multiple objects that can be shared among threads. For this reason,
41  AcquireInterface is often used together with Waitable. Whenever a waitable object changes state
42  indicating that a shared resource is potentially available, this abstract acquisition interface is used to
43  acquire the resource in a resource-independent manner.
44 
45  In the first stage of acquisition TryAcquire is called to see if the resource can be obtained. If it succeeds,
46  the resource is considered to be acquired, and TryAcquireCommit is called to confirm the ownership. If the
47  resource is, however, not needed (because, for example, acquisition of another object in a multi-object wait
48  has failed) TryAcquireCancel can be called.
49 */
50 
51 class AcquireInterface
52 {
53 public:
54  virtual ~AcquireInterface() {}
55 
56  // Call this function to test if an object can be acquired
57  // Like TryAcquire() and TryAcquireCancel() pair, except does not actually try to acquire the object
58  // This function is called from a different thread then the acquisition waiting thread, it is used to check
59  // for potential availability of resource during Wait/Acquire functions
60  virtual bool CanAcquire();
61 
62  // These functions implement a two-stage acquiring protocol used by the AcquireMultiple functions.
63  // First, TryAcquire is called to capture resource. If it succeeded,
64  // TryAcquireCommit is called to permanently obtain the resource.
65  // TryAcquireCancel can be called instead of TryAcquireCommit
66  // in order to release the resource, and leave it untouched.
67  // These functions are only called from the waiter thread by the AcquireMultiple functions.
68 
69  // Try to acquire the resource, return 1 if succeeded.
70  virtual bool TryAcquire();
71 
72  // Complete resource acquisition. Should not fail in general.
73  virtual bool TryAcquireCommit();
74 
75  // Cancel resource acquisition. Should not fail in general.
76  virtual bool TryAcquireCancel();
77 
78  // Static function to acquire multiple objects simultaneously
79  static bool AcquireMultipleObjects(Waitable** waitList, unsigned waitCount, unsigned delay = KY_WAIT_INFINITE);
80 
81  // Static function to acquire one of multiple objects in a list
82  // Returns the index of the object acquired, -1 for fail (wait expired)
83  static int AcquireOneOfMultipleObjects(Waitable** waitList, unsigned waitCount, unsigned delay = KY_WAIT_INFINITE);
84 };
85 
86 // Static acquire interface that doesn't do anything
87 // All acquire calls on this interface always succeed.
88 
89 class DefaultAcquireInterface : public AcquireInterface
90 {
91 public:
92  virtual ~DefaultAcquireInterface() {}
93 
94  // Does nothing
95 
96  static DefaultAcquireInterface* GetDefaultAcquireInterface();
97 };
98 
99 /*
100  Waitable interface is used for thread-shared objects that need to be waited on. A waitable object
101  has an associated 'Signalled' state such that an object is signaled when its associated resource is available,
102  and non-signaled when it is not available. Some objects, such as a Semaphore, can have multiple internal signaled
103  states; in this case the object is considered to be signaled if ANY amount of a resource is available, and
104  non-signaled if NO resource is available.
105 
106  Waiting on a Waitable object is achieved with help of handlers installed by the waiter threads. Whenever the
107  objects state changes from non-signaled to signaled, or from one internal gradation of signaled to another,
108  wait handlers are called (handlers should not get called when the object changes its state from signaled to
109  non-signaled). When called, wait handlers can be used to wake up a waiter thread, so that it can compete for the
110  acquisition of the object's associated resource. The actual acquisition of the resource can happen either through
111  object-specific methods for with help of an associated AcquireInterface.
112 */
113 
114 class Waitable : public RefCountBase<Waitable, Stat_Default_Mem>
115 {
116 public:
117  // Wait handler type
118  typedef void (*WaitHandler)(void* pdata);
119 
120 protected:
121  class HandlerStruct
122  {
123  public:
124  WaitHandler Handler;
125  void* pUserData;
126 
127  // Default constructor - only used in garray.
128  HandlerStruct()
129  {
130  Handler = 0;
131  pUserData = 0;
132  }
133 
134  HandlerStruct(WaitHandler h, void* pd)
135  {
136  Handler = h;
137  pUserData = pd;
138  }
139  HandlerStruct(const HandlerStruct& src)
140  {
141  Handler = src.Handler;
142  pUserData = src.pUserData;
143  }
144 
145  HandlerStruct& operator=(const HandlerStruct& src)
146  {
147  Handler = src.Handler;
148  pUserData = src.pUserData;
149  return *this;
150  }
151 
152  bool operator==(const HandlerStruct& src) { return (Handler == src.Handler) && (pUserData == src.pUserData); }
153  };
154 
155  // Handler array and lock are allocated in a separate ref-counted object
156  // so that their lifetime can be extended past the waitable object in
157  // case other threads are still executing wait handlers. HandlerArray is
158  // only allocated if multiWait was passed in a contsructor; otherwise
159  // it is null.
160  struct HandlerArray : public NewOverrideBase<Stat_Default_Mem>
161  {
162  typedef ArrayConstPolicy<0, 16, true> SizePolicyType; // MinCapacity=0, Granularity=16, NeverShrink
163  typedef Array<HandlerStruct, Stat_Default_Mem, SizePolicyType> HandlerArrayType;
164 
165  std::atomic<SInt32> RefCount;
166  HandlerArrayType Handlers;
167  Lock HandlersLock;
168 
169  HandlerArray() { RefCount = 1; }
170 
171  void AddRef() { RefCount++; }
172 
173  // Decrement ref count. This needs to be thread-safe, since
174  // a different thread could have also decremented the ref count.
175  void Release();
176 
177  // Calls all of the installed handlers during a lock.
178  void CallWaitHandlers();
179  };
180 
181  // A pointer to handler array, only allocated if 'enabled' flag was passed
182  // in a constructor. Such setup is done to save memory and improve performance
183  // of non-multiwait Mutex. Unfortunately, lazy initialization can not
184  // be used for pHandlers, since access of this variable must be done before
185  // system mutex Unlock, and thus can occur before AddHandlers in multi-object wait.
186  HandlerArray* pHandlers;
187 
188 public:
189  // Enable flag must be passed to enable WaitHandlers; if not specified the
190  // wait handlers and multi-object wait functionality is not available.
191  Waitable(bool enable);
192 
193  virtual ~Waitable();
194 
195  // Invoke the associated wait handlers; only safe if we know Waitable could not
196  // have died in response to it being signaled (otherwise GetCallableHandlers should be used).
197  void CallWaitHandlers();
198 
199  // A handle used to access wait handlers after waitable has been signaled
200  // and thus can not be accessed directly (other threads can kill the object).
201  class CallableHandlers
202  {
203  friend class Waitable;
204  Ptr<HandlerArray> pArray;
205 
206  public:
207  CallableHandlers() {}
208 
209  // Calls handlers in a handle.
210  inline void CallWaitHandlers()
211  {
212  if (pArray)
213  pArray->CallWaitHandlers();
214  }
215  };
216 
217  inline void GetCallableHandlers(CallableHandlers* ph)
218  {
219  if (pHandlers)
220  ph->pArray = pHandlers;
221  }
222 
223  // Register a handler to be notified when the wait is finished.
224  // The wait may be called on a different thread and is
225  // usually responsible for waking the waiting thread.
226  bool AddWaitHandler(WaitHandler handler, void* pdata);
227  bool RemoveWaitHandler(WaitHandler handler, void* pdata);
228 
229  // Wait for this object to become signaled
230  bool Wait(unsigned delay = KY_WAIT_INFINITE);
231 
232  // Acquires the current object based on associated acquisition interface
233  bool Acquire(unsigned delay = KY_WAIT_INFINITE);
234 
235  // Returns 1 if the object is currently signaled, i.e. the associated
236  // data is available and no wait is required. Note that this state can change
237  // asynchronously based on behavior of other threads.
238  virtual bool IsSignaled() const;
239 
240  // Obtain the acquisition interface
241  virtual AcquireInterface* GetAcquireInterface();
242 };
243 
244 // ***** Mutex Class
245 
246 // Mutex class represents a Mutex??a synchronization object that provides access
247 // serialization between different threads, allowing one thread mutually exclusive access
248 // to a resource.
249 
250 class MutexImpl;
251 
252 class Mutex : public Waitable, public AcquireInterface
253 {
254  friend class WaitConditionImpl;
255  friend class MutexImpl;
256 
257  // Internal mutex structures
258  MutexImpl* pImpl;
259 
260 public:
261  // Constructor/destructor
262  Mutex(bool recursive = 1, bool multiWait = 0);
263  ~Mutex();
264 
265  // Locking functions
266  void DoLock();
267  bool TryLock();
268  void Unlock();
269 
270  // Returns 1 if the mutes is currently locked by another thread
271  // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired.
272  bool IsLockedByAnotherThread();
273 
274  // ** Waitable overrides
275  // A mutex is only signaled if it is not locked by ANYONE, so IsSignalled() will
276  // return 0 even if it is locked by US. This means that we cannot call Wait() on
277  // a mutex that is locked by us; it will never return. However, we can call Acquire() instead.
278  virtual bool IsSignaled() const;
279  virtual AcquireInterface* GetAcquireInterface();
280 
281  // ** Acquire Interface implementation
282  virtual bool CanAcquire();
283  virtual bool TryAcquire();
284  virtual bool TryAcquireCommit();
285  virtual bool TryAcquireCancel();
286 
287  // Locker class
288  // Used for automatic locking of a mutex
289  class Locker
290  {
291  public:
292  Mutex* pMutex;
293  Locker(Mutex* pmutex)
294  {
295  pMutex = pmutex;
296  pMutex->DoLock();
297  }
298  ~Locker() { pMutex->Unlock(); }
299  };
300 };
301 
302 // ***** WaitCondition
303 
304 /*
305  WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor.
306  Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that
307  call Notify() or NotifyAll().
308 
309  The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then
310  starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same
311  resource, this ensures that any condition checked for while the mutex was locked does not change before
312  the wait on the condition is actually initiated.
313 */
314 
315 class WaitConditionImpl;
316 
317 class WaitCondition
318 {
319  friend class WaitConditionImpl;
320  // Internal implementation structure
321  WaitConditionImpl* pImpl;
322 
323 public:
324  // Constructor/destructor
325  WaitCondition();
326  ~WaitCondition();
327 
328  // Release mutex and wait for condition. The mutex is re-aquired after the wait.
329  // Delay is specified in milliseconds (1/1000 of a second).
330  bool Wait(Mutex* pmutex, unsigned delay = KY_WAIT_INFINITE);
331 
332  // Notify a condition, releasing at one object waiting
333  void Notify();
334 
335  // Notify a condition, releasing all objects waiting
336  void NotifyAll();
337 };
338 
339 // ***** Event
340 
341 class Event : public Waitable, public AcquireInterface
342 {
343  // Event state, its mutex and the wait condition
344  volatile bool State;
345  volatile bool Temporary;
346  mutable Mutex StateMutex;
347  WaitCondition StateWaitCondition;
348 
349 public:
350  // Constructor/destructor
351  Event(bool setInitially = 0, bool multiWait = 0);
352  ~Event();
353 
354  // Wait on an event condition until it is set
355  // Delay is specified in milliseconds (1/1000 of a second).
356  bool Wait(unsigned delay = KY_WAIT_INFINITE);
357 
358  // Set an event, releasing objects waiting on it
359  void SetEvent();
360 
361  // Reset an event, un-signaling it
362  void ResetEvent();
363 
364  // Set and then reset an event once a waiter is released.
365  // If threads are already waiting, they will be notified and released
366  // If threads are not waiting, the event is set until the first thread comes in
367  void PulseEvent();
368 
369  // Signaled override, an event is signaled once it is set
370  virtual bool IsSignaled() const;
371 
372  virtual AcquireInterface* GetAcquireInterface();
373 
374  // Acquire interface implementation
375  virtual bool CanAcquire();
376  virtual bool TryAcquire();
377  virtual bool TryAcquireCommit();
378  virtual bool TryAcquireCancel();
379 };
380 
381 // ***** Semaphore
382 
383 // The Semaphore class represents a Semaphore??a synchronization object
384 // that allows a limited number of threads in one or more processes to access
385 // a resource. A Semaphore object maintains a count of the number of
386 // threads currently accessing a specified resource. The Semaphore class is
387 // primarily used to serialize thread execution, similar to Mutex,
388 // however, unlike a mutex, a semaphore can be accessed by more than one thread
389 // at a time.
390 
391 class Semaphore : public Waitable, public AcquireInterface
392 {
393 protected:
394  // Event state, its mutex and the wait condition
395  int MaxValue;
396  volatile int Value;
397  mutable Mutex ValueMutex;
398  WaitCondition ValueWaitCondition;
399 
400 public:
401  // Constructor/destructor
402  Semaphore(int maxValue = 1, bool multiWait = 0);
403  ~Semaphore();
404 
405  // Get current value and max
406  int GetMaxValue() const;
407  int GetValue() const;
408  int GetAvailable() const;
409 
410  // *** Actions
411 
412  // Obtains multiple value of a semaphore.
413  // Returns 0 if query failed (count > max value error or timeout)
414  // Delay is specified in milliseconds (1/1000 of a second).
415  bool ObtainSemaphore(int count = 1, unsigned delay = KY_WAIT_INFINITE);
416 
417  // Release semaphore values
418  // Returns success code
419  bool ReleaseSemaphore(int count = 1);
420 
421  // *** Operators
422 
423  // Postfix increment/decrement, return value before operation
424  int operator++(int);
425  int operator--(int);
426  // Postfix increment/decrement, return value before operation
427  int operator+=(int count);
428  int operator-=(int count);
429 
430  // *** Waitable objects
431 
432  // Create a semaphore acquisition object that would increment a semaphore by a user-defined count
433  // This object can be passed to AcquireMultipleObjects functions,
434  // and will acquire several values form a semaphore
435  // This object must be released before the semaphore
436  Waitable* CreateWaitableIncrement(int count);
437 
438  // Acquire interface implementation
439  // Default
440  virtual bool CanAcquire();
441  virtual bool TryAcquire();
442  virtual bool TryAcquireCommit();
443  virtual bool TryAcquireCancel();
444 
445  // GWaitable implementation
446  virtual bool IsSignaled() const;
447  virtual AcquireInterface* GetAcquireInterface();
448 
449  // Locker class, used for automatic acquisition of a semaphore
450  class Locker
451  {
452  public:
453  Semaphore* pSemaphore;
454  int Count;
455  Locker(Semaphore* psemaphore, int count = 1)
456  {
457  pSemaphore = psemaphore;
458  Count = count;
459  pSemaphore->ObtainSemaphore(count);
460  }
461  ~Locker() { pSemaphore->ReleaseSemaphore(Count); }
462  };
463 };
464 
465 // ***** Thread class
466 
467 // ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and Thread::GetThreadId().
468 typedef size_t ThreadId;
469 
470 // *** Thread flags
471 
472 // Indicates that the thread is has been started, i.e. Start method has been called, and threads OnExit() method has not yet been called/returned.
473 #define KY_THREAD_STARTED 0x01
474 
475 // This flag is set once the thread has ran, and finished.
476 #define KY_THREAD_FINISHED 0x02
477 
478 // This flag is set temporarily if this thread was started suspended. It is used internally.
479 #define KY_THREAD_START_SUSPENDED 0x08
480 
481 // This flag is used to ask a thread to exit. Message driven threads will usually check this flag and finish once it is set.
482 #define KY_THREAD_EXIT 0x10
483 
484 // NOTE: Waitable must be the first base since it implements RefCountImpl.
485 class Thread : public Waitable, public AcquireInterface
486 {
487 public:
488  // *** Callback functions, can be used instead of overriding Run
489 
490  // Run function prototypes.
491  // Thread function and user handle passed to it, executed by the default
492  // Thread::Run implementation if not null.
493  typedef int (*ThreadFn)(Thread* pthread, void* h);
494 
495  // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried
496  ThreadFn ThreadFunction;
497 
498  // User handle passes to a thread
499  void* UserHandle;
500 
501  // Thread state to start a thread with
502  enum ThreadState
503  {
504  NotRunning = 0,
505  Running = 1,
506  Suspended = 2
507  };
508 
509  // Thread priority
510  enum ThreadPriority
511  {
512  CriticalPriority,
513  HighestPriority,
514  AboveNormalPriority,
515  NormalPriority,
516  BelowNormalPriority,
517  LowestPriority,
518  IdlePriority,
519  };
520 
521  // Thread constructor parameters
522  struct CreateParams
523  {
524  CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority)
525  : threadFunction(func), userHandle(hand), stackSize(ssize), processor(proc), initialState(state), priority(prior)
526  {
527  }
528 
529  ThreadFn threadFunction; // Thread function
530  void* userHandle; // User handle passes to a thread
531  UPInt stackSize; // Thread stack size
532  int processor; // Thread hardware processor
533  ThreadState initialState; //
534  ThreadPriority priority; // Thread priority
535  };
536 
537  // *** Constructors
538 
539  // A default constructor always creates a thread in NotRunning state, because
540  // the derived class has not yet been initialized. The derived class can call Start explicitly.
541  // "processor" parameter specifies which hardware processor this thread will be run on.
542  // -1 means OS decides this. Implemented only from Win32 and XBox360
543  Thread(UPInt stackSize = 128 * 1024, int processor = -1);
544 
545  // Constructors that initialize the thread with a pointer to function.
546  // An option to start a thread is available, but it should not be used if classes are derived from Thread.
547  // "processor" parameter specifies which hardware processor this thread will be run on.
548  // -1 means OS decides this. Implemented only from Win32 and XBox360
549  Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, int processor = -1, ThreadState initialState = NotRunning);
550 
551  // Constructors that initialize the thread with a create parameters structure.
552  explicit Thread(const CreateParams& params);
553 
554  // Destructor.
555  virtual ~Thread();
556 
557  // Waits for all Threads to finish; should be called only from the root
558  // application thread. Once this function returns, we know that all other
559  // thread's references to Thread object have been released.
560  static void FinishAllThreads();
561 
562  // *** Overridable Run function for thread processing
563 
564  // - returning from this method will end the execution of the thread
565  // - return value is usually 0 for success
566  virtual int Run();
567 
568  // Called after return/exit function
569  virtual void OnExit();
570 
571  // *** Thread management
572 
573  // Starts the thread if its not already running
574  // - internally sets up the threading and calls Run()
575  // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing
576  // - returns the exit code
577  virtual bool Start(ThreadState initialState = Running);
578 
579  // Quits with an exit code
580  virtual void Exit(int exitCode = 0);
581 
582  // *** Thread status query functions
583 
584  bool GetExitFlag() const;
585  void SetExitFlag(bool exitFlag);
586 
587  // Determines whether the thread was running and is now finished
588  bool IsFinished() const;
589 
590  // Determines if the thread is currently suspended
591  bool IsSuspended() const;
592 
593  // Returns current thread state
594  ThreadState GetThreadState() const;
595 
596  // Returns the number of available CPUs on the system
597  static int GetCPUCount();
598 
599  // Returns the thread exit code. Exit code is initialized to 0,
600  // and set to the return value if Run function after the thread is finished.
601  inline int GetExitCode() const { return ExitCode; }
602 
603  std::thread* GetOSHandle() const { return ThreadHandle; }
604 
605  ThreadId GetThreadId() const { return IdValue != std::thread::id() ? std::hash<std::thread::id>()(std::this_thread::get_id()) : 0; }
606 
607  // Sleep secs seconds
608  static bool Sleep(unsigned secs);
609 
610  // Sleep msecs milliseconds
611  static bool MSleep(unsigned msecs);
612 
613  // *** Waitable interface implementation
614 
615  // A thread is signaled once it has finished. Waitable interface can be used to wait for a thread to finish.
616  virtual bool IsSignaled() const { return (ThreadFlags & KY_THREAD_FINISHED) != 0; }
617 
618  // A thread can be acquired if it has finished.
619  // However, the acquisition of a thread does not require any release logic.
620  virtual AcquireInterface* GetAcquireInterface() { return this; }
621 
622  // *** Acquire interface implementation
623  virtual bool CanAcquire() { return IsSignaled(); }
624  virtual bool TryAcquire() { return IsSignaled(); }
625 
626  virtual void SetThreadName(const char* name) { KY_UNUSED(name); }
627 
628  friend void Thread_Std11StartFunc(void* phandle);
629 
630 public:
631  // Thread state flags
632  std::atomic<UInt32> ThreadFlags;
633  std::atomic<SInt32> SuspendCount;
634  UPInt StackSize;
635 
636  // Hardware processor which this thread is running on.
637  int Processor;
638  ThreadPriority Priority;
639 
640  // System-specific cleanup function called from destructor
641  void CleanupSystemThread();
642 
643  std::thread* ThreadHandle;
644  std::thread::id IdValue;
645 
646  // Exit code of the thread, as returned by Run.
647  int ExitCode;
648 
649  // Internal run function.
650  int PRun();
651 
652  // Finishes the thread and releases internal reference to it.
653  void FinishAndRelease();
654 
655  void Init(const CreateParams& params);
656 
657  // Protected copy constructor
658  Thread(const Thread& source) : Waitable(1) { KY_UNUSED(source); }
659 };
660 
661 // Returns the unique Id of a thread it is called on, intended for comparison purposes.
662 ThreadId GetCurrentThreadId();
663 
664 }
The Autodesk Navigation namespace.
Definition: gamekitcrowddispersion.cpp:17