Spinning Topp Logo BlackTopp Studios
inc
thread.cpp
Go to the documentation of this file.
1 // The DAGFrameScheduler is a Multi-Threaded lock free and wait free scheduling library.
2 // © Copyright 2010 - 2016 BlackTopp Studios Inc.
3 /* This file is part of The DAGFrameScheduler.
4 
5  The DAGFrameScheduler is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  The DAGFrameScheduler is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with The DAGFrameScheduler. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /* The original authors have included a copy of the license specified above in the
19  'doc' folder. See 'gpl.txt'
20 */
21 /* We welcome the use of the DAGFrameScheduler to anyone, including companies who wish to
22  Build professional software and charge for their product.
23 
24  However there are some practical restrictions, so if your project involves
25  any of the following you should contact us and we will try to work something
26  out:
27  - DRM or Copy Protection of any kind(except Copyrights)
28  - Software Patents You Do Not Wish to Freely License
29  - Any Kind of Linking to Non-GPL licensed Works
30  - Are Currently In Violation of Another Copyright Holder's GPL License
31  - If You want to change our code and not add a few hundred MB of stuff to
32  your distribution
33 
34  These and other limitations could cause serious legal problems if you ignore
35  them, so it is best to simply contact us or the Free Software Foundation, if
36  you have any questions.
37 
38  Joseph Toppi - toppij@gmail.com
39  John Blackwood - makoenergy02@gmail.com
40 */
41 #ifndef _btsthread_cpp
42 #define _btsthread_cpp
43 
44 #include "crossplatformincludes.h"
45 
46 /// @file
47 /// @brief This file implements a minimalistic cross-platform thread that the scheduler uses to schedule tasks.
48 
49 // Parts of this library use the TinyThread++ libary and those parts are also covered by the following license
50 /*
51 Copyright (c) 2010-2012 Marcus Geelnard
52 
53 This software is provided 'as-is', without any express or implied
54 warranty. In no event will the authors be held liable for any damages
55 arising from the use of this software.
56 
57 Permission is granted to anyone to use this software for any purpose,
58 including commercial applications, and to alter it and redistribute it
59 freely, subject to the following restrictions:
60 
61  1. The origin of this software must not be misrepresented; you must not
62  claim that you wrote the original software. If you use this software
63  in a product, an acknowledgment in the product documentation would be
64  appreciated but is not required.
65 
66  2. Altered source versions must be plainly marked as such, and must not be
67  misrepresented as being the original software.
68 
69  3. This notice may not be removed or altered from any source
70  distribution.
71 */
72 
73 #include "thread.h"
74 #include "crossplatformincludes.h"
75 #include "lockguard.h"
76 
77 #if defined(_MEZZ_THREAD_POSIX_)
78  #include <map>
79 #endif
80 
81 /// @cond false
82 
83 namespace Mezzanine
84 {
85  namespace Threading
86  {
87 
88  //------------------------------------------------------------------------------
89  // POSIX pthread_t to unique thread::id mapping logic.
90  // Note: Here we use a global thread safe std::map to convert instances of
91  // pthread_t to small thread identifier numbers (unique within one process).
92  // This method should be portable across different POSIX implementations.
93  //------------------------------------------------------------------------------
94 
95 
96  #if defined(_MEZZ_THREAD_POSIX_)
97  /// @internal
98  /// @brief This converts the id in thread::id into a native thread handle
99  /// @param aHandle An OS specific handle that will be used to lookup the thread::id
100  /// @return The Corresponding thread::id
101  /// @todo Investigate if this is a point of contention in the algorithm and try to remove it if possible
102  static ThreadId _pthread_t_to_ID(const pthread_t &aHandle)
103  {
104  static Mutex idMapLock;
105  static std::map<pthread_t, unsigned long int> idMap;
106  static unsigned long int idCount(1);
107 
108  lock_guard<Mutex> guard(idMapLock);
109  if(idMap.find(aHandle) == idMap.end())
110  idMap[aHandle] = idCount ++;
111  return ThreadId(idMap[aHandle]);
112  }
113  #endif // _MEZZ_POSIX_
114 
115  //------------------------------------------------------------------------------
116  // thread
117  //------------------------------------------------------------------------------
118 
119  /// @brief Information to pass to the new thread (what to run).
120  struct _thread_start_info {
121  void (*mFunction)(void *); ///< Pointer to the function to be executed.
122  void * mArg; ///< Function argument for the thread function.
123  Thread * mThread; ///< Pointer to the thread object.
124  };
125 
126  // Thread wrapper function.
127  #if defined(_MEZZ_THREAD_WIN32_)
128  unsigned WINAPI Thread::wrapper_function(void * aArg)
129  #elif defined(_MEZZ_THREAD_POSIX_)
130  void * Thread::wrapper_function(void * aArg)
131  #endif
132  {
133  // Get thread startup information
134  _thread_start_info * ti = (_thread_start_info *) aArg;
135 
136  try
137  {
138  // Call the actual client thread function
139  ti->mFunction(ti->mArg);
140  }
141  catch(...)
142  {
143  // Uncaught exceptions will terminate the application (default behavior
144  // according to C++11)
145  std::terminate();
146  }
147 
148  // The thread is no longer executing
149  lock_guard<Mutex> guard(ti->mThread->mDataMutex);
150  ti->mThread->mNotAThread = true;
151 
152  // The thread is responsible for freeing the startup information if this can't, then failed threads
153  // will clean it up in the threads constructor.
154  delete ti;
155 
156  return 0;
157  }
158 
159  Thread::Thread(void (*aFunction)(void *))
160  {
161  // Serialize access to this thread structure
162  lock_guard<Mutex> guard(mDataMutex);
163 
164  // Fill out the thread startup information (passed to the thread wrapper,
165  // which will eventually free it)
166  _thread_start_info * ti = new _thread_start_info;
167  ti->mFunction = aFunction;
168  ti->mArg = 0;
169  ti->mThread = this;
170 
171  // The thread is now alive
172  mNotAThread = false;
173 
174  // Create the thread
175  #if defined(_MEZZ_THREAD_WIN32_)
176  mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID);
177  #elif defined(_MEZZ_THREAD_POSIX_)
178  if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0)
179  mHandle = 0;
180  #endif
181 
182  // Did we fail to create the thread?
183  if(!mHandle)
184  {
185  mNotAThread = true;
186  delete ti; // Since there is no thread wrapper_function was never called, so it never deleted this
187  }
188  }
189 
190  Thread::Thread(void (*aFunction)(void *), void * aArg)
191  {
192  // Serialize access to this thread structure
193  lock_guard<Mutex> guard(mDataMutex);
194 
195  // Fill out the thread startup information (passed to the thread wrapper,
196  // which will eventually free it)
197  _thread_start_info * ti = new _thread_start_info;
198  ti->mFunction = aFunction;
199  ti->mArg = aArg;
200  ti->mThread = this;
201 
202  // The thread is now alive
203  mNotAThread = false;
204 
205  // Create the thread
206  #if defined(_MEZZ_THREAD_WIN32_)
207  mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID);
208  #elif defined(_MEZZ_THREAD_POSIX_)
209  if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0)
210  mHandle = 0;
211  #endif
212 
213  // Did we fail to create the thread?
214  if(!mHandle)
215  {
216  mNotAThread = true;
217  delete ti;
218  }
219  }
220 
222  {
223  if(joinable())
224  std::terminate(); // prevents undefined behavior, and hopefully helps identifies bugs early
225  }
226 
227  void Thread::join()
228  {
229  if(joinable())
230  {
231  #if defined(_MEZZ_THREAD_WIN32_)
232  WaitForSingleObject(mHandle, INFINITE);
233  CloseHandle(mHandle);
234  #elif defined(_MEZZ_THREAD_POSIX_)
235  pthread_join(mHandle, NULL);
236  #endif
237  }
238  }
239 
240  bool Thread::joinable() const
241  {
242  mDataMutex.Lock();
243  bool result = !mNotAThread;
244  mDataMutex.Unlock();
245  return result;
246  }
247 
248  void Thread::detach()
249  {
250  mDataMutex.Lock();
251  if(!mNotAThread)
252  {
253  #if defined(_MEZZ_THREAD_WIN32_)
254  CloseHandle(mHandle);
255  #elif defined(_TTHREAD_POSIX_)
256  pthread_detach(mHandle);
257  #endif
258  mNotAThread = true;
259  }
260  mDataMutex.Unlock();
261  }
262 
263  ThreadId Thread::get_id() const
264  {
265  if(!joinable())
266  return ThreadId();
267  #if defined(_MEZZ_THREAD_WIN32_)
268  return ThreadId((unsigned long int) mWin32ThreadID);
269  #elif defined(_MEZZ_THREAD_POSIX_)
270  return _pthread_t_to_ID(mHandle);
271  #endif
272  }
273 
275  {
276  #if defined(_MEZZ_THREAD_WIN32_)
277  SYSTEM_INFO si;
278  GetSystemInfo(&si);
279  return (int) si.dwNumberOfProcessors;
280  #elif defined(_SC_NPROCESSORS_ONLN)
281  return (int) sysconf(_SC_NPROCESSORS_ONLN);
282  #elif defined(_SC_NPROC_ONLN)
283  return (int) sysconf(_SC_NPROC_ONLN);
284  #else
285  // The standard requires this function to return zero if the number of
286  // hardware cores could not be determined.
287  return 0;
288  #endif
289  }
290 
291 
292  //------------------------------------------------------------------------------
293  // this_thread
294  //------------------------------------------------------------------------------
295 
296  ThreadId this_thread::get_id()
297  {
298  #if defined(_MEZZ_THREAD_WIN32_)
299  return ThreadId((unsigned long int) GetCurrentThreadId());
300  #elif defined(_MEZZ_THREAD_POSIX_)
301  return _pthread_t_to_ID(pthread_self());
302  #endif
303  }
304 
306  {
307  #if defined(_MEZZ_THREAD_WIN32_)
308  Sleep(0);
309  #else
310  sched_yield();
311  #endif
312  }
313 
314  void this_thread::sleep_for(UInt32 MicroSeconds)
315  {
316  #if defined(_MEZZ_THREAD_WIN32_)
317  Sleep(MicroSeconds/1000);
318  #else
319  usleep(MicroSeconds);
320  #endif
321  }
322  }//Threading
323 }//Mezzanine
324 
325 /// @endcond
326 
327 
328 #endif
void Lock()
Lock the mutex.
Definition: mutex.cpp:78
ThreadId MEZZ_LIB get_id()
Return the thread ID of the calling thread.
ThreadId get_id() const
Return the thread ID of a thread object.
void detach()
Detach from the thread.
bool joinable() const
Check if the thread is joinable.
This file determines what features are offered by the compiler that this library can take advantage o...
This file defines a minimalistic cross-platform thread that the scheduler uses to schedule tasks...
void MEZZ_LIB yield()
Yield execution to another thread.
static unsigned hardware_concurrency()
Determine the number of threads which can possibly execute concurrently.
#define WINAPI
Calling some win32 api functions require special calling conventions, this is their configuration...
Declares a tool for automatically unlocking a mutex in an exception safe way.
uint32_t UInt32
An 32-bit unsigned integer.
Definition: datatypes.h:126
void MEZZ_LIB sleep_for(UInt32 MicroSeconds)
Blocks the calling thread for a period of time.
Thread()
Default constructor.
Definition: thread.h:112
#define MEZZ_LIB
Some platforms require special decorations to denote what is exported/imported in a share library...
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
void Unlock()
Unlock the mutex.
Definition: mutex.cpp:104
void join()
Wait for the thread to finish (join execution flows).