You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

threading.hpp 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. // threading.hpp
  2. //
  3. // Copyright (C) 2004-2020 MicroNeil Research Corporation.
  4. //
  5. // This software is released under the MIT license. See LICENSE.TXT.
  6. // The "Threading" module is a basic, cross-platform, multi-threading tool kit.
  7. // The differences between posix compatible systems and win32 based systems are
  8. // abstracted. On win32 systems, native win32 primatives are used to construct.
  9. // efficient, lightweight objects.
  10. // On others we assume we can use pthreads. In either case the objects we have
  11. // here are designed to cover all of the basics efficiently while hiding the
  12. // required under-cover work.
  13. #pragma once
  14. #include <set>
  15. #include <vector>
  16. #include <string>
  17. #include <queue>
  18. #include "faults.hpp"
  19. namespace codedweller {
  20. class ThreadManager; // ThreadManager does exist.
  21. extern ThreadManager Threads; // Master thread manager.
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // Thread Status & Type
  24. //
  25. // ThreadState objects are constant static objects defined for each Thread class
  26. // so that the thread can update it's state by changing a pointer. The state
  27. // can then be compared between threads of the same type and can be read-out
  28. // as text for debugging purposes.
  29. class ThreadState { // Thread State Object.
  30. public:
  31. const std::string Name; // Text name of thread descriptor.
  32. ThreadState(std::string N) : Name(N) {} // Constructor requires text name.
  33. };
  34. // ThreadType objects are constant static objects defined for each Thread class
  35. // so that classes can be identified by type using a pointer to the constant.
  36. class ThreadType {
  37. public:
  38. const std::string Name;
  39. ThreadType(std::string N) : Name(N) {}
  40. };
  41. class Thread; // There is such thing as a Thread.
  42. class ThreadStatusRecord { // Describes a Thread's condition.
  43. private:
  44. Thread* Pointer; // A pointer to the thread.
  45. ThreadType* Type; // A descriptor of it's type.
  46. ThreadState* State; // A descriptor of it's state.
  47. std::string Name; // Name of the thread if any.
  48. bool isRunning; // True if the thread is running.
  49. bool isBad; // True if the thread is bad.
  50. std::string Fault; // Bad Thread's Fault if any.
  51. public:
  52. ThreadStatusRecord( // Initialize all items.
  53. Thread* P,
  54. ThreadType& T,
  55. ThreadState& S,
  56. bool R,
  57. bool B,
  58. std::string F,
  59. std::string N
  60. ) :
  61. Pointer(P),
  62. Type(&T),
  63. State(&S),
  64. Name(N),
  65. isRunning(R),
  66. isBad(B),
  67. Fault(F)
  68. {}
  69. ThreadStatusRecord& operator=(const ThreadStatusRecord& Right) { // Minimal Assignment Operator
  70. Pointer = Right.Pointer;
  71. Type = Right.Type;
  72. State = Right.State;
  73. isRunning = Right.isRunning;
  74. isBad = Right.isBad;
  75. Fault = Right.Fault;
  76. Name = Right.Name;
  77. return *this;
  78. }
  79. bool operator<(const ThreadStatusRecord& Right) { // Minimal Comparison Operator.
  80. return (Pointer < Right.Pointer);
  81. }
  82. // How to get the details of the report.
  83. const Thread* getPointer() { return Pointer; }
  84. const ThreadType& getType() { return *Type; }
  85. const ThreadState& getState() { return *State; }
  86. bool getRunning() { return isRunning; }
  87. bool getBad() { return isBad; }
  88. std::string getFault() { return Fault; }
  89. std::string getName() { return Name; }
  90. };
  91. typedef std::vector<ThreadStatusRecord> ThreadStatusReport; // Status report type.
  92. // End ThreadDescriptor
  93. ////////////////////////////////////////////////////////////////////////////////
  94. } // End namespace codedweller
  95. ////////////////////////////////////////////////////////////////////////////////
  96. // Win32 / POSIX abstractions
  97. #ifdef WIN32
  98. // When in WIN32 land...
  99. // Remember to compile (on GNU anyway) with -mthreads
  100. #include <windows.h>
  101. #include <process.h>
  102. namespace codedweller {
  103. typedef HANDLE thread_primative; // The WIN32 thread primative abstracts
  104. // HANDLE
  105. typedef HANDLE mutex_primative; // The WIN32 mutex primative abstracts
  106. // a HANDLE to a Semaphore.
  107. inline void threading_yield() { // When we want to yield time in WIN32
  108. SwitchToThread(); // we call SwitchToThread();
  109. }
  110. } // End namespace codedweller
  111. #else
  112. // When in POSIX land...
  113. // Remember to compile (on GMU anyway) with -pthread
  114. #include <pthread.h>
  115. #include <sched.h>
  116. namespace codedweller {
  117. typedef pthread_t thread_primative; // The POSIX thread primative abstracts
  118. // pthread_t
  119. typedef pthread_mutex_t mutex_primative; // The POSIX mutex primative abstracts
  120. // pthread_mutex_t
  121. inline void threading_yield() { // When we want to yield time in POSIX
  122. sched_yield(); // we call sched_yield();
  123. }
  124. } // End namespace codedweller
  125. #endif
  126. // End Win32 / POSIX abstractions
  127. ////////////////////////////////////////////////////////////////////////////////
  128. namespace codedweller {
  129. ////////////////////////////////////////////////////////////////////////////////
  130. // The Thread class gets extended to do any specific work. The pure virtual
  131. // function MyTask is overloaded by the derived class to define that work. It
  132. // is expected that the class will be initialized with any parameters that
  133. // will be used by the thread and that the thread will make available any
  134. // results through public interfaces either during and/or after the thread
  135. // has finished running.
  136. class Thread {
  137. private:
  138. ThreadState* MyThreadState; // Track current thread state.
  139. protected:
  140. const ThreadType& MyThreadType; // Identify thread type.
  141. const std::string MyThreadName; // Name string of this instance.
  142. thread_primative MyThread; // Abstracted thread.
  143. bool RunningFlag; // True when thread is in myTask()
  144. bool BadFlag; // True when myTask() throws!
  145. std::string BadWhat; // Bad exception what() if any.
  146. void CurrentThreadState(const ThreadState& TS); // Set thread state.
  147. public:
  148. Thread(); // Constructor (just in case)
  149. Thread(const ThreadType& T, std::string N); // Construct with specific Type/Name
  150. virtual ~Thread(); // Destructor (just in case)
  151. void run(); // Method to launch this thread.
  152. void join(); // Method to Join this thread.
  153. void launchTask(); // Launch and watch myTask().
  154. virtual void myTask() = 0; // The actual task must be overloaded.
  155. thread_primative getMyThread(); // Inspect my thread primative.
  156. bool isRunning(); // Return the Running flag state.
  157. bool isBad(); // Return the Bad flag state.
  158. const std::string MyFault(); // Return exception Bad fault if any.
  159. const std::string MyName(); // The thread's name.
  160. const ThreadType& MyType(); // Thread type for this thread.
  161. const ThreadState& MyState(); // Returns the current thread state.
  162. const ThreadState& CurrentThreadState(); // Returns the current thread state.
  163. ThreadStatusRecord StatusReport(); // Return's the thread's status reprt.
  164. // Constants for Thread...
  165. const static ThreadType Type; // The thread's type.
  166. const static ThreadState ThreadInitialized; // Constructed successfully.
  167. const static ThreadState ThreadStarted; // Started.
  168. const static ThreadState ThreadFailed; // Failed by unhandled exception.
  169. const static ThreadState ThreadStopped; // Stopped normally.
  170. const static ThreadState ThreadDestroyed; // Safety value for destructed Threads.
  171. };
  172. // End Thread
  173. ////////////////////////////////////////////////////////////////////////////////
  174. ////////////////////////////////////////////////////////////////////////////////
  175. // The Mutex class abstracts a lightweight, very basic mutex object.
  176. // As with the Thread object, more ellaborate forms can be built up from
  177. // this basic mechanism. An important design constraint for this basic
  178. // mutex object is that it work even if the thread that's running was not
  179. // created with the Thread object... that ensures that it can be used in
  180. // code that is destined to function in other applications.
  181. class Mutex {
  182. private:
  183. mutex_primative MyMutex; // Here is our primative mutex.
  184. volatile bool IAmLocked; // Here is our Lock Count.
  185. public:
  186. Mutex(); // Construct the mutex.
  187. ~Mutex(); // Destroy the mutex.
  188. void lock(); // Lock it.
  189. void unlock(); // Unlock it.
  190. bool tryLock(); // Try to lock it.
  191. bool isLocked(); // Check to see if it's locked.
  192. };
  193. // End of Mutex
  194. ////////////////////////////////////////////////////////////////////////////////
  195. ////////////////////////////////////////////////////////////////////////////////
  196. // ScopeMutex
  197. // A ScopeMutex is a nifty trick for locking a mutex during some segment of
  198. // code. On construction, it locks the Mutex that it is given and keeps it
  199. // locked until it is destroyed. Of course this also means that it will unlock
  200. // the mutex when it goes out of scope - which is precisely the point :-)
  201. //
  202. // The right way to use a ScopeMutex is to create it just before you need to
  203. // have control and then forget about it. From a design perspective, you might
  204. // want to make sure that whatever happens after the ScopeMutex has been
  205. // created is as short as possible and if it is not then you may want to
  206. // use the Mutex directly.
  207. //
  208. // The best place to use a ScopeMutex is where you might leave the controling
  209. // bit of code through a number of logical paths such as a logic tree or even
  210. // due to some exceptions. In this context it saves you having to track down
  211. // all of the possible cases and unlock the mutex in each of them.
  212. class ScopeMutex {
  213. private:
  214. Mutex& MyMutex; // ScopeMutex has an ordinary Mutex to use.
  215. public:
  216. ScopeMutex(Mutex& M); // Constructing a ScopeMutex requires a Mutex
  217. ~ScopeMutex(); // We do have special code for descrution.
  218. };
  219. // End ScopeMutex
  220. ////////////////////////////////////////////////////////////////////////////////
  221. ////////////////////////////////////////////////////////////////////////////////
  222. // ProductionGateway
  223. // A ProductionGateway encapsulates the thread synchronization required for a
  224. // producer / consumer relationship. For each call to the produce() method one
  225. // call to the consume() method can proceed. The object takes into account that
  226. // these methods may be called out of sequence and that, for example, produce()
  227. // might be called several times before any calls to consume.
  228. #ifdef WIN32
  229. // Win32 Implementation ////////////////////////////////////////////////////////
  230. class ProductionGateway {
  231. private:
  232. HANDLE MySemaphore; // WIN32 makes this one easy w/ a 0 semi.
  233. public:
  234. ProductionGateway(); // The constructor and destructor handle
  235. ~ProductionGateway(); // creating and destroying the semi.
  236. void produce(); // Produce "releases" the semi.
  237. void consume(); // Consume "waits" if needed.
  238. };
  239. #else
  240. // POSIX Implementation ////////////////////////////////////////////////////////
  241. class ProductionGateway { // Posix needs a few pieces for this.
  242. private:
  243. mutex_primative MyMutex; // Mutex to protect the data.
  244. pthread_cond_t MyConditionVariable; // A condition variable for signaling.
  245. int Product; // A count of unused calls to produce()
  246. int Waiting; // A count of waiting threads.
  247. int Signaled; // A count of signaled threads.
  248. public:
  249. ProductionGateway(); // The constructor and destructor handle
  250. ~ProductionGateway(); // creating and destroying the semi.
  251. void produce(); // Produce "releases" the semi.
  252. void consume(); // Consume "waits" if needed.
  253. };
  254. #endif
  255. // End ProductionGateway
  256. ////////////////////////////////////////////////////////////////////////////////
  257. ////////////////////////////////////////////////////////////////////////////////
  258. // The ThreadManager class provides a global thread management tool. All Thread
  259. // objects register themselves with the Threads object upon construction and
  260. // remove themselves from the registry upon destruction. The Threads object can
  261. // produce a status report for all of the known threads on the system and can
  262. // temporarily lock the existing thread so that it can be contacted reliably.
  263. // locking and unlocking the ThreadManager is intended only for short messages
  264. // that set flags in the thread or pass some small data packet. The lock only
  265. // prevents the thread from being destroyed before the message can be sent so
  266. // that the thread that owns the threadlock will not make any calls to a dead
  267. // pointer. Most apps should be designed so that the threadlock mechanism is
  268. // not required.
  269. class ThreadManager { // Central manager for threads.
  270. friend class Thread; // Threads are friends.
  271. private:
  272. Mutex MyMutex; // Protect our data with this.
  273. std::set<Thread*> KnownThreads; // Keep track of all threads.
  274. void rememberThread(Thread* T); // Threads register themselves.
  275. void forgetThread(Thread* T); // Threads remove themselves.
  276. Thread* LockedThread; // Pointer to locked thread if any.
  277. public:
  278. ThreadManager():LockedThread(0){} // Initialize nice and clean.
  279. ThreadStatusReport StatusReport(); // Get a status report.
  280. bool lockExistingThread(Thread* T); // Locks ThreadManager if T exists.
  281. void unlockExistingThread(Thread* T); // Unlocks ThreadManager if T locked.
  282. };
  283. class ScopeThreadLock { // This is like a ScopeMutex for
  284. private: // the ThreadManager.
  285. Thread* MyLockedThread; // It needs to know it's Thread.
  286. public:
  287. ScopeThreadLock(Thread* T); // Locks T in ThreadManager if it can.
  288. ~ScopeThreadLock(); // Unlocks T in ThreadManager if locked.
  289. bool isGood(); // True if T was locked.
  290. bool isBad(); // False if T was not locked.
  291. };
  292. // End Thread Manager
  293. ////////////////////////////////////////////////////////////////////////////////
  294. ////////////////////////////////////////////////////////////////////////////////
  295. // A ProductionQueue is a templated, thread safe mechanism for implementing
  296. // a producer/consumer relationship. The objects in the queue should be simple
  297. // data so that they can be created, destroyed, and copied without trouble. Put
  298. // another way - the objects in the ProductionQueue should be lightweight
  299. // handles for other things. Those things should be created and destroyed
  300. // elsewhere.
  301. template<typename T> // Templatized
  302. class ProductionQueue { // Production Queue Class
  303. private:
  304. Mutex myMutex; // Contains a mutex and
  305. volatile unsigned int LatestSize; // a volatile (blinking light) size
  306. ProductionGateway myGateway; // integrated with a production
  307. std::queue<T> myQueue; // gateway and a queue.
  308. public:
  309. ProductionQueue() : LatestSize(0) {} // The size always starts at zero.
  310. T take() { // To consume a queued object
  311. myGateway.consume(); // we wait on the production gateway
  312. ScopeMutex OneAtATimePlease(myMutex); // and when we get through we lock
  313. T O = myQueue.front(); // the mutext, take the object on the
  314. myQueue.pop(); // front of the queue, pop it out,
  315. LatestSize = myQueue.size(); // and rest our size (blinking light).
  316. return O; // Then return the object we got.
  317. }
  318. void give(T O) { // To produce a queued object
  319. ScopeMutex OneAtATimePlease(myMutex); // we wait on the mutex. When we
  320. myQueue.push(O); // get through we push our object
  321. LatestSize = myQueue.size(); // into the queue, reset our size
  322. myGateway.produce(); // indicator and tell the gateway.
  323. } // When we're done it can be grabbed.
  324. unsigned int size() { // To check the size we look at
  325. return LatestSize; // the blinking light.
  326. }
  327. };
  328. // End Production Queue
  329. ////////////////////////////////////////////////////////////////////////////////
  330. } // End namespace codedweller