uLib  User mode C/C++ extended API library for Win32 programmers.
Thread.cpp
Go to the documentation of this file.
1 //==---------------------------------------------------------------------------
2 // Project: uLib utility library.
3 // Module: Threads that can be safely killed (unlike TerminateThread).
4 // License: Released under the NNOSL (See NNOSL.txt in the base directory).
5 //
6 // Author: Love Nystrom 2017 <neo.love@hotmail.com>
7 // Author: Based on code by Jeff Richter 1995, published in MSJ March 1996.
8 //
9 // Jeff's article contains the following disclaimer:
10 //
11 // The information contained herein is not endorsed by Microsoft.
12 // Microsoft makes no warranty as to the accuracy or the completeness of the
13 // information. Users are advised that they use this at their own risk.
14 //
15 //==---------------------------------------------------------------------------
16 // NOTE: Requires /EHa compiler switch to perform stack unwinding correctly.
17 //==---------------------------------------------------------------------------
18 
19 #include <uLib/Common.h>
20 #ifdef HAVE_STRUCTURED_EH
21 #include <uLib/Thread.h>
22 #include <uLib/Debug.h>
23 #include <uLib/UtilFunc.h>
24 
25 BEGIN_NAMESPACE( uLib ) // The following two species are wrapped in our namespace.
26 
27 //==---------------------------------------------------------------------------
28 // SimpleThread (Note: this is not the killsafe species)
29 //==---------------------------------------------------------------------------
30 
32 : hThread( NULL ), Id( 0 ), UserData( NULL )
33 {}
34 
35 SimpleThread::~SimpleThread()
36 {
37  Stop();
38 }
39 
40 bool SimpleThread::Start( PTHREAD_START_ROUTINE ThreadFunc, DWORD msYield )
41 {
42  bool ok = (hThread == NULL);
43  if ( !ok ) SetLastError( ERROR_ALREADY_INITIALIZED );
44  else
45  {
46  hThread = CreateThread( NULL,0, ThreadFunc, this, 0, (PDWORD)&Id );
47  // Letting the calling thread sleep a bit should schedule the new thread promptly.
48  if (hThread && msYield) SleepEx( msYield, true );
49  ok = (hThread != NULL);
50  }
51  return ok;
52 }
53 
54 bool SimpleThread::Stop( DWORD msWait )
55 {
56  bool ok = (hThread == NULL);
57  if ( !ok )
58  {
59  Id = 0; // Set the stop signal.
60  DWORD error = 0;
61  ok = WaitFor( hThread, msWait );
62  if ( !ok )
63  {
64  TRACE( DP_ERROR,
65  _F("Terminating unresponsive thread 0x%04x. Debug your code!\n"),
66  GetCurrentThreadId()
67  );
68  error = WAIT_TIMEOUT;
69  TerminateThread( hThread, error );
70  }
71  Close();
72  if (error) SetLastError( error );
73  }
74  return ok;
75 }
76 
77 void SimpleThread::Close()
78 {
79  hThread = CloseHandleEx( hThread );
80  Id = 0;
81 }
82 
83 //==---------------------------------------------------------------------------
84 // Thread. Based on Jeff Richter's code from 1995. Big thanks :)
85 //==---------------------------------------------------------------------------
86 
87 // Our very own software exception.
88 // This exception is raised when a thread is being killed.
89 // MAKE_SOFTWARE_EXCEPTION --> Common.h
90 
91 #define SE_KILLTHREAD \
92  MAKE_SOFTEXCEPTION( ERROR_SEVERITY_ERROR, FACILITY_ULIB, 1 )
93 
94 // Macros used to abstract the instruction pointer register
95 // for the various CPU platforms.
96 
97 #if defined(_X86_)
98 #define PROGCTR(Context) (Context.Eip)
99 #endif
100 
101 #if defined(_M_AMD64) // x64
102 #define PROGCTR(Context) (Context.Rip)
103 #endif
104 
105 #if defined(_MIPS_)
106 #define PROGCTR(Context) (Context.Fir)
107 #endif
108 
109 #if defined(_ALPHA_)
110 #define PROGCTR(Context) (Context.Fir)
111 #endif
112 
113 #if defined(_PPC_)
114 #define PROGCTR(Context) (Context.Iar)
115 #endif
116 
117 #if !defined(PROGCTR)
118 #error Module contains CPU-specific code; modify and recompile.
119 #endif
120 
121 #ifdef INCL_THREAD_W95_SUPPORT
122 static PVOID CreateExceptionHandlerThunks( PHANDLE phFileMap );
123 static void DeleteExceptionHandlerThunks( PVOID pvThunk, HANDLE hFileMap );
124 #endif
125 
126 //==---------------------------------------------------------------------------
127 
128 static THREADPROC __dummyThr( PVOID arg ) {
129  Thread* _this = (Thread*) arg;
130  while( _this->idThread ) SleepEx( 1000, true );
131  return 0;
132  }
133 
134 //==---------------------------------------------------------------------------
135 
136 Thread::Thread(
137  PTHREAD_START_ROUTINE pFunc,
138  SIZE_T StackSize, DWORD Flags, PSECURITY_ATTRIBUTES pSec
139  )
140 {
141  UserData = NULL;
142  hThread = NULL;
143  idThread = 0;
144 
145  // Kludge for the descendant Execute dilemma: __dummyThr. See Start().
146  _ThreadFunc = pFunc ? pFunc : __dummyThr;
147 
148  _SecAttr = pSec;
149  _StkSize = StackSize;
150  _Flags = Flags;
151 
152  _hmControl = _hmDelay = _heEnd = NULL;
153  _DelayCount = 0;
154 
155  #ifdef INCL_THREAD_W95_SUPPORT
156  _pThunk = NULL;
157  _hFileMap = NULL;
158  #endif
159 }
160 
161 Thread::~Thread()
162 {
163  Stop( 3000 );
164 }
165 
166 bool Thread::SetThreadProc( PTHREAD_START_ROUTINE pFunc )
167 {
168  bool ok = (pFunc != NULL);
169  if (!ok) SetLastError( ERROR_INVALID_PARAMETER );
170  else
171  {
172  if (hThread) // Running, so fail
173  {
174  SetLastError( ERROR_ALREADY_ASSIGNED );
175  ok = false;
176  }
177  else // Not running, so set the function pointer
178  {
179  _ThreadFunc = pFunc;
180  }
181  }
182  return ok;
183 }
184 
185 // Start the thread if it's not running already.
186 
187 
188 bool Thread::Start()
189 {
190  typedef unsigned (__stdcall *PCRT_THREADFUNC)( void* arg );
191 
192  if (!hThread)
193  {
194  // The descendant Execute dilemma: They don't need/use _ThreadFunc,
195  // but I won't sacrifice the pointer check. See __dummyThr in the ctor.
196 
197  if (!_ThreadFunc) SetLastError( ERROR_INVALID_FUNCTION );
198  else
199  {
200  _DelayCount = 0; // Reset counter
201  _hmControl = CreateMutex( NULL, FALSE, NULL );
202  _hmDelay = CreateMutex( NULL, FALSE, NULL );
203  _heEnd = CreateEvent( NULL, TRUE, FALSE, NULL );
204 
205  #ifdef INCL_THREAD_W95_SUPPORT
206  if (_TLSIndex == TLS_OUT_OF_INDEXES) _TLSIndex = TlsAlloc();
207  #endif
208 
209  // Start the thread at our wrapper function _TProc,
210  // which then calls Execute, which by default calls _ThreadFunc.
211 
212  hThread = (HANDLE)_beginthreadex(
213  _SecAttr, (UINT)_StkSize, (PCRT_THREADFUNC)_TProc, this,
214  _Flags, (PUINT)&idThread
215  );
216  Sleep( 10 ); // Yield a little CPU time so thread can be scheduled.
217  }
218  }
219  return (hThread != NULL);
220 }
221 
222 // Stop the thread. Kill it if it's not responding.
223 
224 bool Thread::Stop( DWORD msWait, bool Nuke )
225 {
226  if (hThread)
227  {
228  idThread = 0; // Signal the thread to break it's loop.
229  if (!WaitFor( hThread, msWait ))
230  {
231  DPrint( DP_WARNING,_F("Killing unresponsive thread.\n") );
232  // Raise an exception in the thread to terminate it.
233  if ( !Kill() )
234  {
235  // Kill is pending due to DelayDeath. Wait another msWait.
236  if (Nuke && !WaitFor( hThread, msWait ))
237  {
238  // Nuke is allowed and e.g. pending I/O can't be aborted...
239  // ... Last resort: Call TerminateThread.
240  // This may cause internal leaks, undefined run-state, and various havoc.
241  DPrint( DP_ERROR,_F("ALERT: Forced to use TerminateThread !!\n") );
242  #ifndef __GNUC__
243  BREAK(); // GCC flips the lid... again :/
244  #endif
245  TerminateThread( hThread, ERROR_TIMEOUT );
246  }
247  }
248  }
249  _Cleanup( true );
250  }
251  return (hThread == NULL);
252 }
253 
254 bool Thread::Pause( bool pause ) // Pause/resume the thread.
255 {
256  if (pause) SuspendThread( hThread );
257  else ResumeThread( hThread );
258  return pause;
259 }
260 
261 bool Thread::Kill() // For controller. Force thread to terminate.
262 {
263  // The control thread calls this function to kill a worker thread.
264  // If the worker thread is not currently protected by DelayDeath, we attempt
265  // to kill the thread now by suspending it, changing it's instruction pointer
266  // to ForceDeath, and resuming the thread. Effectively we are raising
267  // an exception in the worker thread. If the worker thread is protected by
268  // DelayDeath, we simply set an event and let the thread kill itself
269  // when it calls DelayDeath( false ) to ends its protection.
270 
271  bool rip = false; // Rest in peace
272  WaitForSingleObject( _hmControl, INFINITE ); // PONDER: Ought to *not* be INFINITE...
273 
274  if (WaitForSingleObject( _hmDelay, 0 ) == WAIT_TIMEOUT)
275  {
276  // The thread is delaying its death,
277  // Set a flag that the thread will check later.
278 
279  SetEvent( _heEnd );
280  }
281  else // The thread can be terminated now!
282  {
283  if (WaitForSingleObject( hThread, 0 ) == WAIT_TIMEOUT)
284  {
285  // The worker has not yet terminated.
286 
287  SuspendThread( hThread ); // Stop the thread
288 
289  // Get the worker thread's current CPU registers.
290 
291  CONTEXT ctx = {0};
292  ctx.ContextFlags = CONTEXT_CONTROL;
293  GetThreadContext( hThread, &ctx );
294 
295  // Change the instruction pointer to our death function.
296 
297  PROGCTR(ctx) = (DWORD_PTR)_ForceDeath;
298  SetThreadContext( hThread, &ctx );
299 
300  // Resuming the thread forces our function to be called which
301  // raises an SE_KILLTHREAD exception in the worker thread.
302 
303  ResumeThread( hThread );
304  rip = true;
305  }
306  ReleaseMutex( _hmDelay );
307  }
308  ReleaseMutex( _hmControl );
309  return rip;
310 }
311 
312 void Thread::DelayDeath( bool enable )
313 {
314  // This function is used to allow the thread to protect sections of code
315  // from termination by the control thread. Call DelayDeath( true ) to
316  // start protection from Kill() and DelayDeath( false ) to end protection.
317  // Multiple DelayDeath( true ) calls are allowed. A delay count is
318  // maintained and the thread remains protected until the count is 0.
319 
320  WaitForSingleObject( _hmControl, INFINITE ); // PONDER: Ought to *not* be INFINITE.
321 
322  if (enable)
323  {
324  // The thread wants to delay its death. We get and keep
325  // the _hmDelay mutex while protected from termination by Kill.
326 
327  WaitForSingleObject( _hmDelay, INFINITE );
328  _DelayCount++;
329  }
330  else // The thread wants to allow its death.
331  {
332  _DelayCount--;
333  ReleaseMutex( _hmDelay );
334 
335  // If the delay death count is zero and Kill has been called
336  // then force the thread to terminate now.
337 
338  if ((_DelayCount == 0)
339  && (WaitForSingleObject( _heEnd, 0 ) == WAIT_OBJECT_0))
340  {
341  _ForceDeath();
342  }
343  }
344  ReleaseMutex( _hmControl );
345 }
346 
347 //==---------------------------------------------------------------------------
348 
349 void Thread::_Cleanup( bool closeThr )
350 {
351  if (closeThr)
352  {
353  hThread = CloseHandleEx( hThread );
354  idThread = 0;
355  }
356  _hmControl = CloseHandleEx( _hmControl );
357  _hmDelay = CloseHandleEx( _hmDelay );
358  _heEnd = CloseHandleEx( _heEnd );
359 }
360 
361 #pragma message("MEMO: [Thread] _TProc requires /EHa compiler switch > " __FILE__ ":" PP_NSTR(__LINE__))
362 // ... in order to peform stack unwind properly and call local destructors.
363 
364 static UINT __tpExFilter( UINT xcodeAct, PEXCEPTION_POINTERS pxp )
365 {
366  return (xcodeAct == pxp->ExceptionRecord->ExceptionCode)
367  ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
368 }
369 
370 THREADPROC Thread::_TProc( PVOID Arg ) // static
371 {
372  // The user's thread function is called from this "real" thread function.
373  // The new thread starts here because we need to wrap the call to the actual
374  // thread function in an SEH __try block and to perform cleanup just before
375  // the thread dies.
376 
377  PThread _this = (PThread) Arg;
378  DWORD ExitCode = 0;
379 
380  __try
381  {
382  __try
383  {
384  #ifdef INCL_THREAD_W95_SUPPORT // The index is pre-allocated
385  TlsSetValue( Thread::_TLSIndex, _this );
386  #endif
387 
388  ExitCode = _this->Execute();
389  }
390 
391  // If the exception occurs because our thread is forcibly being killed,
392  // execute our handler (the system does a global unwind first).
393 
394  __except( __tpExFilter( SE_KILLTHREAD, GetExceptionInformation() ))
395  {
396  ExitCode = ERROR_DBG_RIPEXCEPTION; // Not really, but RIP seems appropriate.
397  #ifdef INCL_THREAD_W95_SUPPORT
398  DeleteExceptionHandlerThunks( _this->_pThunk, _this->_hFileMap );
399  #endif
400  }
401  __end_except // GCC kludge
402  }
403  __finally // This executes even if the thread is dying.
404  {
405  _this->_Cleanup( true );
406 
407  #ifdef INCL_THREAD_W95_SUPPORT
408  TlsSetValue( Thread::_TLSIndex, NULL );
409  #endif
410  }
411  __end_finally // GCC kludge
412 
413  return ExitCode;
414 }
415 
416 // Terminate the worker thread by getting it to execute this function
417 
418 void Thread::_ForceDeath( void ) // static
419 {
420  #ifdef INCL_THREAD_W95_SUPPORT // Work around Windows 95 execption problem.
421  PThread _this = (PThread) TlsGetValue( Thread::_TLSIndex );
422  _this->_pThunk = CreateExceptionHandlerThunks( &_this->_hFileMap );
423  #endif
424 
425  RaiseException( SE_KILLTHREAD, EXCEPTION_NONCONTINUABLE, 0, NULL );
426  // RaiseException is dispatched and never returns
427 }
428 
429 //==---------------------------------------------------------------------------
430 #ifdef INCL_THREAD_W95_SUPPORT // Work around Windows 95 execption problem
431 //==---------------------------------------------------------------------------
432 
433 DWORD Thread::_TLSIndex = TLS_OUT_OF_INDEXES; // Gives static access to this.
434 
435 // Internal structure of the exception handler chain.
436 // This structure is documented, compiler writers need
437 // this information, but it's missing from the header files.
438 
439 typedef struct _EXCEPTIONREGISTRATIONRECORD {
440  _EXCEPTIONREGISTRATIONRECORD *pexrr;
441  PVOID pvHandler; // pointer to the exception handler function
442 } EXCEPTIONREGISTRATIONRECORD, *PEXCEPTIONREGISTRATIONRECORD;
443 
444 // This marks the end of the chain
445 
446 const PEXCEPTIONREGISTRATIONRECORD pexrrEndMark =
447  (PEXCEPTIONREGISTRATIONRECORD) 0xFFFFFFFF;
448 
449 // This is the lowest shared address
450 
451 const PVOID pvSharedMin = (PVOID) 0x80000000;
452 
453 // Code for thunks
454 
455 static const BYTE _MovAxImm[] = { (BYTE)'\xB8' }; // MOV EAX, imm
456 static const BYTE _JmpAx[] = { (BYTE)'\xFF', (BYTE)'\xE0' }; // JMP EAX
457 
458 // Structure of thunk. We use byte arrays to avoid any possibility
459 // of alignment problems (we do NOT want alignment).
460 
461 typedef struct {
462  BYTE abMovAxImm[ sizeof(_MovAxImm) ];
463  BYTE abJmpAddr[ sizeof(PROC) ]; // Size of a function pointer
464  BYTE abJmpAx[ sizeof(_JmpAx) ];
465 } EXCEPTTHUNK, *PEXCEPTTHUNK;
466 
467 static PVOID CreateExceptionHandlerThunks( PHANDLE phFileMap )
468 {
469  // This functions works around a problem that only exists on Windows 95
470  // when KillThrd_Kill interrupts the worker thread while in a system
471  // call. When an exception is raised from within a system call on
472  // Windows 95, only system exception handlers are called, user exception
473  // handlers are not. This can prevent KillThrd_ThreadFunc from handling
474  // the SE_KILLLTHREAD exception. We are able to trick the system into
475  // behaving as if all the handlers are system handlers by creating
476  // thunks in shared memory to call the actual exception handlers.
477 
478  // WARNING: Windows 95 was trying to protect us but we are defeating it.
479  // The system may be in an unusual state when the exception filters and
480  // handlers are processed. You should be sure they do not delay the
481  // termination of the thread.
482 
483  PVOID pvRet = NULL;
484  PEXCEPTIONREGISTRATIONRECORD pexrrHead;
485  PEXCEPTIONREGISTRATIONRECORD pexrrCur;
486  PEXCEPTTHUNK pThunk;
487  OSVERSIONINFO osvi;
488 
489  osvi.dwOSVersionInfoSize = sizeof(osvi);
490  GetVersionEx( &osvi );
491  BOOL fIsWin95 = (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
492  int nThunkCnt;
493 
494  if (fIsWin95)
495  {
496  // Get the head of the SEH handler chain
497  __asm mov eax, fs:[0]
498  __asm mov pexrrHead, eax
499 
500  // First calculate the number of thunks we need to create
501  pexrrCur = pexrrHead;
502  nThunkCnt = 0;
503  while (pexrrEndMark != pexrrCur)
504  {
505  // Don't thunk system handlers,
506  if (pexrrCur->pvHandler < pvSharedMin)
507  {
508  nThunkCnt++;
509  }
510  pexrrCur = pexrrCur->pexrr;
511  }
512 
513  // Allocate shared storage for the thunks (+1 for sentinel value)
514 
515  *phFileMap = CreateFileMapping(
516  INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
517  0, (nThunkCnt+1) * sizeof(EXCEPTTHUNK), NULL
518  );
519  if (NULL != *phFileMap)
520  {
521  pvRet = MapViewOfFile( *phFileMap, FILE_MAP_WRITE, 0,0,0 );
522  if (NULL == pvRet)
523  {
524  CloseHandle( *phFileMap );
525  *phFileMap = NULL;
526  }
527  else
528  {
529  pThunk = (PEXCEPTTHUNK) pvRet;
530  pexrrCur = pexrrHead;
531 
532  while( 0 < nThunkCnt )
533  {
534  // Only thunk user handlers
535  if (pexrrCur->pvHandler < pvSharedMin)
536  {
537  // Build the thunk in shared memory
538 
539  CopyMemory( pThunk->abMovAxImm, _MovAxImm, sizeof(pThunk->abMovAxImm) );
540  CopyMemory( pThunk->abJmpAddr, &pexrrCur->pvHandler, sizeof(pThunk->abJmpAddr) );
541  CopyMemory( pThunk->abJmpAx, _JmpAx, sizeof(pThunk->abJmpAx) );
542 
543  // Make the execption record point to thunk
544 
545  pexrrCur->pvHandler = (PVOID) pThunk;
546  pThunk++;
547  nThunkCnt--;
548  }
549  pexrrCur = pexrrCur->pexrr;
550  }
551  // Null terminate list
552  FillMemory( pThunk, sizeof(pThunk), 0 );
553  }
554  }
555  }
556  return pvRet;
557 }
558 
559 static void DeleteExceptionHandlerThunks( PVOID pvThunk, HANDLE hFileMap )
560 {
561  PEXCEPTIONREGISTRATIONRECORD pexrrCur;
562  PEXCEPTTHUNK pThunk;
563  PEXCEPTTHUNK pThunkCur;
564  int nThunkCnt = 0;
565 
566  if (NULL != pvThunk)
567  {
568  // Get the head of the SEH handler chain
569  __asm mov eax, fs:[0]
570  __asm mov pexrrCur, eax
571 
572  pThunk = (PEXCEPTTHUNK) pvThunk;
573 
574  // Count he number of entries in the null terminated thunk list
575 
576  while( '\0' != pThunk[nThunkCnt].abMovAxImm[0] )
577  {
578  nThunkCnt++;
579  }
580 
581  // Loop through the exception handler list
582 
583  while (pexrrEndMark != pexrrCur)
584  {
585  if ( (PVOID) pThunk <= pexrrCur->pvHandler
586  && pexrrCur->pvHandler < (PVOID) &pThunk[nThunkCnt] )
587  {
588  // This exception handler is pointing into our thunk table
589  // unthunk the entry
590 
591  pThunkCur = (PEXCEPTTHUNK) pexrrCur->pvHandler;
592  CopyMemory( &pexrrCur->pvHandler, pThunkCur->abJmpAddr, sizeof(pexrrCur->pvHandler) );
593  }
594  pexrrCur = pexrrCur->pexrr;
595  }
596  UnmapViewOfFile( pvThunk );
597  }
598 
599  if (NULL != hFileMap) CloseHandle( hFileMap );
600 }
601 
602 #endif//def INCL_THREAD_W95_SUPPORT
604 #endif//def HAVE_STRUCTURED_EH
605 // EOF
#define SE_KILLTHREAD
Definition: Thread.cpp:91
HANDLE CloseHandleEx(HANDLE H)
Definition: KernelUtil.cpp:80
unsigned long DWORD
Definition: Common.h:414
bool WaitFor(HANDLE hObj, DWORD msWait)
Definition: KernelUtil.cpp:95
#define END_NAMESPACE(name)
Definition: Common.h:225
void __cdecl DPrint(int Level, CSTR Fmt,...)
Definition: Debug.cpp:134
#define TRACE(_lvl,...)
Definition: Debug.h:216
#define THREADPROC
Definition: Common.h:1114
#define BREAK()
Definition: Debug.h:208
BOOL(WINAPI *SysImgList::Shell_GetImageLists)(HIMAGELIST *pimlLarge
Definition: DynArray.h:18
#define DP_ERROR
Definition: Debug.h:82
Debug and error handling support.
#define BEGIN_NAMESPACE(name)
Definition: Common.h:224
Common include; Added types, small "ubiquitous" utilities, et c.
#define _F(s)
Definition: Debug.h:49
#define DP_WARNING
Definition: Debug.h:83
unsigned char BYTE
Definition: Common.h:412
class Thread * PThread
Definition: Thread.h:121
unsigned long * PDWORD
Definition: Common.h:414