Source code of Windows XP (NT5)
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.

326 lines
9.9 KiB

  1. /***
  2. *threadex.c - Extended versions of Begin (Create) and End (Exit) a Thread
  3. *
  4. * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * This source contains the _beginthreadex() and _endthreadex()
  8. * routines which are used to start and terminate a thread. These
  9. * routines are more like the Win32 APIs CreateThread() and ExitThread()
  10. * than the original functions _beginthread() & _endthread() were.
  11. *
  12. *Revision History:
  13. * 02-16-94 SKS Original version, based on thread.c which contains
  14. * _beginthread() and _endthread().
  15. * 02-17-94 SKS Changed error return from -1 to 0, fix some comments.
  16. * 06-10-94 SKS Pass the thrdaddr value directly to CreateThread().
  17. * Do *NOT* store the thread handle into the per-thread
  18. * data block of the child thread. (It is not needed.)
  19. * The thread data structure may have been freed by the
  20. * child thread before the parent thread returns from the
  21. * call to CreateThread(). Watch that synchronization!
  22. * 01-10-95 CFW Debug CRT allocs.
  23. * 04-18-95 SKS Add 5 MIPS per-thread variables.
  24. * 05-02-95 SKS Call _initptd for initialization of per-thread data.
  25. * 02-03-98 GJF Changes for Win64: use uintptr_t type for anything with
  26. * a HANDLE value.
  27. * 02-02-00 GB Modified threadstartex() to prevent leaking of ptd
  28. * allocated during call to getptd while ATTACHing THREAD
  29. * in dlls.
  30. * 05-31-00 PML Don't pass NULL thrdaddr into CreateThread, since a
  31. * non-NULL lpThreadId is required on Win9x.
  32. * 08-04-00 PML Set EINVAL error if thread start address null in
  33. * _beginthreadex (VS7#118688).
  34. *
  35. *******************************************************************************/
  36. #ifdef _MT
  37. #include <cruntime.h>
  38. #include <oscalls.h>
  39. #include <internal.h>
  40. #include <mtdll.h>
  41. #include <msdos.h>
  42. #include <malloc.h>
  43. #include <process.h>
  44. #include <stddef.h>
  45. #include <rterr.h>
  46. #include <dbgint.h>
  47. #include <errno.h>
  48. /*
  49. * Startup code for new thread.
  50. */
  51. static unsigned long WINAPI _threadstartex(void *);
  52. /*
  53. * declare pointers to per-thread FP initialization and termination routines
  54. */
  55. _PVFV _FPmtinit;
  56. _PVFV _FPmtterm;
  57. /***
  58. *_beginthreadex() - Create a child thread
  59. *
  60. *Purpose:
  61. * Create a child thread.
  62. *
  63. *Entry:
  64. * *** Same parameters as the Win32 API CreateThread() ***
  65. * security = security descriptor for the new thread
  66. * stacksize = size of stack
  67. * initialcode = pointer to thread's startup code address
  68. * must be a __stdcall function returning an unsigned.
  69. * argument = argument to be passed to new thread
  70. * createflag = flag to create thread in a suspended state
  71. * thrdaddr = points to an int to receive the ID of the new thread
  72. *
  73. *Exit:
  74. * *** Same as the Win32 API CreateThread() ***
  75. *
  76. * success = handle for new thread if successful
  77. *
  78. * failure = 0 in case of error, errno and _doserrno are set
  79. *
  80. *Exceptions:
  81. *
  82. *Notes:
  83. * This routine is more like the Win32 API CreateThread() than it
  84. * is like the C run-time routine _beginthread(). Ditto for
  85. * _endthreadex() and the Win32 API ExitThread() versus _endthread().
  86. *
  87. * Differences between _beginthread/_endthread and the "ex" versions:
  88. *
  89. * 1) _beginthreadex takes the 3 extra parameters to CreateThread
  90. * which are lacking in _beginthread():
  91. * A) security descriptor for the new thread
  92. * B) initial thread state (running/asleep)
  93. * C) pointer to return ID of newly created thread
  94. *
  95. * 2) The routine passed to _beginthread() must be __cdecl and has
  96. * no return code, but the routine passed to _beginthreadex()
  97. * must be __stdcall and returns a thread exit code. _endthread
  98. * likewise takes no parameter and calls ExitThread() with a
  99. * parameter of zero, but _endthreadex() takes a parameter as
  100. * thread exit code.
  101. *
  102. * 3) _endthread implicitly closes the handle to the thread, but
  103. * _endthreadex does not!
  104. *
  105. * 4) _beginthread returns -1 for failure, _beginthreadex returns
  106. * 0 for failure (just like CreateThread).
  107. *
  108. *******************************************************************************/
  109. uintptr_t __cdecl _beginthreadex (
  110. void *security,
  111. unsigned stacksize,
  112. unsigned (__stdcall * initialcode) (void *),
  113. void * argument,
  114. unsigned createflag,
  115. unsigned *thrdaddr
  116. )
  117. {
  118. _ptiddata ptd; /* pointer to per-thread data */
  119. uintptr_t thdl; /* thread handle */
  120. unsigned long errcode = 0L; /* Return from GetLastError() */
  121. unsigned dummyid; /* dummy returned thread ID */
  122. if ( initialcode == NULL ) {
  123. errno = EINVAL;
  124. return( (uintptr_t)0 );
  125. }
  126. /*
  127. * Allocate and initialize a per-thread data structure for the to-
  128. * be-created thread.
  129. */
  130. if ( (ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL )
  131. goto error_return;
  132. /*
  133. * Initialize the per-thread data
  134. */
  135. _initptd(ptd);
  136. ptd->_initaddr = (void *) initialcode;
  137. ptd->_initarg = argument;
  138. ptd->_thandle = (uintptr_t)(-1);
  139. /*
  140. * Make sure non-NULL thrdaddr is passed to CreateThread
  141. */
  142. if ( thrdaddr == NULL )
  143. thrdaddr = &dummyid;
  144. /*
  145. * Create the new thread using the parameters supplied by the caller.
  146. */
  147. if ( (thdl = (uintptr_t)
  148. CreateThread( security,
  149. stacksize,
  150. _threadstartex,
  151. (LPVOID)ptd,
  152. createflag,
  153. thrdaddr))
  154. == (uintptr_t)0 )
  155. {
  156. errcode = GetLastError();
  157. goto error_return;
  158. }
  159. /*
  160. * Good return
  161. */
  162. return(thdl);
  163. /*
  164. * Error return
  165. */
  166. error_return:
  167. /*
  168. * Either ptd is NULL, or it points to the no-longer-necessary block
  169. * calloc-ed for the _tiddata struct which should now be freed up.
  170. */
  171. _free_crt(ptd);
  172. /*
  173. * Map the error, if necessary.
  174. *
  175. * Note: this routine returns 0 for failure, just like the Win32
  176. * API CreateThread, but _beginthread() returns -1 for failure.
  177. */
  178. if ( errcode != 0L )
  179. _dosmaperr(errcode);
  180. return( (uintptr_t)0 );
  181. }
  182. /***
  183. *_threadstartex() - New thread begins here
  184. *
  185. *Purpose:
  186. * The new thread begins execution here. This routine, in turn,
  187. * passes control to the user's code.
  188. *
  189. *Entry:
  190. * void *ptd = pointer to _tiddata structure for this thread
  191. *
  192. *Exit:
  193. * Never returns - terminates thread!
  194. *
  195. *Exceptions:
  196. *
  197. *******************************************************************************/
  198. static unsigned long WINAPI _threadstartex (
  199. void * ptd
  200. )
  201. {
  202. _ptiddata _ptd; /* pointer to per-thread data */
  203. /*
  204. * Check if ptd is initialised during THREAD_ATTACH call to dll mains
  205. */
  206. if ( ( _ptd = TlsGetValue(__tlsindex)) == NULL)
  207. {
  208. /*
  209. * Stash the pointer to the per-thread data stucture in TLS
  210. */
  211. if ( !TlsSetValue(__tlsindex, ptd) )
  212. _amsg_exit(_RT_THREAD);
  213. /*
  214. * Set the thread ID field -- parent thread cannot set it after
  215. * CreateThread() returns since the child thread might have run
  216. * to completion and already freed its per-thread data block!
  217. */
  218. ((_ptiddata) ptd)->_tid = GetCurrentThreadId();
  219. }
  220. else
  221. {
  222. _ptd->_initaddr = ((_ptiddata) ptd)->_initaddr;
  223. _ptd->_initarg = ((_ptiddata) ptd)->_initarg;
  224. _free_crt(ptd);
  225. ptd = _ptd;
  226. }
  227. /*
  228. * Call fp initialization, if necessary
  229. */
  230. if ( _FPmtinit != NULL )
  231. (*_FPmtinit)();
  232. /*
  233. * Guard call to user code with a _try - _except statement to
  234. * implement runtime errors and signal support
  235. */
  236. __try {
  237. _endthreadex (
  238. ( (unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr) )
  239. ( ((_ptiddata)ptd)->_initarg ) ) ;
  240. }
  241. __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
  242. {
  243. /*
  244. * Should never reach here
  245. */
  246. _exit( GetExceptionCode() );
  247. } /* end of _try - _except */
  248. /*
  249. * Never executed!
  250. */
  251. return(0L);
  252. }
  253. /***
  254. *_endthreadex() - Terminate the calling thread
  255. *
  256. *Purpose:
  257. *
  258. *Entry:
  259. * Thread exit code
  260. *
  261. *Exit:
  262. * Never returns!
  263. *
  264. *Exceptions:
  265. *
  266. *******************************************************************************/
  267. void __cdecl _endthreadex (
  268. unsigned retcode
  269. )
  270. {
  271. _ptiddata ptd; /* pointer to thread's _tiddata struct */
  272. /*
  273. * Call fp termination, if necessary
  274. */
  275. if ( _FPmtterm != NULL )
  276. (*_FPmtterm)();
  277. if ( (ptd = _getptd()) == NULL )
  278. _amsg_exit(_RT_THREAD);
  279. /*
  280. * Free up the _tiddata structure & its subordinate buffers
  281. * _freeptd() will also clear the value for this thread
  282. * of the TLS variable __tlsindex.
  283. */
  284. _freeptd(ptd);
  285. /*
  286. * Terminate the thread
  287. */
  288. ExitThread(retcode);
  289. }
  290. #endif /* _MT */