1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 thread.cpp
12
13Abstract:
14
15 Thread object and core APIs
16
17
18
19--*/
20
21#include "pal/dbgmsg.h"
22SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first
23
24#include "pal/corunix.hpp"
25#include "pal/context.h"
26#include "pal/thread.hpp"
27#include "pal/mutex.hpp"
28#include "pal/handlemgr.hpp"
29#include "pal/cs.hpp"
30#include "pal/seh.hpp"
31#include "pal/signal.hpp"
32
33#include "procprivate.hpp"
34#include "pal/process.h"
35#include "pal/module.h"
36#include "pal/environ.h"
37#include "pal/init.h"
38#include "pal/utils.h"
39#include "pal/virtual.h"
40
41#if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID
42#include <sys/cdefs.h>
43#include <sys/param.h>
44#include <sys/sysctl.h>
45#include <kvm.h>
46#endif
47
48#include <signal.h>
49#include <pthread.h>
50#if HAVE_PTHREAD_NP_H
51#include <pthread_np.h>
52#endif
53#include <unistd.h>
54#include <errno.h>
55#include <stddef.h>
56#include <sys/stat.h>
57#include <sys/mman.h>
58#if HAVE_MACH_THREADS
59#include <mach/mach.h>
60#endif // HAVE_MACH_THREADS
61#if HAVE_POLL
62#include <poll.h>
63#else
64#include "pal/fakepoll.h"
65#endif // HAVE_POLL
66#include <limits.h>
67
68#if HAVE_SYS_LWP_H
69#include <sys/lwp.h>
70#endif
71#if HAVE_LWP_H
72#include <lwp.h>
73#endif
74// If we don't have sys/lwp.h but do expect to use _lwp_self, declare it to silence compiler warnings
75#if HAVE__LWP_SELF && !HAVE_SYS_LWP_H && !HAVE_LWP_H
76extern "C" int _lwp_self ();
77#endif
78
79using namespace CorUnix;
80
81
82/* ------------------- Definitions ------------------------------*/
83
84/* list of free CPalThread objects */
85static Volatile<CPalThread*> free_threads_list = NULL;
86
87/* lock to access list of free THREAD structures */
88/* NOTE: can't use a CRITICAL_SECTION here (see comment in FreeTHREAD) */
89int free_threads_spinlock = 0;
90
91/* lock to access iEndingThreads counter, condition variable to signal shutdown
92thread when any remaining threads have died, and count of exiting threads that
93can't be suspended. */
94pthread_mutex_t ptmEndThread;
95pthread_cond_t ptcEndThread;
96static int iEndingThreads = 0;
97
98// Activation function that gets called when an activation is injected into a thread.
99PAL_ActivationFunction g_activationFunction = NULL;
100// Function to check if an activation can be safely injected at a specified context
101PAL_SafeActivationCheckFunction g_safeActivationCheckFunction = NULL;
102
103void
104ThreadCleanupRoutine(
105 CPalThread *pThread,
106 IPalObject *pObjectToCleanup,
107 bool fShutdown,
108 bool fCleanupSharedState
109 );
110
111PAL_ERROR
112ThreadInitializationRoutine(
113 CPalThread *pThread,
114 CObjectType *pObjectType,
115 void *pImmutableData,
116 void *pSharedData,
117 void *pProcessLocalData
118 );
119
120void
121IncrementEndingThreadCount(
122 void
123 );
124
125void
126DecrementEndingThreadCount(
127 void
128 );
129
130CObjectType CorUnix::otThread(
131 otiThread,
132 ThreadCleanupRoutine,
133 ThreadInitializationRoutine,
134 0, // sizeof(CThreadImmutableData),
135 NULL, // No immutable data copy routine
136 NULL, // No immutable data cleanup routine
137 sizeof(CThreadProcessLocalData),
138 NULL, // No process local data cleanup routine
139 0, // sizeof(CThreadSharedData),
140 0, // THREAD_ALL_ACCESS,
141 CObjectType::SecuritySupported,
142 CObjectType::SecurityInfoNotPersisted,
143 CObjectType::UnnamedObject,
144 CObjectType::LocalDuplicationOnly,
145 CObjectType::WaitableObject,
146 CObjectType::SingleTransitionObject,
147 CObjectType::ThreadReleaseHasNoSideEffects,
148 CObjectType::NoOwner
149 );
150
151CAllowedObjectTypes aotThread(otiThread);
152
153/*++
154Function:
155 InternalEndCurrentThreadWrapper
156
157 Destructor for the thread-specific data representing the current PAL thread.
158 Called from pthread_exit. (pthread_exit is not called from the thread on which
159 main() was first invoked. This is not a problem, though, since when main()
160 returns, this results in an implicit call to exit().)
161
162 arg: the PAL thread
163*/
164static void InternalEndCurrentThreadWrapper(void *arg)
165{
166 CPalThread *pThread = (CPalThread *) arg;
167
168 // When pthread_exit calls us, it has already removed the PAL thread
169 // from TLS. Since InternalEndCurrentThread calls functions that assert
170 // that the current thread is known to this PAL, and that pThread
171 // actually is the current PAL thread, put it back in TLS temporarily.
172 pthread_setspecific(thObjKey, pThread);
173 (void)PAL_Enter(PAL_BoundaryTop);
174
175 /* Call entry point functions of every attached modules to
176 indicate the thread is exiting */
177 /* note : no need to enter a critical section for serialization, the loader
178 will lock its own critical section */
179 LOADCallDllMain(DLL_THREAD_DETACH, NULL);
180
181#if !HAVE_MACH_EXCEPTIONS
182 pThread->FreeSignalAlternateStack();
183#endif // !HAVE_MACH_EXCEPTIONS
184
185 // PAL_Leave will be called just before we release the thread reference
186 // in InternalEndCurrentThread.
187 InternalEndCurrentThread(pThread);
188 pthread_setspecific(thObjKey, NULL);
189}
190
191/*++
192Function:
193 TLSInitialize
194
195 Initialize the TLS subsystem
196--*/
197BOOL TLSInitialize()
198{
199 /* Create the pthread key for thread objects, which we use
200 for fast access to the current thread object. */
201 if (pthread_key_create(&thObjKey, InternalEndCurrentThreadWrapper))
202 {
203 ERROR("Couldn't create the thread object key\n");
204 return FALSE;
205 }
206
207 SPINLOCKInit(&free_threads_spinlock);
208
209 return TRUE;
210}
211
212/*++
213Function:
214 TLSCleanup
215
216 Shutdown the TLS subsystem
217--*/
218VOID TLSCleanup()
219{
220 SPINLOCKDestroy(&free_threads_spinlock);
221
222 pthread_key_delete(thObjKey);
223}
224
225/*++
226Function:
227 AllocTHREAD
228
229Abstract:
230 Allocate CPalThread instance
231
232Return:
233 The fresh thread structure, NULL otherwise
234--*/
235CPalThread* AllocTHREAD()
236{
237 CPalThread* pThread = NULL;
238
239 /* Get the lock */
240 SPINLOCKAcquire(&free_threads_spinlock, 0);
241
242 pThread = free_threads_list;
243 if (pThread != NULL)
244 {
245 free_threads_list = pThread->GetNext();
246 }
247
248 /* Release the lock */
249 SPINLOCKRelease(&free_threads_spinlock);
250
251 if (pThread == NULL)
252 {
253 pThread = InternalNew<CPalThread>();
254 }
255 else
256 {
257 pThread = new (pThread) CPalThread;
258 }
259
260 return pThread;
261}
262
263/*++
264Function:
265 FreeTHREAD
266
267Abstract:
268 Free THREAD structure
269
270--*/
271static void FreeTHREAD(CPalThread *pThread)
272{
273 //
274 // Run the destructors for this object
275 //
276
277 pThread->~CPalThread();
278
279#ifdef _DEBUG
280 // Fill value so we can find code re-using threads after they're dead. We
281 // check against pThread->dwGuard when getting the current thread's data.
282 memset((void*)pThread, 0xcc, sizeof(*pThread));
283#endif
284
285 // We SHOULD be doing the following, but it causes massive problems. See the
286 // comment below.
287 //pthread_setspecific(thObjKey, NULL); // Make sure any TLS entry is removed.
288
289 //
290 // Never actually free the THREAD structure to make the TLS lookaside cache work.
291 // THREAD* for terminated thread can be stuck in the lookaside cache code for an
292 // arbitrary amount of time. The unused THREAD* structures has to remain in a
293 // valid memory and thus can't be returned to the heap.
294 //
295 // TODO: is this really true? Why would the entry remain in the cache for
296 // an indefinite period of time after we've flushed it?
297 //
298
299 /* NOTE: can't use a CRITICAL_SECTION here: EnterCriticalSection(&cs,TRUE) and
300 LeaveCriticalSection(&cs,TRUE) need to access the thread private data
301 stored in the very THREAD structure that we just destroyed. Entering and
302 leaving the critical section with internal==FALSE leads to possible hangs
303 in the PROCSuspendOtherThreads logic, at shutdown time
304
305 Update: [TODO] PROCSuspendOtherThreads has been removed. Can this
306 code be changed?*/
307
308 /* Get the lock */
309 SPINLOCKAcquire(&free_threads_spinlock, 0);
310
311 pThread->SetNext(free_threads_list);
312 free_threads_list = pThread;
313
314 /* Release the lock */
315 SPINLOCKRelease(&free_threads_spinlock);
316}
317
318
319/*++
320Function:
321 THREADGetThreadProcessId
322
323returns the process owner ID of the indicated hThread
324--*/
325DWORD
326THREADGetThreadProcessId(
327 HANDLE hThread
328 // UNIXTODO Should take pThread parameter here (modify callers)
329 )
330{
331 CPalThread *pThread;
332 CPalThread *pTargetThread;
333 IPalObject *pobjThread = NULL;
334 PAL_ERROR palError = NO_ERROR;
335
336 DWORD dwProcessId = 0;
337
338 pThread = InternalGetCurrentThread();
339
340 palError = InternalGetThreadDataFromHandle(
341 pThread,
342 hThread,
343 0,
344 &pTargetThread,
345 &pobjThread
346 );
347
348 if (NO_ERROR != palError)
349 {
350 if (!pThread->IsDummy())
351 {
352 dwProcessId = GetCurrentProcessId();
353 }
354 else
355 {
356 ASSERT("Dummy thread passed to THREADGetProcessId\n");
357 }
358
359 if (NULL != pobjThread)
360 {
361 pobjThread->ReleaseReference(pThread);
362 }
363 }
364 else
365 {
366 ERROR("Couldn't retreive the hThread:%p pid owner !\n", hThread);
367 }
368
369
370 return dwProcessId;
371}
372
373/*++
374Function:
375 GetCurrentThreadId
376
377See MSDN doc.
378--*/
379DWORD
380PALAPI
381GetCurrentThreadId(
382 VOID)
383{
384 DWORD dwThreadId;
385
386 PERF_ENTRY(GetCurrentThreadId);
387 ENTRY("GetCurrentThreadId()\n");
388
389 //
390 // TODO: should do perf test to see how this compares
391 // with calling InternalGetCurrentThread (i.e., is our lookaside
392 // cache faster on average than pthread_self?)
393 //
394
395 dwThreadId = (DWORD)THREADSilentGetCurrentThreadId();
396
397 LOGEXIT("GetCurrentThreadId returns DWORD %#x\n", dwThreadId);
398 PERF_EXIT(GetCurrentThreadId);
399
400 return dwThreadId;
401}
402
403/*++
404Function:
405 PAL_GetCurrentOSThreadId
406
407Returns the current thread's OS thread ID.
408This API is functionally equivalent to GetCurrentThreadId, but does not truncate the return value to 32-bits.
409This is needed to ensure that we can provide the correct OS thread ID on platforms such as OSX that have a 64-bit thread ID.
410--*/
411size_t
412PALAPI
413PAL_GetCurrentOSThreadId(
414 VOID)
415{
416 size_t threadId;
417
418 PERF_ENTRY(PAL_GetCurrentOSThreadId);
419 ENTRY("PAL_GetCurrentOSThreadId()\n");
420
421 threadId = THREADSilentGetCurrentThreadId();
422
423 LOGEXIT("PAL_GetCurrentOSThreadId returns %p\n", threadId);
424 PERF_EXIT(GetCurrentThreadId);
425
426 return threadId;
427}
428
429
430/*++
431Function:
432 GetCurrentThread
433
434See MSDN doc.
435--*/
436HANDLE
437PALAPI
438PAL_GetCurrentThread(
439 VOID)
440{
441 PERF_ENTRY(GetCurrentThread);
442 ENTRY("GetCurrentThread()\n");
443
444 LOGEXIT("GetCurrentThread returns HANDLE %p\n", hPseudoCurrentThread);
445 PERF_EXIT(GetCurrentThread);
446
447 /* return a pseudo handle */
448 return (HANDLE) hPseudoCurrentThread;
449}
450
451/*++
452Function:
453 SwitchToThread
454
455See MSDN doc.
456--*/
457BOOL
458PALAPI
459SwitchToThread(
460 VOID)
461{
462 BOOL ret;
463
464 PERF_ENTRY(SwitchToThread);
465 ENTRY("SwitchToThread(VOID)\n");
466
467 /* sched_yield yields to another thread in the current process. This implementation
468 won't work well for cross-process synchronization. */
469 ret = (sched_yield() == 0);
470
471 LOGEXIT("SwitchToThread returns BOOL %d\n", ret);
472 PERF_EXIT(SwitchToThread);
473
474 return ret;
475}
476
477/*++
478Function:
479 CreateThread
480
481Note:
482 lpThreadAttributes could be ignored.
483
484See MSDN doc.
485
486--*/
487HANDLE
488PALAPI
489CreateThread(
490 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
491 IN DWORD dwStackSize,
492 IN LPTHREAD_START_ROUTINE lpStartAddress,
493 IN LPVOID lpParameter,
494 IN DWORD dwCreationFlags,
495 OUT LPDWORD lpThreadId)
496{
497 PAL_ERROR palError;
498 CPalThread *pThread;
499 HANDLE hNewThread = NULL;
500
501 PERF_ENTRY(CreateThread);
502 ENTRY("CreateThread(lpThreadAttr=%p, dwStackSize=%u, lpStartAddress=%p, "
503 "lpParameter=%p, dwFlags=%#x, lpThreadId=%#x)\n",
504 lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter,
505 dwCreationFlags, lpThreadId);
506
507 pThread = InternalGetCurrentThread();
508
509 palError = InternalCreateThread(
510 pThread,
511 lpThreadAttributes,
512 dwStackSize,
513 lpStartAddress,
514 lpParameter,
515 dwCreationFlags,
516 UserCreatedThread,
517 lpThreadId,
518 &hNewThread
519 );
520
521 if (NO_ERROR != palError)
522 {
523 pThread->SetLastError(palError);
524 }
525
526 LOGEXIT("CreateThread returns HANDLE %p\n", hNewThread);
527 PERF_EXIT(CreateThread);
528
529 return hNewThread;
530}
531
532PAL_ERROR
533CorUnix::InternalCreateThread(
534 CPalThread *pThread,
535 LPSECURITY_ATTRIBUTES lpThreadAttributes,
536 DWORD dwStackSize,
537 LPTHREAD_START_ROUTINE lpStartAddress,
538 LPVOID lpParameter,
539 DWORD dwCreationFlags,
540 PalThreadType eThreadType,
541 LPDWORD lpThreadId,
542 HANDLE *phThread
543 )
544{
545 PAL_ERROR palError;
546 CPalThread *pNewThread = NULL;
547 CObjectAttributes oa;
548 bool fAttributesInitialized = FALSE;
549 bool fThreadDataAddedToProcessList = FALSE;
550 HANDLE hNewThread = NULL;
551
552 pthread_t pthread;
553 pthread_attr_t pthreadAttr;
554 size_t pthreadStackSize;
555#if PTHREAD_CREATE_MODIFIES_ERRNO
556 int storedErrno;
557#endif // PTHREAD_CREATE_MODIFIES_ERRNO
558 BOOL fHoldingProcessLock = FALSE;
559 int iError = 0;
560 size_t alignedStackSize;
561
562 if (0 != terminator)
563 {
564 //
565 // Since the PAL is in the middle of shutting down we don't want to
566 // create any new threads (since it's possible for that new thread
567 // to create another thread before the shutdown thread gets around
568 // to suspending it, and so on). We don't want to return an error
569 // here, though, as some programs (in particular, build) do not
570 // handle CreateThread errors properly -- instead, we just put
571 // the calling thread to sleep (unless it is the shutdown thread,
572 // which could occur if a DllMain PROCESS_DETACH handler tried to
573 // create a new thread for some odd reason).
574 //
575
576 ERROR("process is terminating, can't create new thread.\n");
577
578 if (pThread->GetThreadId() != static_cast<DWORD>(terminator))
579 {
580 while (true)
581 {
582 poll(NULL, 0, INFTIM);
583 sched_yield();
584 }
585 }
586 else
587 {
588 //
589 // This is the shutdown thread, so just return an error
590 //
591
592 palError = ERROR_PROCESS_ABORTED;
593 goto EXIT;
594 }
595 }
596
597 /* Validate parameters */
598
599 if (lpThreadAttributes != NULL)
600 {
601 ASSERT("lpThreadAttributes parameter must be NULL (%p)\n",
602 lpThreadAttributes);
603 palError = ERROR_INVALID_PARAMETER;
604 goto EXIT;
605 }
606
607 alignedStackSize = dwStackSize;
608 if (alignedStackSize != 0)
609 {
610 // Some systems require the stack size to be aligned to the page size
611 if (sizeof(alignedStackSize) <= sizeof(dwStackSize) && alignedStackSize + (GetVirtualPageSize() - 1) < alignedStackSize)
612 {
613 // When coming here from the public API surface, the incoming value is originally a nonnegative signed int32, so
614 // this shouldn't happen
615 ASSERT(
616 "Couldn't align the requested stack size (%Iu) to the page size because the stack size was too large\n",
617 alignedStackSize);
618 palError = ERROR_INVALID_PARAMETER;
619 goto EXIT;
620 }
621 alignedStackSize = ALIGN_UP(alignedStackSize, GetVirtualPageSize());
622 }
623
624 // Ignore the STACK_SIZE_PARAM_IS_A_RESERVATION flag
625 dwCreationFlags &= ~STACK_SIZE_PARAM_IS_A_RESERVATION;
626
627 if ((dwCreationFlags != 0) && (dwCreationFlags != CREATE_SUSPENDED))
628 {
629 ASSERT("dwCreationFlags parameter is invalid (%#x)\n", dwCreationFlags);
630 palError = ERROR_INVALID_PARAMETER;
631 goto EXIT;
632 }
633
634 //
635 // Create the CPalThread for the thread
636 //
637
638 pNewThread = AllocTHREAD();
639 if (NULL == pNewThread)
640 {
641 palError = ERROR_OUTOFMEMORY;
642 goto EXIT;
643 }
644
645 palError = pNewThread->RunPreCreateInitializers();
646 if (NO_ERROR != palError)
647 {
648 goto EXIT;
649 }
650
651 pNewThread->m_lpStartAddress = lpStartAddress;
652 pNewThread->m_lpStartParameter = lpParameter;
653 pNewThread->m_bCreateSuspended = (dwCreationFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
654 pNewThread->m_eThreadType = eThreadType;
655
656 if (0 != pthread_attr_init(&pthreadAttr))
657 {
658 ERROR("couldn't initialize pthread attributes\n");
659 palError = ERROR_INTERNAL_ERROR;
660 goto EXIT;
661 }
662
663 fAttributesInitialized = TRUE;
664
665 if (alignedStackSize == 0)
666 {
667 // The thread is to be created with default stack size. Use the default stack size
668 // override that was determined during the PAL initialization.
669 alignedStackSize = g_defaultStackSize;
670 }
671
672 /* adjust the stack size if necessary */
673 if (alignedStackSize != 0)
674 {
675#ifdef PTHREAD_STACK_MIN
676 size_t MinStackSize = ALIGN_UP(PTHREAD_STACK_MIN, GetVirtualPageSize());
677#else // !PTHREAD_STACK_MIN
678 size_t MinStackSize = 64 * 1024; // this value is typically accepted by pthread_attr_setstacksize()
679#endif // PTHREAD_STACK_MIN
680 if (alignedStackSize < MinStackSize)
681 {
682 // Adjust the stack size to a minimum value that is likely to be accepted by pthread_attr_setstacksize(). If this
683 // function fails, typically the caller will end up throwing OutOfMemoryException under the assumption that the
684 // requested stack size is too large or the system does not have sufficient memory to create a thread. Try to
685 // prevent failing just just because the stack size value is too low.
686 alignedStackSize = MinStackSize;
687 }
688
689 TRACE("setting thread stack size to %Iu\n", alignedStackSize);
690 if (0 != pthread_attr_setstacksize(&pthreadAttr, alignedStackSize))
691 {
692 ERROR("couldn't set pthread stack size to %Iu\n", alignedStackSize);
693 palError = ERROR_INTERNAL_ERROR;
694 goto EXIT;
695 }
696 }
697 else
698 {
699 TRACE("using the system default thread stack size\n");
700 }
701
702#if HAVE_THREAD_SELF || HAVE__LWP_SELF
703 /* Create new threads as "bound", so each pthread is permanently bound
704 to an LWP. Get/SetThreadContext() depend on this 1:1 mapping. */
705 pthread_attr_setscope(&pthreadAttr, PTHREAD_SCOPE_SYSTEM);
706#endif // HAVE_THREAD_SELF || HAVE__LWP_SELF
707
708 //
709 // We never call pthread_join, so create the new thread as detached
710 //
711
712 iError = pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED);
713 _ASSERTE(0 == iError);
714
715 //
716 // Create the IPalObject for the thread and store it in the object
717 //
718
719 palError = CreateThreadObject(
720 pThread,
721 pNewThread,
722 &hNewThread);
723
724 if (NO_ERROR != palError)
725 {
726 goto EXIT;
727 }
728
729 //
730 // Add the thread to the process list
731 //
732
733 //
734 // We use the process lock to ensure that we're not interrupted
735 // during the creation process. After adding the CPalThread reference
736 // to the process list, we want to make sure the actual thread has been
737 // started. Otherwise, there's a window where the thread can be found
738 // in the process list but doesn't yet exist in the system.
739 //
740
741 PROCProcessLock();
742 fHoldingProcessLock = TRUE;
743
744 PROCAddThread(pThread, pNewThread);
745 fThreadDataAddedToProcessList = TRUE;
746
747 //
748 // Spawn the new pthread
749 //
750
751#if PTHREAD_CREATE_MODIFIES_ERRNO
752 storedErrno = errno;
753#endif // PTHREAD_CREATE_MODIFIES_ERRNO
754
755#ifdef FEATURE_PAL_SXS
756 _ASSERT_MSG(pNewThread->IsInPal(), "New threads we're about to spawn should always be in the PAL.\n");
757#endif // FEATURE_PAL_SXS
758 iError = pthread_create(&pthread, &pthreadAttr, CPalThread::ThreadEntry, pNewThread);
759
760#if PTHREAD_CREATE_MODIFIES_ERRNO
761 if (iError == 0)
762 {
763 // Restore errno if pthread_create succeeded.
764 errno = storedErrno;
765 }
766#endif // PTHREAD_CREATE_MODIFIES_ERRNO
767
768 if (0 != iError)
769 {
770 ERROR("pthread_create failed, error is %d (%s)\n", iError, strerror(iError));
771 palError = ERROR_NOT_ENOUGH_MEMORY;
772 goto EXIT;
773 }
774
775 //
776 // Wait for the new thread to finish its initial startup tasks
777 // (i.e., the ones that might fail)
778 //
779 if (pNewThread->WaitForStartStatus())
780 {
781 //
782 // Everything succeeded. Store the handle for the new thread and
783 // the thread's ID in the out params
784 //
785 *phThread = hNewThread;
786
787 if (NULL != lpThreadId)
788 {
789 *lpThreadId = pNewThread->GetThreadId();
790 }
791 }
792 else
793 {
794 ERROR("error occurred in THREADEntry, thread creation failed.\n");
795 palError = ERROR_INTERNAL_ERROR;
796 goto EXIT;
797 }
798
799 //
800 // If we're here, then we've locked the process list and both pthread_create
801 // and WaitForStartStatus succeeded. Thus, we can now unlock the process list.
802 // Since palError == NO_ERROR, we won't call this again in the exit block.
803 //
804 PROCProcessUnlock();
805 fHoldingProcessLock = FALSE;
806
807EXIT:
808
809 if (fAttributesInitialized)
810 {
811 if (0 != pthread_attr_destroy(&pthreadAttr))
812 {
813 WARN("pthread_attr_destroy() failed\n");
814 }
815 }
816
817 if (NO_ERROR != palError)
818 {
819 //
820 // We either were not able to create the new thread, or a failure
821 // occurred in the new thread's entry routine. Free up the associated
822 // resources here
823 //
824
825 if (fThreadDataAddedToProcessList)
826 {
827 PROCRemoveThread(pThread, pNewThread);
828 }
829 //
830 // Once we remove the thread from the process list, we can call
831 // PROCProcessUnlock.
832 //
833 if (fHoldingProcessLock)
834 {
835 PROCProcessUnlock();
836 }
837 fHoldingProcessLock = FALSE;
838 }
839
840 _ASSERT_MSG(!fHoldingProcessLock, "Exiting InternalCreateThread while still holding the process critical section.\n");
841
842 return palError;
843}
844
845
846
847/*++
848Function:
849 ExitThread
850
851See MSDN doc.
852--*/
853PAL_NORETURN
854VOID
855PALAPI
856ExitThread(
857 IN DWORD dwExitCode)
858{
859 CPalThread *pThread;
860
861 ENTRY("ExitThread(dwExitCode=%u)\n", dwExitCode);
862 PERF_ENTRY_ONLY(ExitThread);
863
864 pThread = InternalGetCurrentThread();
865
866 /* store the exit code */
867 pThread->SetExitCode(dwExitCode);
868
869 /* pthread_exit runs TLS destructors and cleanup routines,
870 possibly registered by foreign code. The right thing
871 to do here is to leave the PAL. Our own TLS destructor
872 re-enters us explicitly. */
873 PAL_Leave(PAL_BoundaryTop);
874
875 /* kill the thread (itself), resulting in a call to InternalEndCurrentThread */
876 pthread_exit(NULL);
877
878 ASSERT("pthread_exit should not return!\n");
879 for (;;);
880}
881
882/*++
883Function:
884 InternalEndCurrentThread
885
886Does any necessary memory clean up, signals waiting threads, and then forces
887the current thread to exit.
888--*/
889
890VOID
891CorUnix::InternalEndCurrentThread(
892 CPalThread *pThread
893 )
894{
895 PAL_ERROR palError = NO_ERROR;
896 ISynchStateController *pSynchStateController = NULL;
897
898#ifdef PAL_PERF
899 PERFDisableThreadProfile(UserCreatedThread != pThread->GetThreadType());
900#endif
901
902 //
903 // Abandon any objects owned by this thread
904 //
905
906 palError = g_pSynchronizationManager->AbandonObjectsOwnedByThread(
907 pThread,
908 pThread
909 );
910
911 if (NO_ERROR != palError)
912 {
913 ERROR("Failure abandoning owned objects");
914 }
915
916 //
917 // Need to synchronize setting the thread state to TS_DONE since
918 // this is checked for in InternalSuspendThreadFromData.
919 // TODO: Is this still needed after removing InternalSuspendThreadFromData?
920 //
921
922 pThread->suspensionInfo.AcquireSuspensionLock(pThread);
923 IncrementEndingThreadCount();
924 pThread->synchronizationInfo.SetThreadState(TS_DONE);
925 pThread->suspensionInfo.ReleaseSuspensionLock(pThread);
926
927 //
928 // Mark the thread object as signaled
929 //
930
931 palError = pThread->GetThreadObject()->GetSynchStateController(
932 pThread,
933 &pSynchStateController
934 );
935
936 if (NO_ERROR == palError)
937 {
938 palError = pSynchStateController->SetSignalCount(1);
939 if (NO_ERROR != palError)
940 {
941 ASSERT("Unable to mark thread object as signaled");
942 }
943
944 pSynchStateController->ReleaseController();
945 }
946 else
947 {
948 ASSERT("Unable to obtain state controller for thread");
949 }
950
951#ifndef FEATURE_PAL_SXS
952 // If this is the last thread then delete the process' data,
953 // but don't exit because the application hosting the PAL
954 // might have its own threads.
955 if (PROCGetNumberOfThreads() == 1)
956 {
957 TRACE("Last thread is exiting\n");
958 DecrementEndingThreadCount();
959 TerminateCurrentProcessNoExit(FALSE);
960 }
961 else
962#endif // !FEATURE_PAL_SXS
963 {
964 /* Do this ONLY if we aren't the last thread -> otherwise
965 it gets done by TerminateProcess->
966 PROCCleanupProcess->PALShutdown->PAL_Terminate */
967
968 //
969 // Add a reference to the thread data before releasing the
970 // thread object, so we can still use it
971 //
972
973 pThread->AddThreadReference();
974
975 //
976 // Release the reference to the IPalObject for this thread
977 //
978
979 pThread->GetThreadObject()->ReleaseReference(pThread);
980
981 /* Remove thread for the thread list of the process
982 (don't do if this is the last thread -> gets handled by
983 TerminateProcess->PROCCleanupProcess->PROCTerminateOtherThreads) */
984
985 PROCRemoveThread(pThread, pThread);
986
987#ifdef FEATURE_PAL_SXS
988 // Ensure that EH is disabled on the current thread
989 SEHDisable(pThread);
990 PAL_Leave(PAL_BoundaryTop);
991#endif // FEATURE_PAL_SXS
992
993
994 //
995 // Now release our reference to the thread data. We cannot touch
996 // it after this point
997 //
998
999 pThread->ReleaseThreadReference();
1000 DecrementEndingThreadCount();
1001
1002 }
1003}
1004
1005/*++
1006Function:
1007 GetThreadPriority
1008
1009See MSDN doc.
1010--*/
1011int
1012PALAPI
1013GetThreadPriority(
1014 IN HANDLE hThread)
1015{
1016 CPalThread *pThread;
1017 PAL_ERROR palError;
1018 int iPriority = THREAD_PRIORITY_ERROR_RETURN;
1019
1020 PERF_ENTRY(GetThreadPriority);
1021 ENTRY("GetThreadPriority(hThread=%p)\n", hThread);
1022
1023 pThread = InternalGetCurrentThread();
1024
1025 palError = InternalGetThreadPriority(
1026 pThread,
1027 hThread,
1028 &iPriority
1029 );
1030
1031 if (NO_ERROR != palError)
1032 {
1033 pThread->SetLastError(palError);
1034 }
1035
1036 LOGEXIT("GetThreadPriorityExit returns int %d\n", iPriority);
1037 PERF_EXIT(GetThreadPriority);
1038
1039 return iPriority;
1040}
1041
1042PAL_ERROR
1043CorUnix::InternalGetThreadPriority(
1044 CPalThread *pThread,
1045 HANDLE hThread,
1046 int *piPriority
1047 )
1048{
1049 PAL_ERROR palError = NO_ERROR;
1050 CPalThread *pTargetThread;
1051 IPalObject *pobjThread = NULL;
1052
1053 palError = InternalGetThreadDataFromHandle(
1054 pThread,
1055 hThread,
1056 0, // THREAD_QUERY_INFORMATION
1057 &pTargetThread,
1058 &pobjThread
1059 );
1060
1061 if (NO_ERROR != palError)
1062 {
1063 goto InternalGetThreadPriorityExit;
1064 }
1065
1066 pTargetThread->Lock(pThread);
1067
1068 *piPriority = pTargetThread->GetThreadPriority();
1069
1070 pTargetThread->Unlock(pThread);
1071
1072InternalGetThreadPriorityExit:
1073
1074 if (NULL != pobjThread)
1075 {
1076 pobjThread->ReleaseReference(pThread);
1077 }
1078
1079 return palError;
1080}
1081
1082
1083/*++
1084Function:
1085 SetThreadPriority
1086
1087See MSDN doc.
1088--*/
1089BOOL
1090PALAPI
1091SetThreadPriority(
1092 IN HANDLE hThread,
1093 IN int nPriority)
1094{
1095 CPalThread *pThread;
1096 PAL_ERROR palError = NO_ERROR;
1097
1098 PERF_ENTRY(SetThreadPriority);
1099 ENTRY("SetThreadPriority(hThread=%p, nPriority=%#x)\n", hThread, nPriority);
1100
1101 pThread = InternalGetCurrentThread();
1102
1103 palError = InternalSetThreadPriority(
1104 pThread,
1105 hThread,
1106 nPriority
1107 );
1108
1109 if (NO_ERROR != palError)
1110 {
1111 pThread->SetLastError(palError);
1112 }
1113
1114 LOGEXIT("SetThreadPriority returns BOOL %d\n", NO_ERROR == palError);
1115 PERF_EXIT(SetThreadPriority);
1116
1117 return NO_ERROR == palError;
1118}
1119
1120PAL_ERROR
1121CorUnix::InternalSetThreadPriority(
1122 CPalThread *pThread,
1123 HANDLE hTargetThread,
1124 int iNewPriority
1125 )
1126{
1127 PAL_ERROR palError = NO_ERROR;
1128 CPalThread *pTargetThread = NULL;
1129 IPalObject *pobjThread = NULL;
1130
1131 int st;
1132 int policy;
1133 struct sched_param schedParam;
1134 int max_priority;
1135 int min_priority;
1136 float posix_priority;
1137
1138
1139 palError = InternalGetThreadDataFromHandle(
1140 pThread,
1141 hTargetThread,
1142 0, // THREAD_SET_INFORMATION
1143 &pTargetThread,
1144 &pobjThread
1145 );
1146
1147 if (NO_ERROR != palError)
1148 {
1149 goto InternalSetThreadPriorityExit;
1150 }
1151
1152 pTargetThread->Lock(pThread);
1153
1154 /* validate the requested priority */
1155 switch (iNewPriority)
1156 {
1157 case THREAD_PRIORITY_TIME_CRITICAL: /* fall through */
1158 case THREAD_PRIORITY_IDLE:
1159 break;
1160
1161 case THREAD_PRIORITY_HIGHEST: /* fall through */
1162 case THREAD_PRIORITY_ABOVE_NORMAL: /* fall through */
1163 case THREAD_PRIORITY_NORMAL: /* fall through */
1164 case THREAD_PRIORITY_BELOW_NORMAL: /* fall through */
1165 case THREAD_PRIORITY_LOWEST:
1166#if PAL_IGNORE_NORMAL_THREAD_PRIORITY
1167 /* We aren't going to set the thread priority. Just record what it is,
1168 and exit */
1169 pTargetThread->m_iThreadPriority = iNewPriority;
1170 goto InternalSetThreadPriorityExit;
1171#endif
1172 break;
1173
1174 default:
1175 ASSERT("Priority %d not supported\n", iNewPriority);
1176 palError = ERROR_INVALID_PARAMETER;
1177 goto InternalSetThreadPriorityExit;
1178 }
1179
1180 /* check if the thread is still running */
1181 if (TS_DONE == pTargetThread->synchronizationInfo.GetThreadState())
1182 {
1183 /* the thread has exited, set the priority in the thread structure
1184 and exit */
1185 pTargetThread->m_iThreadPriority = iNewPriority;
1186 goto InternalSetThreadPriorityExit;
1187 }
1188
1189 /* get the previous thread schedule parameters. We need to know the
1190 scheduling policy to determine the priority range */
1191 if (pthread_getschedparam(
1192 pTargetThread->GetPThreadSelf(),
1193 &policy,
1194 &schedParam
1195 ) != 0)
1196 {
1197 ASSERT("Unable to get current thread scheduling information\n");
1198 palError = ERROR_INTERNAL_ERROR;
1199 goto InternalSetThreadPriorityExit;
1200 }
1201
1202#if !HAVE_SCHED_OTHER_ASSIGNABLE
1203 /* Defining thread priority for SCHED_OTHER is implementation defined.
1204 Some platforms like NetBSD cannot reassign it as they are dynamic.
1205 */
1206 if (policy == SCHED_OTHER)
1207 {
1208 TRACE("Pthread priority levels for SCHED_OTHER cannot be reassigned on this platform\n");
1209 goto InternalSetThreadPriorityExit;
1210 }
1211#endif
1212
1213#if HAVE_SCHED_GET_PRIORITY
1214 max_priority = sched_get_priority_max(policy);
1215 min_priority = sched_get_priority_min(policy);
1216 if( -1 == max_priority || -1 == min_priority)
1217 {
1218 ASSERT("sched_get_priority_min/max failed; error is %d (%s)\n",
1219 errno, strerror(errno));
1220 palError = ERROR_INTERNAL_ERROR;
1221 goto InternalSetThreadPriorityExit;
1222 }
1223#else
1224 max_priority = PAL_THREAD_PRIORITY_MAX;
1225 min_priority = PAL_THREAD_PRIORITY_MIN;
1226#endif
1227
1228 TRACE("Pthread priorities for policy %d must be in the range %d to %d\n",
1229 policy, min_priority, max_priority);
1230
1231 /* explanation for fancy maths below :
1232 POSIX doesn't specify the range of thread priorities that can be used
1233 with pthread_setschedparam. Instead, one must use sched_get_priority_min
1234 and sched_get_priority_max to obtain the lower and upper bounds of this
1235 range. Since the PAL also uses a range of values (from Idle [-15] to
1236 Time Critical [+15]), we have to do a mapping from a known range to an
1237 unknown (at compilation) range.
1238 We do this by :
1239 -substracting the minimal PAL priority from the desired priority. this
1240 gives a value between 0 and the PAL priority range
1241 -dividing this value by the PAL priority range. this allows us to
1242 express the desired priority as a floating-point value between 0 and 1
1243 -multiplying this value by the PTHREAD priority range. This gives a
1244 value between 0 and the PTHREAD priority range
1245 -adding the minimal PTHREAD priority range. This will give us a value
1246 between the minimal and maximla pthread priority, which should be
1247 equivalent to the original PAL value.
1248
1249 example : suppose a pthread range 100 to 200, and a desired priority
1250 of 0 (halfway between PAL minimum and maximum)
1251 0 - (IDLE [-15]) = 15
1252 15 / (TIMECRITICAL[15] - IDLE[-15]) = 0.5
1253 0.5 * (pthreadmax[200]-pthreadmin[100]) = 50
1254 50 + pthreadmin[100] = 150 -> halfway between pthread min and max
1255 */
1256 posix_priority = (iNewPriority - THREAD_PRIORITY_IDLE);
1257 posix_priority /= (THREAD_PRIORITY_TIME_CRITICAL - THREAD_PRIORITY_IDLE);
1258 posix_priority *= (max_priority-min_priority);
1259 posix_priority += min_priority;
1260
1261 schedParam.sched_priority = (int)posix_priority;
1262
1263 TRACE("PAL priority %d is mapped to pthread priority %d\n",
1264 iNewPriority, schedParam.sched_priority);
1265
1266 /* Finally, set the new priority into place */
1267 st = pthread_setschedparam(pTargetThread->GetPThreadSelf(), policy, &schedParam);
1268 if (st != 0)
1269 {
1270#if SET_SCHEDPARAM_NEEDS_PRIVS
1271 if (EPERM == st)
1272 {
1273 // UNIXTODO: Should log a warning to the event log
1274 TRACE("Caller does not have OS privileges to call pthread_setschedparam\n");
1275 pTargetThread->m_iThreadPriority = iNewPriority;
1276 goto InternalSetThreadPriorityExit;
1277 }
1278#endif
1279
1280 ASSERT("Unable to set thread priority to %d (error %d)\n", (int)posix_priority, st);
1281 palError = ERROR_INTERNAL_ERROR;
1282 goto InternalSetThreadPriorityExit;
1283 }
1284
1285 pTargetThread->m_iThreadPriority = iNewPriority;
1286
1287InternalSetThreadPriorityExit:
1288
1289 if (NULL != pTargetThread)
1290 {
1291 pTargetThread->Unlock(pThread);
1292 }
1293
1294 if (NULL != pobjThread)
1295 {
1296 pobjThread->ReleaseReference(pThread);
1297 }
1298
1299 return palError;
1300}
1301
1302BOOL
1303CorUnix::GetThreadTimesInternal(
1304 IN HANDLE hThread,
1305 OUT LPFILETIME lpKernelTime,
1306 OUT LPFILETIME lpUserTime)
1307{
1308 __int64 calcTime;
1309 BOOL retval = FALSE;
1310 const __int64 SECS_TO_NS = 1000000000; /* 10^9 */
1311 const __int64 USECS_TO_NS = 1000; /* 10^3 */
1312
1313#if HAVE_MACH_THREADS
1314 thread_basic_info resUsage;
1315 PAL_ERROR palError = NO_ERROR;
1316 CPalThread *pthrCurrent = NULL;
1317 CPalThread *pthrTarget = NULL;
1318 IPalObject *pobjThread = NULL;
1319 mach_msg_type_number_t resUsage_count = THREAD_BASIC_INFO_COUNT;
1320
1321 pthrCurrent = InternalGetCurrentThread();
1322 palError = InternalGetThreadDataFromHandle(
1323 pthrCurrent,
1324 hThread,
1325 0,
1326 &pthrTarget,
1327 &pobjThread
1328 );
1329
1330 if (palError != NO_ERROR)
1331 {
1332 ASSERT("Unable to get thread data from handle %p"
1333 "thread\n", hThread);
1334 SetLastError(ERROR_INTERNAL_ERROR);
1335 goto SetTimesToZero;
1336 }
1337
1338 pthrTarget->Lock(pthrCurrent);
1339
1340 mach_port_t mhThread;
1341 mhThread = pthread_mach_thread_np(pthrTarget->GetPThreadSelf());
1342
1343 kern_return_t status;
1344 status = thread_info(
1345 mhThread,
1346 THREAD_BASIC_INFO,
1347 (thread_info_t)&resUsage,
1348 &resUsage_count);
1349
1350 pthrTarget->Unlock(pthrCurrent);
1351
1352 if (status != KERN_SUCCESS)
1353 {
1354 ASSERT("Unable to get resource usage information for the current "
1355 "thread\n");
1356 SetLastError(ERROR_INTERNAL_ERROR);
1357 goto SetTimesToZero;
1358 }
1359
1360 /* Get the time of user mode execution, in nanoseconds */
1361 calcTime = (__int64)resUsage.user_time.seconds * SECS_TO_NS;
1362 calcTime += (__int64)resUsage.user_time.microseconds * USECS_TO_NS;
1363 /* Assign the time into lpUserTime */
1364 lpUserTime->dwLowDateTime = (DWORD)calcTime;
1365 lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1366
1367 /* Get the time of kernel mode execution, in nanoseconds */
1368 calcTime = (__int64)resUsage.system_time.seconds * SECS_TO_NS;
1369 calcTime += (__int64)resUsage.system_time.microseconds * USECS_TO_NS;
1370 /* Assign the time into lpKernelTime */
1371 lpKernelTime->dwLowDateTime = (DWORD)calcTime;
1372 lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1373
1374 retval = TRUE;
1375
1376 goto GetThreadTimesInternalExit;
1377
1378#elif defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID /* Currently unimplemented */
1379
1380 PAL_ERROR palError;
1381 CPalThread *pThread;
1382 CPalThread *pTargetThread;
1383 IPalObject *pobjThread = NULL;
1384 kvm_t *kd;
1385 int cnt, nlwps;
1386 struct kinfo_lwp *klwp;
1387 int i;
1388 bool found = false;
1389
1390 pThread = InternalGetCurrentThread();
1391
1392 palError = InternalGetThreadDataFromHandle(
1393 pThread,
1394 hThread,
1395 0, // THREAD_GET_CONTEXT
1396 &pTargetThread,
1397 &pobjThread
1398 );
1399 if (palError != NO_ERROR)
1400 {
1401 ASSERT("Unable to get thread data from handle %p"
1402 "thread\n", hThread);
1403 SetLastError(ERROR_INTERNAL_ERROR);
1404 goto SetTimesToZero;
1405 }
1406
1407 kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
1408 if (kd == NULL)
1409 {
1410 ASSERT("kvm_open(3) error");
1411 SetLastError(ERROR_INTERNAL_ERROR);
1412 goto SetTimesToZero;
1413 }
1414
1415 pTargetThread->Lock(pThread);
1416
1417 klwp = kvm_getlwps(kd, getpid(), 0, sizeof(struct kinfo_lwp), &nlwps);
1418 if (klwp == NULL || nlwps < 1)
1419 {
1420 kvm_close(kd);
1421 ASSERT("Unable to get clock from %p thread\n", hThread);
1422 SetLastError(ERROR_INTERNAL_ERROR);
1423 pTargetThread->Unlock(pThread);
1424 goto SetTimesToZero;
1425 }
1426
1427 for (i = 0; i < nlwps; i++)
1428 {
1429 if (klwp[i].l_lid == THREADSilentGetCurrentThreadId())
1430 {
1431 found = true;
1432 break;
1433 }
1434 }
1435
1436 if (!found)
1437 {
1438 kvm_close(kd);
1439 ASSERT("Unable to get clock from %p thread\n", hThread);
1440 SetLastError(ERROR_INTERNAL_ERROR);
1441 pTargetThread->Unlock(pThread);
1442 goto SetTimesToZero;
1443 }
1444
1445 pTargetThread->Unlock(pThread);
1446
1447 kvm_close(kd);
1448
1449 calcTime = (__int64) klwp[i].l_rtime_sec * SECS_TO_NS;
1450 calcTime += (__int64) klwp[i].l_rtime_usec * USECS_TO_NS;
1451 lpUserTime->dwLowDateTime = (DWORD)calcTime;
1452 lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1453
1454 /* NetBSD as of (7.0) doesn't differentiate used time in user/kernel for lwp */
1455 lpKernelTime->dwLowDateTime = 0;
1456 lpKernelTime->dwHighDateTime = 0;
1457
1458 retval = TRUE;
1459 goto GetThreadTimesInternalExit;
1460
1461#else //HAVE_MACH_THREADS
1462
1463 PAL_ERROR palError;
1464 CPalThread *pThread;
1465 CPalThread *pTargetThread;
1466 IPalObject *pobjThread = NULL;
1467 clockid_t cid;
1468
1469 pThread = InternalGetCurrentThread();
1470
1471 palError = InternalGetThreadDataFromHandle(
1472 pThread,
1473 hThread,
1474 0, // THREAD_GET_CONTEXT
1475 &pTargetThread,
1476 &pobjThread
1477 );
1478 if (palError != NO_ERROR)
1479 {
1480 ASSERT("Unable to get thread data from handle %p"
1481 "thread\n", hThread);
1482 SetLastError(ERROR_INTERNAL_ERROR);
1483 goto SetTimesToZero;
1484 }
1485
1486 pTargetThread->Lock(pThread);
1487
1488#if HAVE_PTHREAD_GETCPUCLOCKID
1489 if (pthread_getcpuclockid(pTargetThread->GetPThreadSelf(), &cid) != 0)
1490#endif
1491 {
1492 ASSERT("Unable to get clock from thread\n", hThread);
1493 SetLastError(ERROR_INTERNAL_ERROR);
1494 pTargetThread->Unlock(pThread);
1495 goto SetTimesToZero;
1496 }
1497
1498 struct timespec ts;
1499 if (clock_gettime(cid, &ts) != 0)
1500 {
1501 ASSERT("clock_gettime() failed; errno is %d (%s)\n", errno, strerror(errno));
1502 SetLastError(ERROR_INTERNAL_ERROR);
1503 pTargetThread->Unlock(pThread);
1504 goto SetTimesToZero;
1505 }
1506
1507 pTargetThread->Unlock(pThread);
1508
1509 /* Calculate time in nanoseconds and assign to user time */
1510 calcTime = (__int64) ts.tv_sec * SECS_TO_NS;
1511 calcTime += (__int64) ts.tv_nsec;
1512 lpUserTime->dwLowDateTime = (DWORD)calcTime;
1513 lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1514
1515 /* Set kernel time to zero, for now */
1516 lpKernelTime->dwLowDateTime = 0;
1517 lpKernelTime->dwHighDateTime = 0;
1518
1519 retval = TRUE;
1520 goto GetThreadTimesInternalExit;
1521
1522#endif //HAVE_MACH_THREADS
1523
1524SetTimesToZero:
1525
1526 lpUserTime->dwLowDateTime = 0;
1527 lpUserTime->dwHighDateTime = 0;
1528 lpKernelTime->dwLowDateTime = 0;
1529 lpKernelTime->dwHighDateTime = 0;
1530 goto GetThreadTimesInternalExit;
1531
1532GetThreadTimesInternalExit:
1533 return retval;
1534}
1535
1536/*++
1537Function:
1538 GetThreadTimes
1539
1540See MSDN doc.
1541--*/
1542BOOL
1543PALAPI
1544GetThreadTimes(
1545 IN HANDLE hThread,
1546 OUT LPFILETIME lpCreationTime,
1547 OUT LPFILETIME lpExitTime,
1548 OUT LPFILETIME lpKernelTime,
1549 OUT LPFILETIME lpUserTime)
1550{
1551 PERF_ENTRY(GetThreadTimes);
1552 ENTRY("GetThreadTimes(hThread=%p, lpExitTime=%p, lpKernelTime=%p,"
1553 "lpUserTime=%p)\n",
1554 hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime );
1555
1556 FILETIME KernelTime, UserTime;
1557
1558 BOOL retval = GetThreadTimesInternal(hThread, &KernelTime, &UserTime);
1559
1560 /* Not sure if this still needs to be here */
1561 /*
1562 TRACE ("thread_info User: %ld sec,%ld microsec. Kernel: %ld sec,%ld"
1563 " microsec\n",
1564 resUsage.user_time.seconds, resUsage.user_time.microseconds,
1565 resUsage.system_time.seconds, resUsage.system_time.microseconds);
1566 */
1567
1568 __int64 calcTime;
1569 if (lpUserTime)
1570 {
1571 /* Produce the time in 100s of ns */
1572 calcTime = ((ULONG64)UserTime.dwHighDateTime << 32);
1573 calcTime += (ULONG64)UserTime.dwLowDateTime;
1574 calcTime /= 100;
1575 lpUserTime->dwLowDateTime = (DWORD)calcTime;
1576 lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1577 }
1578 if (lpKernelTime)
1579 {
1580 /* Produce the time in 100s of ns */
1581 calcTime = ((ULONG64)KernelTime.dwHighDateTime << 32);
1582 calcTime += (ULONG64)KernelTime.dwLowDateTime;
1583 calcTime /= 100;
1584 lpKernelTime->dwLowDateTime = (DWORD)calcTime;
1585 lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
1586 }
1587 //Set CreationTime and Exit time to zero for now - maybe change this later?
1588 if (lpCreationTime)
1589 {
1590 lpCreationTime->dwLowDateTime = 0;
1591 lpCreationTime->dwHighDateTime = 0;
1592 }
1593
1594 if (lpExitTime)
1595 {
1596 lpExitTime->dwLowDateTime = 0;
1597 lpExitTime->dwHighDateTime = 0;
1598 }
1599
1600 LOGEXIT("GetThreadTimes returns BOOL %d\n", retval);
1601 PERF_EXIT(GetThreadTimes);
1602 return (retval);
1603}
1604
1605
1606
1607void *
1608CPalThread::ThreadEntry(
1609 void *pvParam
1610 )
1611{
1612 PAL_ERROR palError;
1613 CPalThread *pThread;
1614 PTHREAD_START_ROUTINE pfnStartRoutine;
1615 LPVOID pvPar;
1616 DWORD retValue;
1617
1618 pThread = reinterpret_cast<CPalThread*>(pvParam);
1619
1620 if (NULL == pThread)
1621 {
1622 ASSERT("THREAD pointer is NULL!\n");
1623 goto fail;
1624 }
1625
1626#if !HAVE_MACH_EXCEPTIONS
1627 if (!pThread->EnsureSignalAlternateStack())
1628 {
1629 ASSERT("Cannot allocate alternate stack for SIGSEGV!\n");
1630 goto fail;
1631 }
1632#endif // !HAVE_MACH_EXCEPTIONS
1633
1634#if defined(FEATURE_PAL_SXS) && defined(_DEBUG)
1635 // We cannot assert yet, as we haven't set in this thread into the TLS, and so __ASSERT_ENTER
1636 // will fail if the assert fails and we'll crash.
1637 //_ASSERT_MSG(pThread->m_fInPal == 1, "New threads should always be in the PAL upon ThreadEntry.\n");
1638 if (g_Dbg_asserts_enabled && pThread->m_fInPal != 1)
1639 DebugBreak();
1640#endif // FEATURE_PAL_SXS && _DEBUG
1641
1642 pThread->m_threadId = THREADSilentGetCurrentThreadId();
1643 pThread->m_pthreadSelf = pthread_self();
1644#if HAVE_MACH_THREADS
1645 pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf);
1646#endif
1647#if HAVE_THREAD_SELF
1648 pThread->m_dwLwpId = (DWORD) thread_self();
1649#elif HAVE__LWP_SELF
1650 pThread->m_dwLwpId = (DWORD) _lwp_self();
1651#else
1652 pThread->m_dwLwpId = 0;
1653#endif
1654
1655 palError = pThread->RunPostCreateInitializers();
1656 if (NO_ERROR != palError)
1657 {
1658 ASSERT("Error %i initializing thread data (post creation)\n", palError);
1659 goto fail;
1660 }
1661
1662 // Check if the thread should be started suspended.
1663 if (pThread->GetCreateSuspended())
1664 {
1665 palError = pThread->suspensionInfo.InternalSuspendNewThreadFromData(pThread);
1666 if (NO_ERROR != palError)
1667 {
1668 ASSERT("Error %i attempting to suspend new thread\n", palError);
1669 goto fail;
1670 }
1671
1672 //
1673 // We need to run any APCs that have already been queued for
1674 // this thread.
1675 //
1676
1677 (void) g_pSynchronizationManager->DispatchPendingAPCs(pThread);
1678 }
1679 else
1680 {
1681 //
1682 // All startup operations that might have failed have succeeded,
1683 // so thread creation is successful. Let CreateThread return.
1684 //
1685
1686 pThread->SetStartStatus(TRUE);
1687 }
1688
1689 pThread->synchronizationInfo.SetThreadState(TS_RUNNING);
1690
1691 if (UserCreatedThread == pThread->GetThreadType())
1692 {
1693 /* Inform all loaded modules that a thread has been created */
1694 /* note : no need to take a critical section to serialize here; the loader
1695 will take the module critical section */
1696 LOADCallDllMain(DLL_THREAD_ATTACH, NULL);
1697 }
1698
1699#ifdef PAL_PERF
1700 PERFAllocThreadInfo();
1701 PERFEnableThreadProfile(UserCreatedThread != pThread->GetThreadType());
1702#endif
1703
1704 /* call the startup routine */
1705 pfnStartRoutine = pThread->GetStartAddress();
1706 pvPar = pThread->GetStartParameter();
1707
1708 retValue = (*pfnStartRoutine)(pvPar);
1709
1710 TRACE("Thread exited (%u)\n", retValue);
1711 ExitThread(retValue);
1712
1713 /* Note: never get here */
1714 ASSERT("ExitThread failed!\n");
1715 for (;;);
1716
1717fail:
1718
1719 //
1720 // Notify InternalCreateThread that a failure occurred
1721 //
1722
1723 if (NULL != pThread)
1724 {
1725 pThread->synchronizationInfo.SetThreadState(TS_FAILED);
1726 pThread->SetStartStatus(FALSE);
1727 }
1728
1729 /* do not call ExitThread : we don't want to call DllMain(), and the thread
1730 isn't in a clean state (e.g. lpThread isn't in TLS). the cleanup work
1731 above should release all resources */
1732 return NULL;
1733}
1734
1735/*++
1736Function:
1737 CreateThreadData
1738
1739Abstract:
1740 Create the CPalThread for the startup thread
1741 or another external thread entering the PAL
1742 for the first time
1743
1744Parameters:
1745 ppThread - on success, receives the CPalThread
1746
1747Return:
1748 PAL_ERROR
1749--*/
1750
1751PAL_ERROR
1752CorUnix::CreateThreadData(
1753 CPalThread **ppThread
1754 )
1755{
1756 PAL_ERROR palError = NO_ERROR;
1757 CPalThread *pThread = NULL;
1758
1759 /* Create the thread object */
1760 pThread = AllocTHREAD();
1761
1762 if (NULL == pThread)
1763 {
1764 palError = ERROR_OUTOFMEMORY;
1765 goto CreateThreadDataExit;
1766 }
1767
1768 palError = pThread->RunPreCreateInitializers();
1769
1770 if (NO_ERROR != palError)
1771 {
1772 goto CreateThreadDataExit;
1773 }
1774
1775 pThread->SetLastError(0);
1776
1777 pThread->m_threadId = THREADSilentGetCurrentThreadId();
1778 pThread->m_pthreadSelf = pthread_self();
1779#if HAVE_MACH_THREADS
1780 pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf);
1781#endif
1782#if HAVE_THREAD_SELF
1783 pThread->m_dwLwpId = (DWORD) thread_self();
1784#elif HAVE__LWP_SELF
1785 pThread->m_dwLwpId = (DWORD) _lwp_self();
1786#else
1787 pThread->m_dwLwpId = 0;
1788#endif
1789
1790 palError = pThread->RunPostCreateInitializers();
1791 if (NO_ERROR != palError)
1792 {
1793 goto CreateThreadDataExit;
1794 }
1795
1796 *ppThread = pThread;
1797
1798CreateThreadDataExit:
1799
1800 if (NO_ERROR != palError)
1801 {
1802 if (NULL != pThread)
1803 {
1804 pThread->ReleaseThreadReference();
1805 }
1806 }
1807
1808 return palError;
1809}
1810
1811/*++
1812Function:
1813 CreateThreadData
1814
1815Abstract:
1816 Creates the IPalObject for a thread, storing
1817 the reference in the CPalThread
1818
1819Parameters:
1820 pThread - the thread data for the creating thread
1821 pNewThread - the thread data for the thread being initialized
1822
1823Return:
1824 PAL_ERROR
1825--*/
1826
1827PAL_ERROR
1828CorUnix::CreateThreadObject(
1829 CPalThread *pThread,
1830 CPalThread *pNewThread,
1831 HANDLE *phThread
1832 )
1833{
1834 PAL_ERROR palError = NO_ERROR;
1835 IPalObject *pobjThread = NULL;
1836 IDataLock *pDataLock;
1837 HANDLE hThread = NULL;
1838 CThreadProcessLocalData *pLocalData = NULL;
1839 CObjectAttributes oa;
1840 BOOL fThreadDataStoredInObject = FALSE;
1841 IPalObject *pobjRegisteredThread = NULL;
1842
1843 //
1844 // Create the IPalObject for the thread
1845 //
1846
1847 palError = g_pObjectManager->AllocateObject(
1848 pThread,
1849 &otThread,
1850 &oa,
1851 &pobjThread
1852 );
1853
1854 if (NO_ERROR != palError)
1855 {
1856 goto CreateThreadObjectExit;
1857 }
1858
1859 //
1860 // Store the CPalThread inside of the IPalObject
1861 //
1862
1863 palError = pobjThread->GetProcessLocalData(
1864 pThread,
1865 WriteLock,
1866 &pDataLock,
1867 reinterpret_cast<void **>(&pLocalData)
1868 );
1869
1870 if (NO_ERROR != palError)
1871 {
1872 goto CreateThreadObjectExit;
1873 }
1874
1875 pLocalData->pThread = pNewThread;
1876 pDataLock->ReleaseLock(pThread, TRUE);
1877 fThreadDataStoredInObject = TRUE;
1878
1879 //
1880 // Register the IPalObject (obtaining a handle)
1881 //
1882
1883 palError = g_pObjectManager->RegisterObject(
1884 pThread,
1885 pobjThread,
1886 &aotThread,
1887 0, //THREAD_ALL_ACCESS,
1888 &hThread,
1889 &pobjRegisteredThread
1890 );
1891
1892 //
1893 // pobjThread is invalidated by the call to RegisterObject, so NULL
1894 // it out here to prevent it from being released
1895 //
1896
1897 pobjThread = NULL;
1898
1899 if (NO_ERROR != palError)
1900 {
1901 goto CreateThreadObjectExit;
1902 }
1903
1904 //
1905 // Store the registered object inside of the thread object,
1906 // adding a reference for the thread itself
1907 //
1908
1909 pNewThread->m_pThreadObject = pobjRegisteredThread;
1910 pNewThread->m_pThreadObject->AddReference();
1911
1912 *phThread = hThread;
1913
1914CreateThreadObjectExit:
1915
1916 if (NO_ERROR != palError)
1917 {
1918 if (NULL != hThread)
1919 {
1920 g_pObjectManager->RevokeHandle(pThread, hThread);
1921 }
1922
1923 if (NULL != pNewThread->m_pThreadObject)
1924 {
1925 //
1926 // Release the new thread's reference on the underlying thread
1927 // object
1928 //
1929
1930 pNewThread->m_pThreadObject->ReleaseReference(pThread);
1931 }
1932
1933 if (!fThreadDataStoredInObject)
1934 {
1935 //
1936 // The CPalThread for the new thread was never stored in
1937 // an IPalObject instance, so we need to release the initial
1938 // reference here. (If it has been stored it will get freed in
1939 // the owning object's cleanup routine)
1940 //
1941
1942 pNewThread->ReleaseThreadReference();
1943 }
1944 }
1945
1946 if (NULL != pobjThread)
1947 {
1948 pobjThread->ReleaseReference(pThread);
1949 }
1950
1951 if (NULL != pobjRegisteredThread)
1952 {
1953 pobjRegisteredThread->ReleaseReference(pThread);
1954 }
1955
1956 return palError;
1957}
1958
1959PAL_ERROR
1960CorUnix::InternalCreateDummyThread(
1961 CPalThread *pThread,
1962 LPSECURITY_ATTRIBUTES lpThreadAttributes,
1963 CPalThread **ppDummyThread,
1964 HANDLE *phThread
1965 )
1966{
1967 PAL_ERROR palError = NO_ERROR;
1968 CPalThread *pDummyThread = NULL;
1969 IPalObject *pobjThread = NULL;
1970 IPalObject *pobjThreadRegistered = NULL;
1971 IDataLock *pDataLock;
1972 CThreadProcessLocalData *pLocalData;
1973 CObjectAttributes oa(NULL, lpThreadAttributes);
1974 bool fThreadDataStoredInObject = FALSE;
1975
1976 pDummyThread = AllocTHREAD();
1977 if (NULL == pDummyThread)
1978 {
1979 palError = ERROR_OUTOFMEMORY;
1980 goto InternalCreateDummyThreadExit;
1981 }
1982
1983 pDummyThread->m_fIsDummy = TRUE;
1984
1985 palError = g_pObjectManager->AllocateObject(
1986 pThread,
1987 &otThread,
1988 &oa,
1989 &pobjThread
1990 );
1991
1992 if (NO_ERROR != palError)
1993 {
1994 goto InternalCreateDummyThreadExit;
1995 }
1996
1997 palError = pobjThread->GetProcessLocalData(
1998 pThread,
1999 WriteLock,
2000 &pDataLock,
2001 reinterpret_cast<void **>(&pLocalData)
2002 );
2003
2004 if (NO_ERROR != palError)
2005 {
2006 goto InternalCreateDummyThreadExit;
2007 }
2008
2009 pLocalData->pThread = pDummyThread;
2010 pDataLock->ReleaseLock(pThread, TRUE);
2011 fThreadDataStoredInObject = TRUE;
2012
2013 palError = g_pObjectManager->RegisterObject(
2014 pThread,
2015 pobjThread,
2016 &aotThread,
2017 0, // THREAD_ALL_ACCESS
2018 phThread,
2019 &pobjThreadRegistered
2020 );
2021
2022 //
2023 // pobjThread is invalidated by the above call, so NULL
2024 // it out here
2025 //
2026
2027 pobjThread = NULL;
2028
2029 if (NO_ERROR != palError)
2030 {
2031 goto InternalCreateDummyThreadExit;
2032 }
2033
2034 //
2035 // Note the we do NOT store the registered object for the
2036 // thread w/in pDummyThread. Since this thread is not actually
2037 // executing that reference would never be released (and thus
2038 // the thread object would never be cleaned up...)
2039 //
2040
2041 *ppDummyThread = pDummyThread;
2042
2043InternalCreateDummyThreadExit:
2044
2045 if (NULL != pobjThreadRegistered)
2046 {
2047 pobjThreadRegistered->ReleaseReference(pThread);
2048 }
2049
2050 if (NULL != pobjThread)
2051 {
2052 pobjThread->ReleaseReference(pThread);
2053 }
2054
2055 if (NO_ERROR != palError
2056 && NULL != pDummyThread
2057 && !fThreadDataStoredInObject)
2058 {
2059 pDummyThread->ReleaseThreadReference();
2060 }
2061
2062 return palError;
2063}
2064
2065PAL_ERROR
2066CorUnix::InternalGetThreadDataFromHandle(
2067 CPalThread *pThread,
2068 HANDLE hThread,
2069 DWORD dwRightsRequired,
2070 CPalThread **ppTargetThread,
2071 IPalObject **ppobjThread
2072 )
2073{
2074 PAL_ERROR palError = NO_ERROR;
2075 IPalObject *pobj;
2076 IDataLock *pLock;
2077 CThreadProcessLocalData *pData;
2078
2079 *ppobjThread = NULL;
2080
2081 if (hPseudoCurrentThread == hThread)
2082 {
2083 *ppTargetThread = pThread;
2084 }
2085 else
2086 {
2087 palError = g_pObjectManager->ReferenceObjectByHandle(
2088 pThread,
2089 hThread,
2090 &aotThread,
2091 dwRightsRequired,
2092 &pobj
2093 );
2094
2095 if (NO_ERROR == palError)
2096 {
2097 palError = pobj->GetProcessLocalData(
2098 pThread,
2099 ReadLock,
2100 &pLock,
2101 reinterpret_cast<void**>(&pData)
2102 );
2103
2104 if (NO_ERROR == palError)
2105 {
2106 *ppTargetThread = pData->pThread;
2107 pLock->ReleaseLock(pThread, FALSE);
2108
2109 //
2110 // Transfer object reference to out param
2111 //
2112
2113 *ppobjThread = pobj;
2114 }
2115 else
2116 {
2117 pobj->ReleaseReference(pThread);
2118 }
2119 }
2120 }
2121
2122 return palError;
2123}
2124
2125PAL_ERROR
2126CPalThread::RunPreCreateInitializers(
2127 void
2128 )
2129{
2130 PAL_ERROR palError = NO_ERROR;
2131 int iError;
2132
2133 //
2134 // First, perform initialization of CPalThread private members
2135 //
2136
2137 InternalInitializeCriticalSection(&m_csLock);
2138 m_fLockInitialized = TRUE;
2139
2140 iError = pthread_mutex_init(&m_startMutex, NULL);
2141 if (0 != iError)
2142 {
2143 goto RunPreCreateInitializersExit;
2144 }
2145
2146 iError = pthread_cond_init(&m_startCond, NULL);
2147 if (0 != iError)
2148 {
2149 pthread_mutex_destroy(&m_startMutex);
2150 goto RunPreCreateInitializersExit;
2151 }
2152
2153 m_fStartItemsInitialized = TRUE;
2154
2155 //
2156 // Call the pre-create initializers for embedded classes
2157 //
2158
2159 palError = synchronizationInfo.InitializePreCreate();
2160 if (NO_ERROR != palError)
2161 {
2162 goto RunPreCreateInitializersExit;
2163 }
2164
2165 palError = suspensionInfo.InitializePreCreate();
2166 if (NO_ERROR != palError)
2167 {
2168 goto RunPreCreateInitializersExit;
2169 }
2170
2171 palError = sehInfo.InitializePreCreate();
2172 if (NO_ERROR != palError)
2173 {
2174 goto RunPreCreateInitializersExit;
2175 }
2176
2177 palError = tlsInfo.InitializePreCreate();
2178 if (NO_ERROR != palError)
2179 {
2180 goto RunPreCreateInitializersExit;
2181 }
2182
2183 palError = apcInfo.InitializePreCreate();
2184 if (NO_ERROR != palError)
2185 {
2186 goto RunPreCreateInitializersExit;
2187 }
2188
2189 palError = crtInfo.InitializePreCreate();
2190 if (NO_ERROR != palError)
2191 {
2192 goto RunPreCreateInitializersExit;
2193 }
2194
2195RunPreCreateInitializersExit:
2196
2197 return palError;
2198}
2199
2200CPalThread::~CPalThread()
2201{
2202 // @UNIXTODO: This is our last chance to unlink our Mach exception handler from the pseudo-chain we're trying
2203 // to maintain. Unfortunately we don't have enough data or control to do this at all well (and we can't
2204 // guarantee that another component hasn't chained to us, about which we can do nothing). If the kernel or
2205 // another component forwards an exception notification to us for this thread things will go badly (we'll
2206 // terminate the process when trying to look up this CPalThread in order to find forwarding information).
2207 // On the flip side I don't believe we'll get here currently unless the thread has been terminated (in
2208 // which case it's not an issue). If we start supporting unload or early disposal of CPalThread objects
2209 // (say when we return from an outer reverse p/invoke) then we'll need to revisit this. But hopefully by
2210 // then we'll have an alternative design for handling hardware exceptions.
2211
2212 if (m_fLockInitialized)
2213 {
2214 InternalDeleteCriticalSection(&m_csLock);
2215 }
2216
2217 if (m_fStartItemsInitialized)
2218 {
2219 int iError;
2220
2221 iError = pthread_cond_destroy(&m_startCond);
2222 _ASSERTE(0 == iError);
2223
2224 iError = pthread_mutex_destroy(&m_startMutex);
2225 _ASSERTE(0 == iError);
2226 }
2227}
2228
2229void
2230CPalThread::AddThreadReference(
2231 void
2232 )
2233{
2234 InterlockedIncrement(&m_lRefCount);
2235}
2236
2237void
2238CPalThread::ReleaseThreadReference(
2239 void
2240 )
2241{
2242 LONG lRefCount = InterlockedDecrement(&m_lRefCount);
2243 _ASSERT_MSG(lRefCount >= 0, "Released a thread and ended with a negative refcount (%ld)\n", lRefCount);
2244 if (0 == lRefCount)
2245 {
2246 FreeTHREAD(this);
2247 }
2248
2249}
2250
2251PAL_ERROR
2252CPalThread::RunPostCreateInitializers(
2253 void
2254 )
2255{
2256 PAL_ERROR palError = NO_ERROR;
2257
2258 //
2259 // Call the post-create initializers for embedded classes
2260 //
2261
2262 palError = synchronizationInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2263 if (NO_ERROR != palError)
2264 {
2265 goto RunPostCreateInitializersExit;
2266 }
2267
2268 palError = suspensionInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2269 if (NO_ERROR != palError)
2270 {
2271 goto RunPostCreateInitializersExit;
2272 }
2273
2274 palError = sehInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2275 if (NO_ERROR != palError)
2276 {
2277 goto RunPostCreateInitializersExit;
2278 }
2279
2280 palError = tlsInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2281 if (NO_ERROR != palError)
2282 {
2283 goto RunPostCreateInitializersExit;
2284 }
2285
2286 palError = apcInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2287 if (NO_ERROR != palError)
2288 {
2289 goto RunPostCreateInitializersExit;
2290 }
2291
2292 palError = crtInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
2293 if (NO_ERROR != palError)
2294 {
2295 goto RunPostCreateInitializersExit;
2296 }
2297
2298#ifdef FEATURE_PAL_SXS
2299 _ASSERTE(m_fInPal);
2300 palError = SEHEnable(this);
2301 if (NO_ERROR != palError)
2302 {
2303 goto RunPostCreateInitializersExit;
2304 }
2305#endif // FEATURE_PAL_SXS
2306
2307RunPostCreateInitializersExit:
2308
2309 return palError;
2310}
2311
2312void
2313CPalThread::SetStartStatus(
2314 bool fStartSucceeded
2315 )
2316{
2317 int iError;
2318
2319#if _DEBUG
2320 if (m_fStartStatusSet)
2321 {
2322 ASSERT("Multiple calls to CPalThread::SetStartStatus\n");
2323 }
2324#endif
2325
2326 //
2327 // This routine may get called from CPalThread::ThreadEntry
2328 //
2329 // If we've reached this point there are no further thread
2330 // suspensions that happen at creation time, so reset
2331 // m_bCreateSuspended
2332 //
2333
2334 m_bCreateSuspended = FALSE;
2335
2336 iError = pthread_mutex_lock(&m_startMutex);
2337 if (0 != iError)
2338 {
2339 ASSERT("pthread primitive failure\n");
2340 // bugcheck?
2341 }
2342
2343 m_fStartStatus = fStartSucceeded;
2344 m_fStartStatusSet = TRUE;
2345
2346 iError = pthread_cond_signal(&m_startCond);
2347 if (0 != iError)
2348 {
2349 ASSERT("pthread primitive failure\n");
2350 // bugcheck?
2351 }
2352
2353 iError = pthread_mutex_unlock(&m_startMutex);
2354 if (0 != iError)
2355 {
2356 ASSERT("pthread primitive failure\n");
2357 // bugcheck?
2358 }
2359}
2360
2361bool
2362CPalThread::WaitForStartStatus(
2363 void
2364 )
2365{
2366 int iError;
2367
2368 iError = pthread_mutex_lock(&m_startMutex);
2369 if (0 != iError)
2370 {
2371 ASSERT("pthread primitive failure\n");
2372 // bugcheck?
2373 }
2374
2375 while (!m_fStartStatusSet)
2376 {
2377 iError = pthread_cond_wait(&m_startCond, &m_startMutex);
2378 if (0 != iError)
2379 {
2380 ASSERT("pthread primitive failure\n");
2381 // bugcheck?
2382 }
2383 }
2384
2385 iError = pthread_mutex_unlock(&m_startMutex);
2386 if (0 != iError)
2387 {
2388 ASSERT("pthread primitive failure\n");
2389 // bugcheck?
2390 }
2391
2392 return m_fStartStatus;
2393}
2394
2395#if !HAVE_MACH_EXCEPTIONS
2396/*++
2397Function :
2398 EnsureSignalAlternateStack
2399
2400 Ensure that alternate stack for signal handling is allocated for the current thread
2401
2402Parameters :
2403 None
2404
2405Return :
2406 TRUE in case of a success, FALSE otherwise
2407--*/
2408BOOL
2409CPalThread::EnsureSignalAlternateStack()
2410{
2411 int st = 0;
2412
2413 if (g_registered_signal_handlers)
2414 {
2415 stack_t oss;
2416
2417 // Query the current alternate signal stack
2418 st = sigaltstack(NULL, &oss);
2419 if ((st == 0) && (oss.ss_flags == SS_DISABLE))
2420 {
2421 // There is no alternate stack for SIGSEGV handling installed yet so allocate one
2422
2423 // We include the size of the SignalHandlerWorkerReturnPoint in the alternate stack size since the
2424 // context contained in it is large and the SIGSTKSZ was not sufficient on ARM64 during testing.
2425 int altStackSize = SIGSTKSZ + ALIGN_UP(sizeof(SignalHandlerWorkerReturnPoint), 16) + GetVirtualPageSize();
2426#ifdef HAS_ASAN
2427 // Asan also uses alternate stack so we increase its size on the SIGSTKSZ * 4 that enough for asan
2428 // (see kAltStackSize in compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc)
2429 altStackSize += SIGSTKSZ * 4;
2430#endif
2431 altStackSize = ALIGN_UP(altStackSize, GetVirtualPageSize());
2432 void* altStack = mmap(NULL, altStackSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_STACK | MAP_PRIVATE, -1, 0);
2433 if (altStack != MAP_FAILED)
2434 {
2435 // create a guard page for the alternate stack
2436 st = mprotect(altStack, GetVirtualPageSize(), PROT_NONE);
2437 if (st == 0)
2438 {
2439 stack_t ss;
2440 ss.ss_sp = (char*)altStack;
2441 ss.ss_size = altStackSize;
2442 ss.ss_flags = 0;
2443 st = sigaltstack(&ss, NULL);
2444 }
2445
2446 if (st == 0)
2447 {
2448 m_alternateStack = altStack;
2449 }
2450 else
2451 {
2452 int st2 = munmap(altStack, altStackSize);
2453 _ASSERTE(st2 == 0);
2454 }
2455 }
2456 }
2457 }
2458
2459 return (st == 0);
2460}
2461
2462/*++
2463Function :
2464 FreeSignalAlternateStack
2465
2466 Free alternate stack for signal handling
2467
2468Parameters :
2469 None
2470
2471Return :
2472 None
2473--*/
2474void
2475CPalThread::FreeSignalAlternateStack()
2476{
2477 void *altstack = m_alternateStack;
2478 m_alternateStack = nullptr;
2479
2480 if (altstack != nullptr)
2481 {
2482 stack_t ss, oss;
2483 // The man page for sigaltstack says that when the ss.ss_flags is set to SS_DISABLE,
2484 // all other ss fields are ignored. However, MUSL implementation checks that the
2485 // ss_size is >= MINSIGSTKSZ even in this case.
2486 ss.ss_size = MINSIGSTKSZ;
2487 ss.ss_flags = SS_DISABLE;
2488 int st = sigaltstack(&ss, &oss);
2489 if ((st == 0) && (oss.ss_flags != SS_DISABLE))
2490 {
2491 // Make sure this altstack is this PAL's before freeing.
2492 if (oss.ss_sp == altstack)
2493 {
2494 int st = munmap(oss.ss_sp, oss.ss_size);
2495 _ASSERTE(st == 0);
2496 }
2497 }
2498 }
2499}
2500
2501#endif // !HAVE_MACH_EXCEPTIONS
2502
2503/* IncrementEndingThreadCount and DecrementEndingThreadCount are used
2504to control a global counter that indicates if any threads are about to die.
2505Once a thread's state is set to TS_DONE, it cannot be suspended. However,
2506the dying thread can still access PAL resources, which is dangerous if the
2507thread dies during PAL cleanup. To avoid this, the shutdown thread calls
2508WaitForEndingThreads after suspending all other threads. WaitForEndingThreads
2509uses a condition variable along with the global counter to wait for remaining
2510PAL threads to die before proceeding with cleanup. As threads die, they
2511decrement the counter and signal the condition variable. */
2512
2513void
2514IncrementEndingThreadCount(
2515 void
2516 )
2517{
2518 int iError;
2519
2520 iError = pthread_mutex_lock(&ptmEndThread);
2521 _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
2522
2523 iEndingThreads++;
2524
2525 iError = pthread_mutex_unlock(&ptmEndThread);
2526 _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
2527}
2528
2529void
2530DecrementEndingThreadCount(
2531 void
2532 )
2533{
2534 int iError;
2535
2536 iError = pthread_mutex_lock(&ptmEndThread);
2537 _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
2538
2539 iEndingThreads--;
2540 _ASSERTE(iEndingThreads >= 0);
2541
2542 if (iEndingThreads == 0)
2543 {
2544 iError = pthread_cond_signal(&ptcEndThread);
2545 _ASSERT_MSG(iError == 0, "pthread_cond_signal returned %d\n", iError);
2546 }
2547
2548 iError = pthread_mutex_unlock(&ptmEndThread);
2549 _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
2550}
2551
2552void
2553WaitForEndingThreads(
2554 void
2555 )
2556{
2557 int iError;
2558
2559 iError = pthread_mutex_lock(&ptmEndThread);
2560 _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
2561
2562 while (iEndingThreads > 0)
2563 {
2564 iError = pthread_cond_wait(&ptcEndThread, &ptmEndThread);
2565 _ASSERT_MSG(iError == 0, "pthread_cond_wait returned %d\n", iError);
2566 }
2567
2568 iError = pthread_mutex_unlock(&ptmEndThread);
2569 _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
2570}
2571
2572PAL_ERROR
2573CorUnix::InitializeEndingThreadsData(
2574 void
2575 )
2576{
2577 PAL_ERROR palError = ERROR_INTERNAL_ERROR;
2578 int iError;
2579
2580 iError = pthread_mutex_init(&ptmEndThread, NULL);
2581 if (0 != iError)
2582 {
2583 goto InitializeEndingThreadsDataExit;
2584 }
2585
2586 iError = pthread_cond_init(&ptcEndThread, NULL);
2587 if (0 != iError)
2588 {
2589 //
2590 // Don't bother checking the return value of pthread_mutex_destroy
2591 // since PAL initialization will now fail.
2592 //
2593
2594 pthread_mutex_destroy(&ptmEndThread);
2595 goto InitializeEndingThreadsDataExit;
2596 }
2597
2598 palError = NO_ERROR;
2599
2600InitializeEndingThreadsDataExit:
2601
2602 return palError;
2603}
2604
2605void
2606ThreadCleanupRoutine(
2607 CPalThread *pThread,
2608 IPalObject *pObjectToCleanup,
2609 bool fShutdown,
2610 bool fCleanupSharedState
2611 )
2612{
2613 CThreadProcessLocalData *pThreadData = NULL;
2614 CPalThread *pThreadToCleanup = NULL;
2615 IDataLock *pDataLock = NULL;
2616 PAL_ERROR palError = NO_ERROR;
2617
2618 //
2619 // Free the CPalThread data for the passed in thread
2620 //
2621
2622 palError = pObjectToCleanup->GetProcessLocalData(
2623 pThread,
2624 WriteLock,
2625 &pDataLock,
2626 reinterpret_cast<void**>(&pThreadData)
2627 );
2628
2629 if (NO_ERROR == palError)
2630 {
2631 //
2632 // Note that we may be cleaning up the data for the calling
2633 // thread (i.e., pThread == pThreadToCleanup), so the release
2634 // of the thread reference needs to be the last thing that
2635 // we do (though in that case it's very likely that the person
2636 // calling us will be holding an extra reference to allow
2637 // for the thread data to be available while the rest of the
2638 // object cleanup takes place).
2639 //
2640
2641 pThreadToCleanup = pThreadData->pThread;
2642 pThreadData->pThread = NULL;
2643 pDataLock->ReleaseLock(pThread, TRUE);
2644 pThreadToCleanup->ReleaseThreadReference();
2645 }
2646 else
2647 {
2648 ASSERT("Unable to obtain thread data");
2649 }
2650
2651}
2652
2653PAL_ERROR
2654ThreadInitializationRoutine(
2655 CPalThread *pThread,
2656 CObjectType *pObjectType,
2657 void *pImmutableData,
2658 void *pSharedData,
2659 void *pProcessLocalData
2660 )
2661{
2662 return NO_ERROR;
2663}
2664
2665// Get base address of the current thread's stack
2666void *
2667CPalThread::GetStackBase()
2668{
2669 void* stackBase;
2670#ifdef _TARGET_MAC64
2671 // This is a Mac specific method
2672 stackBase = pthread_get_stackaddr_np(pthread_self());
2673#else
2674 pthread_attr_t attr;
2675 void* stackAddr;
2676 size_t stackSize;
2677 int status;
2678
2679 pthread_t thread = pthread_self();
2680
2681 status = pthread_attr_init(&attr);
2682 _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
2683
2684#if HAVE_PTHREAD_ATTR_GET_NP
2685 status = pthread_attr_get_np(thread, &attr);
2686#elif HAVE_PTHREAD_GETATTR_NP
2687 status = pthread_getattr_np(thread, &attr);
2688#else
2689#error Dont know how to get thread attributes on this platform!
2690#endif
2691 _ASSERT_MSG(status == 0, "pthread_getattr_np call failed");
2692
2693 status = pthread_attr_getstack(&attr, &stackAddr, &stackSize);
2694 _ASSERT_MSG(status == 0, "pthread_attr_getstack call failed");
2695
2696 status = pthread_attr_destroy(&attr);
2697 _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
2698
2699 stackBase = (void*)((size_t)stackAddr + stackSize);
2700#endif
2701
2702 return stackBase;
2703}
2704
2705// Get limit address of the current thread's stack
2706void *
2707CPalThread::GetStackLimit()
2708{
2709 void* stackLimit;
2710#ifdef _TARGET_MAC64
2711 // This is a Mac specific method
2712 stackLimit = ((BYTE *)pthread_get_stackaddr_np(pthread_self()) -
2713 pthread_get_stacksize_np(pthread_self()));
2714#else
2715 pthread_attr_t attr;
2716 size_t stackSize;
2717 int status;
2718
2719 pthread_t thread = pthread_self();
2720
2721 status = pthread_attr_init(&attr);
2722 _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
2723
2724#if HAVE_PTHREAD_ATTR_GET_NP
2725 status = pthread_attr_get_np(thread, &attr);
2726#elif HAVE_PTHREAD_GETATTR_NP
2727 status = pthread_getattr_np(thread, &attr);
2728#else
2729#error Dont know how to get thread attributes on this platform!
2730#endif
2731 _ASSERT_MSG(status == 0, "pthread_getattr_np call failed");
2732
2733 status = pthread_attr_getstack(&attr, &stackLimit, &stackSize);
2734 _ASSERT_MSG(status == 0, "pthread_attr_getstack call failed");
2735
2736 status = pthread_attr_destroy(&attr);
2737 _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
2738#endif
2739
2740 return stackLimit;
2741}
2742
2743// Get cached base address of this thread's stack
2744// Can be called only for the current thread.
2745void *
2746CPalThread::GetCachedStackBase()
2747{
2748 _ASSERT_MSG(this == InternalGetCurrentThread(), "CPalThread::GetStackBase called from foreign thread");
2749
2750 if (m_stackBase == NULL)
2751 {
2752 m_stackBase = GetStackBase();
2753 }
2754
2755 return m_stackBase;
2756}
2757
2758// Get cached limit address of this thread's stack.
2759// Can be called only for the current thread.
2760void *
2761CPalThread::GetCachedStackLimit()
2762{
2763 _ASSERT_MSG(this == InternalGetCurrentThread(), "CPalThread::GetCachedStackLimit called from foreign thread");
2764
2765 if (m_stackLimit == NULL)
2766 {
2767 m_stackLimit = GetStackLimit();
2768 }
2769
2770 return m_stackLimit;
2771}
2772
2773void *
2774PALAPI
2775PAL_GetStackBase()
2776{
2777 CPalThread* thread = InternalGetCurrentThread();
2778 return thread->GetCachedStackBase();
2779}
2780
2781void *
2782PALAPI
2783PAL_GetStackLimit()
2784{
2785 CPalThread* thread = InternalGetCurrentThread();
2786 return thread->GetCachedStackLimit();
2787}
2788
2789PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread);
2790
2791/*++
2792Function:
2793 PAL_SetActivationFunction
2794
2795 Register an activation function that gets called when an activation is injected
2796 into a thread.
2797
2798Parameters:
2799 pActivationFunction - activation function
2800 pSafeActivationCheckFunction - function to check if an activation can be safely
2801 injected at a specified context
2802Return value:
2803 None
2804--*/
2805PALIMPORT
2806VOID
2807PALAPI
2808PAL_SetActivationFunction(
2809 IN PAL_ActivationFunction pActivationFunction,
2810 IN PAL_SafeActivationCheckFunction pSafeActivationCheckFunction)
2811{
2812 g_activationFunction = pActivationFunction;
2813 g_safeActivationCheckFunction = pSafeActivationCheckFunction;
2814}
2815
2816/*++
2817Function:
2818PAL_InjectActivation
2819
2820Interrupt the specified thread and have it call an activation function registered
2821using the PAL_SetActivationFunction
2822
2823Parameters:
2824hThread - handle of the target thread
2825
2826Return:
2827TRUE if it succeeded, FALSE otherwise.
2828--*/
2829BOOL
2830PALAPI
2831PAL_InjectActivation(
2832 IN HANDLE hThread)
2833{
2834 PERF_ENTRY(PAL_InjectActivation);
2835 ENTRY("PAL_InjectActivation(hThread=%p)\n", hThread);
2836
2837 CPalThread *pCurrentThread;
2838 CPalThread *pTargetThread;
2839 IPalObject *pobjThread = NULL;
2840
2841 pCurrentThread = InternalGetCurrentThread();
2842
2843 PAL_ERROR palError = InternalGetThreadDataFromHandle(
2844 pCurrentThread,
2845 hThread,
2846 0,
2847 &pTargetThread,
2848 &pobjThread
2849 );
2850
2851 if (palError == NO_ERROR)
2852 {
2853 palError = InjectActivationInternal(pTargetThread);
2854 }
2855
2856 if (palError == NO_ERROR)
2857 {
2858 pCurrentThread->SetLastError(palError);
2859 }
2860
2861 if (pobjThread != NULL)
2862 {
2863 pobjThread->ReleaseReference(pCurrentThread);
2864 }
2865
2866 BOOL success = (palError == NO_ERROR);
2867 LOGEXIT("PAL_InjectActivation returns:d\n", success);
2868 PERF_EXIT(PAL_InjectActivation);
2869
2870 return success;
2871}
2872
2873#if HAVE_MACH_EXCEPTIONS
2874
2875extern mach_port_t s_ExceptionPort;
2876
2877// Get handler details for a given type of exception. If successful the structure pointed at by pHandler is
2878// filled in and true is returned. Otherwise false is returned.
2879bool CorUnix::CThreadMachExceptionHandlers::GetHandler(exception_type_t eException, CorUnix::MachExceptionHandler *pHandler)
2880{
2881 exception_mask_t bmExceptionMask = (1 << eException);
2882 int idxHandler = GetIndexOfHandler(bmExceptionMask);
2883
2884 // Did we find a handler?
2885 if (idxHandler == -1)
2886 return false;
2887
2888 // Found one, so initialize the output structure with the details.
2889 pHandler->m_mask = m_masks[idxHandler];
2890 pHandler->m_handler = m_handlers[idxHandler];
2891 pHandler->m_behavior = m_behaviors[idxHandler];
2892 pHandler->m_flavor = m_flavors[idxHandler];
2893
2894 return true;
2895}
2896
2897// Look for a handler for the given exception within the given handler node. Return its index if successful or
2898// -1 otherwise.
2899int CorUnix::CThreadMachExceptionHandlers::GetIndexOfHandler(exception_mask_t bmExceptionMask)
2900{
2901 // Check all handler entries for one handling the exception mask.
2902 for (mach_msg_type_number_t i = 0; i < m_nPorts; i++)
2903 {
2904 // Entry covers this exception type and the handler isn't null
2905 if (m_masks[i] & bmExceptionMask && m_handlers[i] != MACH_PORT_NULL)
2906 {
2907 _ASSERTE(m_handlers[i] != s_ExceptionPort);
2908
2909 // One more check; has the target handler port become dead?
2910 mach_port_type_t ePortType;
2911 if (mach_port_type(mach_task_self(), m_handlers[i], &ePortType) == KERN_SUCCESS && !(ePortType & MACH_PORT_TYPE_DEAD_NAME))
2912 {
2913 // Got a matching entry.
2914 return i;
2915 }
2916 }
2917 }
2918
2919 // Didn't find a handler.
2920 return -1;
2921}
2922
2923#endif // HAVE_MACH_EXCEPTIONS
2924