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** Header: COMSynchronizable.cpp
9**
10** Purpose: Native methods on System.SynchronizableObject
11** and its subclasses.
12**
13**
14===========================================================*/
15
16#include "common.h"
17
18#include <object.h>
19#include "threads.h"
20#include "excep.h"
21#include "vars.hpp"
22#include "field.h"
23#include "comsynchronizable.h"
24#include "dbginterface.h"
25#include "comdelegate.h"
26#include "eeconfig.h"
27#include "callhelpers.h"
28#include "appdomain.hpp"
29#include "appdomain.inl"
30
31#ifndef FEATURE_PAL
32#include "utilcode.h"
33#endif
34
35// To include definition of CAPTURE_BUCKETS_AT_TRANSITION
36#include "exstate.h"
37
38// The two threads need to communicate some information. Any object references must
39// be declared to GC.
40struct SharedState
41{
42 OBJECTHANDLE m_Threadable;
43 OBJECTHANDLE m_ThreadStartArg;
44 Thread *m_Internal;
45
46 SharedState(OBJECTREF threadable, OBJECTREF threadStartArg, Thread *internal)
47 {
48 CONTRACTL
49 {
50 GC_NOTRIGGER;
51 THROWS; // From CreateHandle()
52 MODE_COOPERATIVE;
53 }
54 CONTRACTL_END;
55
56 AppDomain *ad = SystemDomain::GetAppDomainFromId(internal->GetKickOffDomainId(), ADV_CURRENTAD);
57
58 m_Threadable = ad->CreateHandle(threadable);
59 m_ThreadStartArg = ad->CreateHandle(threadStartArg);
60
61 m_Internal = internal;
62 }
63
64 ~SharedState()
65 {
66 CONTRACTL
67 {
68 GC_NOTRIGGER;
69 NOTHROW;
70 MODE_COOPERATIVE;
71 }
72 CONTRACTL_END;
73
74 DestroyHandle(m_Threadable);
75 DestroyHandle(m_ThreadStartArg);
76 }
77};
78
79
80// For the following helpers, we make no attempt to synchronize. The app developer
81// is responsible for managing his own race conditions.
82//
83// Note: if the internal Thread is NULL, this implies that the exposed object has
84// finalized and then been resurrected.
85static inline BOOL ThreadNotStarted(Thread *t)
86{
87 WRAPPER_NO_CONTRACT;
88 return (t && t->IsUnstarted() && !t->HasValidThreadHandle());
89}
90
91static inline BOOL ThreadIsRunning(Thread *t)
92{
93 WRAPPER_NO_CONTRACT;
94 return (t &&
95 (t->m_State & (Thread::TS_ReportDead|Thread::TS_Dead)) == 0 &&
96 (t->HasValidThreadHandle()));
97}
98
99static inline BOOL ThreadIsDead(Thread *t)
100{
101 WRAPPER_NO_CONTRACT;
102 return (t == 0 || t->IsDead());
103}
104
105
106// Map our exposed notion of thread priorities into the enumeration that NT uses.
107static INT32 MapToNTPriority(INT32 ours)
108{
109 CONTRACTL
110 {
111 GC_NOTRIGGER;
112 THROWS;
113 MODE_ANY;
114 }
115 CONTRACTL_END;
116
117 INT32 NTPriority = 0;
118
119 switch (ours)
120 {
121 case ThreadNative::PRIORITY_LOWEST:
122 NTPriority = THREAD_PRIORITY_LOWEST;
123 break;
124
125 case ThreadNative::PRIORITY_BELOW_NORMAL:
126 NTPriority = THREAD_PRIORITY_BELOW_NORMAL;
127 break;
128
129 case ThreadNative::PRIORITY_NORMAL:
130 NTPriority = THREAD_PRIORITY_NORMAL;
131 break;
132
133 case ThreadNative::PRIORITY_ABOVE_NORMAL:
134 NTPriority = THREAD_PRIORITY_ABOVE_NORMAL;
135 break;
136
137 case ThreadNative::PRIORITY_HIGHEST:
138 NTPriority = THREAD_PRIORITY_HIGHEST;
139 break;
140
141 default:
142 COMPlusThrow(kArgumentOutOfRangeException, W("Argument_InvalidFlag"));
143 }
144 return NTPriority;
145}
146
147
148// Map to our exposed notion of thread priorities from the enumeration that NT uses.
149INT32 MapFromNTPriority(INT32 NTPriority)
150{
151 LIMITED_METHOD_CONTRACT;
152
153 INT32 ours = 0;
154
155 if (NTPriority <= THREAD_PRIORITY_LOWEST)
156 {
157 // managed code does not support IDLE. Map it to PRIORITY_LOWEST.
158 ours = ThreadNative::PRIORITY_LOWEST;
159 }
160 else if (NTPriority >= THREAD_PRIORITY_HIGHEST)
161 {
162 ours = ThreadNative::PRIORITY_HIGHEST;
163 }
164 else if (NTPriority == THREAD_PRIORITY_BELOW_NORMAL)
165 {
166 ours = ThreadNative::PRIORITY_BELOW_NORMAL;
167 }
168 else if (NTPriority == THREAD_PRIORITY_NORMAL)
169 {
170 ours = ThreadNative::PRIORITY_NORMAL;
171 }
172 else if (NTPriority == THREAD_PRIORITY_ABOVE_NORMAL)
173 {
174 ours = ThreadNative::PRIORITY_ABOVE_NORMAL;
175 }
176 else
177 {
178 _ASSERTE (!"not supported priority");
179 ours = ThreadNative::PRIORITY_NORMAL;
180 }
181 return ours;
182}
183
184
185void ThreadNative::KickOffThread_Worker(LPVOID ptr)
186{
187 CONTRACTL
188 {
189 GC_TRIGGERS;
190 THROWS;
191 MODE_COOPERATIVE;
192 SO_TOLERANT;
193 }
194 CONTRACTL_END;
195
196 KickOffThread_Args *args = (KickOffThread_Args *) ptr;
197 _ASSERTE(ObjectFromHandle(args->share->m_Threadable) != NULL);
198 args->retVal = 0;
199
200 // we are saving the delagate and result primarily for debugging
201 struct _gc
202 {
203 OBJECTREF orThreadStartArg;
204 OBJECTREF orDelegate;
205 OBJECTREF orResult;
206 OBJECTREF orThread;
207 } gc;
208 ZeroMemory(&gc, sizeof(gc));
209
210 Thread *pThread;
211 pThread = GetThread();
212 _ASSERTE(pThread);
213 GCPROTECT_BEGIN(gc);
214 BEGIN_SO_INTOLERANT_CODE(pThread);
215
216 gc.orDelegate = ObjectFromHandle(args->share->m_Threadable);
217 gc.orThreadStartArg = ObjectFromHandle(args->share->m_ThreadStartArg);
218
219 // We cannot call the Delegate Invoke method directly from ECall. The
220 // stub has not been created for non multicast delegates. Instead, we
221 // will invoke the Method on the OR stored in the delegate directly.
222 // If there are changes to the signature of the ThreadStart delegate
223 // this code will need to change. I've noted this in the Thread start
224 // class.
225
226 delete args->share;
227 args->share = 0;
228
229 MethodDesc *pMeth = ((DelegateEEClass*)( gc.orDelegate->GetMethodTable()->GetClass() ))->GetInvokeMethod();
230 _ASSERTE(pMeth);
231 MethodDescCallSite invokeMethod(pMeth, &gc.orDelegate);
232
233 if (MscorlibBinder::IsClass(gc.orDelegate->GetMethodTable(), CLASS__PARAMETERIZEDTHREADSTART))
234 {
235 //Parameterized ThreadStart
236 ARG_SLOT arg[2];
237
238 arg[0] = ObjToArgSlot(gc.orDelegate);
239 arg[1]=ObjToArgSlot(gc.orThreadStartArg);
240 invokeMethod.Call(arg);
241 }
242 else
243 {
244 //Simple ThreadStart
245 ARG_SLOT arg[1];
246
247 arg[0] = ObjToArgSlot(gc.orDelegate);
248 invokeMethod.Call(arg);
249 }
250 STRESS_LOG2(LF_SYNC, LL_INFO10, "Managed thread exiting normally for delegate %p Type %pT\n", OBJECTREFToObject(gc.orDelegate), (size_t) gc.orDelegate->GetMethodTable());
251
252 END_SO_INTOLERANT_CODE;
253 GCPROTECT_END();
254}
255
256// Helper to avoid two EX_TRY/EX_CATCH blocks in one function
257static void PulseAllHelper(Thread* pThread)
258{
259 CONTRACTL
260 {
261 GC_TRIGGERS;
262 DISABLED(NOTHROW);
263 MODE_COOPERATIVE;
264 }
265 CONTRACTL_END;
266
267 EX_TRY
268 {
269 // GetExposedObject() will either throw, or we have a valid object. Note
270 // that we re-acquire it each time, since it may move during calls.
271 pThread->GetExposedObject()->EnterObjMonitor();
272 pThread->GetExposedObject()->PulseAll();
273 pThread->GetExposedObject()->LeaveObjMonitor();
274 }
275 EX_CATCH
276 {
277 // just keep going...
278 }
279 EX_END_CATCH(SwallowAllExceptions)
280}
281
282// When an exposed thread is started by Win32, this is where it starts.
283ULONG WINAPI ThreadNative::KickOffThread(void* pass)
284{
285
286 CONTRACTL
287 {
288 GC_TRIGGERS;
289 THROWS;
290 MODE_ANY;
291 SO_TOLERANT;
292 }
293 CONTRACTL_END;
294
295 ULONG retVal = 0;
296 // Before we do anything else, get Setup so that we have a real thread.
297
298 // Our thread isn't setup yet, so we can't use the standard probe
299 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return E_FAIL);
300
301 KickOffThread_Args args;
302 // don't have a separate var becuase this can be updated in the worker
303 args.share = (SharedState *) pass;
304 args.pThread = args.share->m_Internal;
305
306 Thread* pThread = args.pThread;
307
308 _ASSERTE(pThread != NULL);
309
310 BOOL ok = TRUE;
311
312 {
313 EX_TRY
314 {
315 CExecutionEngine::CheckThreadState(0);
316 }
317 EX_CATCH
318 {
319 // OOM might be thrown from CheckThreadState, so it's important
320 // that we don't rethrow it; if we do then the process will die
321 // because there are no installed handlers at this point, so
322 // swallow the exception. this will set the thread's state to
323 // FailStarted which will result in a ThreadStartException being
324 // thrown from the thread that attempted to start this one.
325 if (!GET_EXCEPTION()->IsTransient() && !SwallowUnhandledExceptions())
326 EX_RETHROW;
327 }
328 EX_END_CATCH(SwallowAllExceptions);
329 if (CExecutionEngine::CheckThreadStateNoCreate(0) == NULL)
330 {
331 // We can not
332 pThread->SetThreadState(Thread::TS_FailStarted);
333 pThread->DetachThread(FALSE);
334 // !!! Do not touch any field of Thread object. The Thread object is subject to delete
335 // !!! after DetachThread call.
336 ok = FALSE;
337 }
338 }
339
340 if (ok)
341 {
342 ok = pThread->HasStarted();
343 }
344
345 if (ok)
346 {
347 // Do not swallow the unhandled exception here
348 //
349
350 // Fire ETW event to correlate with the thread that created current thread
351 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadRunning))
352 FireEtwThreadRunning(pThread, GetClrInstanceId());
353
354 // We have a sticky problem here.
355 //
356 // Under some circumstances, the context of 'this' doesn't match the context
357 // of the thread. Today this can only happen if the thread is marked for an
358 // STA. If so, the delegate that is stored in the object may not be directly
359 // suitable for invocation. Instead, we need to call through a proxy so that
360 // the correct context transitions occur.
361 //
362 // All the changes occur inside HasStarted(), which will switch this thread
363 // over to a brand new STA as necessary. We have to notice this happening, so
364 // we can adjust the delegate we are going to invoke on.
365
366 _ASSERTE(GetThread() == pThread); // Now that it's started
367 ManagedThreadBase::KickOff(pThread->GetKickOffDomainId(), KickOffThread_Worker, &args);
368
369 // If TS_FailStarted is set then the args are deleted in ThreadNative::StartInner
370 if ((args.share) && !pThread->HasThreadState(Thread::TS_FailStarted))
371 {
372 delete args.share;
373 }
374
375 PulseAllHelper(pThread);
376
377 GCX_PREEMP_NO_DTOR();
378
379 pThread->ClearThreadCPUGroupAffinity();
380
381 DestroyThread(pThread);
382 }
383
384 END_SO_INTOLERANT_CODE;
385
386 return retVal;
387}
388
389
390FCIMPL1(void, ThreadNative::Start, ThreadBaseObject* pThisUNSAFE)
391{
392 FCALL_CONTRACT;
393
394 HELPER_METHOD_FRAME_BEGIN_NOPOLL();
395
396 StartInner(pThisUNSAFE);
397
398 HELPER_METHOD_FRAME_END_POLL();
399}
400FCIMPLEND
401
402// Start up a thread, which by now should be in the ThreadStore's Unstarted list.
403void ThreadNative::StartInner(ThreadBaseObject* pThisUNSAFE)
404{
405 CONTRACTL
406 {
407 GC_TRIGGERS;
408 THROWS;
409 MODE_COOPERATIVE;
410 }
411 CONTRACTL_END;
412
413 struct _gc
414 {
415 THREADBASEREF pThis;
416 } gc;
417
418 gc.pThis = (THREADBASEREF) pThisUNSAFE;
419
420 GCPROTECT_BEGIN(gc);
421
422 if (gc.pThis == NULL)
423 COMPlusThrow(kNullReferenceException, W("NullReference_This"));
424
425 Thread *pNewThread = gc.pThis->GetInternal();
426 if (pNewThread == NULL)
427 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
428
429 _ASSERTE(GetThread() != NULL); // Current thread wandered in!
430
431 gc.pThis->EnterObjMonitor();
432
433 EX_TRY
434 {
435 // Is the thread already started? You can't restart a thread.
436 if (!ThreadNotStarted(pNewThread))
437 {
438 COMPlusThrow(kThreadStateException, IDS_EE_THREADSTART_STATE);
439 }
440
441 OBJECTREF threadable = gc.pThis->GetDelegate();
442 OBJECTREF threadStartArg = gc.pThis->GetThreadStartArg();
443 gc.pThis->SetDelegate(NULL);
444 gc.pThis->SetThreadStartArg(NULL);
445
446 // This can never happen, because we construct it with a valid one and then
447 // we never let you change it (because SetStart is private).
448 _ASSERTE(threadable != NULL);
449
450 // Allocate this away from our stack, so we can unwind without affecting
451 // KickOffThread. It is inside a GCFrame, so we can enable GC now.
452 NewHolder<SharedState> share(new SharedState(threadable, threadStartArg, pNewThread));
453
454 pNewThread->IncExternalCount();
455
456 // Fire an ETW event to mark the current thread as the launcher of the new thread
457 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadCreating))
458 FireEtwThreadCreating(pNewThread, GetClrInstanceId());
459
460 // copy out the managed name into a buffer that will not move if a GC happens
461 const WCHAR* nativeThreadName = NULL;
462 InlineSString<64> threadNameBuffer;
463 STRINGREF managedThreadName = gc.pThis->GetName();
464 if (managedThreadName != NULL)
465 {
466 managedThreadName->GetSString(threadNameBuffer);
467 nativeThreadName = threadNameBuffer.GetUnicode();
468 }
469
470 // As soon as we create the new thread, it is eligible for suspension, etc.
471 // So it gets transitioned to cooperative mode before this call returns to
472 // us. It is our duty to start it running immediately, so that GC isn't blocked.
473
474 BOOL success = pNewThread->CreateNewThread(
475 pNewThread->RequestedThreadStackSize() /* 0 stackSize override*/,
476 KickOffThread, share, nativeThreadName);
477
478 if (!success)
479 {
480 pNewThread->DecExternalCount(FALSE);
481 COMPlusThrowOM();
482 }
483
484 // After we have established the thread handle, we can check m_Priority.
485 // This ordering is required to eliminate the race condition on setting the
486 // priority of a thread just as it starts up.
487 pNewThread->SetThreadPriority(MapToNTPriority(gc.pThis->m_Priority));
488 pNewThread->ChooseThreadCPUGroupAffinity();
489
490 FastInterlockOr((ULONG *) &pNewThread->m_State, Thread::TS_LegalToJoin);
491
492 DWORD ret;
493 ret = pNewThread->StartThread();
494
495 // When running under a user mode native debugger there is a race
496 // between the moment we've created the thread (in CreateNewThread) and
497 // the moment we resume it (in StartThread); the debugger may receive
498 // the "ct" (create thread) notification, and it will attempt to
499 // suspend/resume all threads in the process. Now imagine the debugger
500 // resumes this thread first, and only later does it try to resume the
501 // newly created thread. In these conditions our call to ResumeThread
502 // may come before the debugger's call to ResumeThread actually causing
503 // ret to equal 2.
504 // We cannot use IsDebuggerPresent() in the condition below because the
505 // debugger may have been detached between the time it got the notification
506 // and the moment we execute the test below.
507 _ASSERTE(ret == 1 || ret == 2);
508
509 {
510 GCX_PREEMP();
511
512 // Synchronize with HasStarted.
513 YIELD_WHILE (!pNewThread->HasThreadState(Thread::TS_FailStarted) &&
514 pNewThread->HasThreadState(Thread::TS_Unstarted));
515 }
516
517 if (!pNewThread->HasThreadState(Thread::TS_FailStarted))
518 {
519 share.SuppressRelease(); // we have handed off ownership of the shared struct
520 }
521 else
522 {
523 share.Release();
524 PulseAllHelper(pNewThread);
525 pNewThread->HandleThreadStartupFailure();
526 }
527 }
528 EX_CATCH
529 {
530 gc.pThis->LeaveObjMonitor();
531 EX_RETHROW;
532 }
533 EX_END_CATCH_UNREACHABLE;
534
535 gc.pThis->LeaveObjMonitor();
536
537 GCPROTECT_END();
538}
539
540FCIMPL1(void, ThreadNative::Abort, ThreadBaseObject* pThis)
541{
542 FCALL_CONTRACT;
543
544 if (pThis == NULL)
545 FCThrowVoid(kNullReferenceException);
546
547 THREADBASEREF thisRef(pThis);
548 // We need to keep the managed Thread object alive so that we can call UserAbort on
549 // unmanaged thread object.
550 HELPER_METHOD_FRAME_BEGIN_1(thisRef);
551
552 Thread *thread = thisRef->GetInternal();
553 if (thread == NULL)
554 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
555#ifdef _DEBUG
556 DWORD testAbort = g_pConfig->GetHostTestThreadAbort();
557 if (testAbort != 0) {
558 thread->UserAbort(Thread::TAR_Thread, testAbort == 1 ? EEPolicy::TA_Safe : EEPolicy::TA_Rude, INFINITE, Thread::UAC_Normal);
559 }
560 else
561#endif
562 thread->UserAbort(Thread::TAR_Thread, EEPolicy::TA_V1Compatible, INFINITE, Thread::UAC_Normal);
563
564 if (thread->CatchAtSafePoint())
565 CommonTripThread();
566 HELPER_METHOD_FRAME_END_POLL();
567}
568FCIMPLEND
569
570FCIMPL1(void, ThreadNative::ResetAbort, ThreadBaseObject* pThis)
571{
572 FCALL_CONTRACT;
573
574 _ASSERTE(pThis);
575 VALIDATEOBJECT(pThis);
576
577 Thread *thread = pThis->GetInternal();
578 // We do not allow user to reset rude thread abort in MustRun code.
579 if (thread && thread->IsRudeAbort())
580 {
581 return;
582 }
583
584 HELPER_METHOD_FRAME_BEGIN_NOPOLL();
585
586 if (thread == NULL)
587 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
588 thread->UserResetAbort(Thread::TAR_Thread);
589 thread->ClearAborted();
590 HELPER_METHOD_FRAME_END_POLL();
591}
592FCIMPLEND
593
594
595// Note that you can manipulate the priority of a thread that hasn't started yet,
596// or one that is running. But you get an exception if you manipulate the priority
597// of a thread that has died.
598FCIMPL1(INT32, ThreadNative::GetPriority, ThreadBaseObject* pThisUNSAFE)
599{
600 FCALL_CONTRACT;
601
602 if (pThisUNSAFE==NULL)
603 FCThrowRes(kNullReferenceException, W("NullReference_This"));
604
605 // validate the handle
606 if (ThreadIsDead(pThisUNSAFE->GetInternal()))
607 FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_PRIORITY, NULL, NULL, NULL);
608
609 return pThisUNSAFE->m_Priority;
610}
611FCIMPLEND
612
613FCIMPL2(void, ThreadNative::SetPriority, ThreadBaseObject* pThisUNSAFE, INT32 iPriority)
614{
615 FCALL_CONTRACT;
616
617 int priority;
618 Thread *thread;
619
620 THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
621 HELPER_METHOD_FRAME_BEGIN_1(pThis);
622
623 if (pThis==NULL)
624 {
625 COMPlusThrow(kNullReferenceException, W("NullReference_This"));
626 }
627
628 // translate the priority (validating as well)
629 priority = MapToNTPriority(iPriority); // can throw; needs a frame
630
631 // validate the thread
632 thread = pThis->GetInternal();
633
634 if (ThreadIsDead(thread))
635 {
636 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_DEAD_PRIORITY, NULL, NULL, NULL);
637 }
638
639 INT32 oldPriority = pThis->m_Priority;
640
641 // Eliminate the race condition by establishing m_Priority before we check for if
642 // the thread is running. See ThreadNative::Start() for the other half.
643 pThis->m_Priority = iPriority;
644
645 if (!thread->SetThreadPriority(priority))
646 {
647 pThis->m_Priority = oldPriority;
648 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_PRIORITY_FAIL, NULL, NULL, NULL);
649 }
650
651 HELPER_METHOD_FRAME_END();
652}
653FCIMPLEND
654
655// This service can be called on unstarted and dead threads. For unstarted ones, the
656// next wait will be interrupted. For dead ones, this service quietly does nothing.
657FCIMPL1(void, ThreadNative::Interrupt, ThreadBaseObject* pThisUNSAFE)
658{
659 FCALL_CONTRACT;
660
661 if (pThisUNSAFE==NULL)
662 FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
663
664 Thread *thread = pThisUNSAFE->GetInternal();
665
666 if (thread == 0)
667 FCThrowExVoid(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
668
669 HELPER_METHOD_FRAME_BEGIN_0();
670
671 thread->UserInterrupt(Thread::TI_Interrupt);
672
673 HELPER_METHOD_FRAME_END();
674}
675FCIMPLEND
676
677FCIMPL1(FC_BOOL_RET, ThreadNative::IsAlive, ThreadBaseObject* pThisUNSAFE)
678{
679 FCALL_CONTRACT;
680
681 if (pThisUNSAFE==NULL)
682 FCThrowRes(kNullReferenceException, W("NullReference_This"));
683
684 THREADBASEREF thisRef(pThisUNSAFE);
685 BOOL ret = false;
686
687 // Keep managed Thread object alive, since the native object's
688 // lifetime is tied to the managed object's finalizer. And with
689 // resurrection, it may be possible to get a dangling pointer here -
690 // consider both protecting thisRef and setting the managed object's
691 // Thread* to NULL in the GC's ScanForFinalization method.
692 HELPER_METHOD_FRAME_BEGIN_RET_1(thisRef);
693
694 Thread *thread = thisRef->GetInternal();
695
696 if (thread == 0)
697 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
698
699 ret = ThreadIsRunning(thread);
700
701 HELPER_METHOD_POLL();
702 HELPER_METHOD_FRAME_END();
703
704 FC_RETURN_BOOL(ret);
705}
706FCIMPLEND
707
708FCIMPL2(FC_BOOL_RET, ThreadNative::Join, ThreadBaseObject* pThisUNSAFE, INT32 Timeout)
709{
710 FCALL_CONTRACT;
711
712 BOOL retVal = FALSE;
713 THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
714
715 HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
716
717 if (pThis==NULL)
718 COMPlusThrow(kNullReferenceException, W("NullReference_This"));
719
720 // validate the timeout
721 if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT))
722 COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegOrNegative1"));
723
724 retVal = DoJoin(pThis, Timeout);
725
726 HELPER_METHOD_FRAME_END();
727
728 FC_RETURN_BOOL(retVal);
729}
730FCIMPLEND
731
732#undef Sleep
733FCIMPL1(void, ThreadNative::Sleep, INT32 iTime)
734{
735 FCALL_CONTRACT;
736
737 HELPER_METHOD_FRAME_BEGIN_0();
738
739 // validate the sleep time
740 if ((iTime < 0) && (iTime != INFINITE_TIMEOUT))
741 COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegOrNegative1"));
742
743 while(true)
744 {
745 INT64 sPauseTime = g_PauseTime;
746 INT64 sTime = CLRGetTickCount64();
747 GetThread()->UserSleep(iTime);
748 iTime = (INT32)AdditionalWait(sPauseTime, sTime, iTime);
749 if(iTime == 0)
750 break;
751 }
752
753 HELPER_METHOD_FRAME_END();
754}
755FCIMPLEND
756
757#define Sleep(dwMilliseconds) Dont_Use_Sleep(dwMilliseconds)
758
759FCIMPL1(INT32, ThreadNative::GetManagedThreadId, ThreadBaseObject* th) {
760 FCALL_CONTRACT;
761
762 FC_GC_POLL_NOT_NEEDED();
763 if (th == NULL)
764 FCThrow(kNullReferenceException);
765
766 return th->GetManagedThreadId();
767}
768FCIMPLEND
769
770NOINLINE static Object* GetCurrentThreadHelper()
771{
772 FCALL_CONTRACT;
773 FC_INNER_PROLOG(ThreadNative::GetCurrentThread);
774 OBJECTREF refRetVal = NULL;
775
776 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refRetVal);
777 refRetVal = GetThread()->GetExposedObject();
778 HELPER_METHOD_FRAME_END();
779
780 FC_INNER_EPILOG();
781 return OBJECTREFToObject(refRetVal);
782}
783
784FCIMPL0(Object*, ThreadNative::GetCurrentThread)
785{
786 FCALL_CONTRACT;
787 OBJECTHANDLE ExposedObject = GetThread()->m_ExposedObject;
788 _ASSERTE(ExposedObject != 0); //Thread's constructor always initializes its GCHandle
789 Object* result = *((Object**) ExposedObject);
790 if (result != 0)
791 return result;
792
793 FC_INNER_RETURN(Object*, GetCurrentThreadHelper());
794}
795FCIMPLEND
796
797UINT64 QCALLTYPE ThreadNative::GetCurrentOSThreadId()
798{
799 QCALL_CONTRACT;
800
801 // The Windows API GetCurrentThreadId returns a 32-bit integer thread ID.
802 // On some non-Windows platforms (e.g. OSX), the thread ID is a 64-bit value.
803 // We special case the API for non-Windows to get the 64-bit value and zero-extend
804 // the Windows value to return a single data type on all platforms.
805
806 UINT64 threadId;
807
808 BEGIN_QCALL;
809#ifndef FEATURE_PAL
810 threadId = (UINT64) GetCurrentThreadId();
811#else
812 threadId = (UINT64) PAL_GetCurrentOSThreadId();
813#endif
814 END_QCALL;
815
816 return threadId;
817}
818
819FCIMPL3(void, ThreadNative::SetStart, ThreadBaseObject* pThisUNSAFE, Object* pDelegateUNSAFE, INT32 iRequestedStackSize)
820{
821 FCALL_CONTRACT;
822
823 if (pThisUNSAFE==NULL)
824 FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
825
826 THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
827 OBJECTREF pDelegate = (OBJECTREF ) pDelegateUNSAFE;
828
829 HELPER_METHOD_FRAME_BEGIN_2(pThis, pDelegate);
830
831 _ASSERTE(pThis != NULL);
832 _ASSERTE(pDelegate != NULL); // Thread's constructor validates this
833
834 if (pThis->m_InternalThread == NULL)
835 {
836 // if we don't have an internal Thread object associated with this exposed object,
837 // now is our first opportunity to create one.
838 Thread *unstarted = SetupUnstartedThread();
839
840 PREFIX_ASSUME(unstarted != NULL);
841
842 if (GetThread()->GetDomain()->IgnoreUnhandledExceptions())
843 {
844 unstarted->SetThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions);
845 }
846
847 pThis->SetInternal(unstarted);
848 pThis->SetManagedThreadId(unstarted->GetThreadId());
849 unstarted->SetExposedObject(pThis);
850 unstarted->RequestedThreadStackSize(iRequestedStackSize);
851 }
852
853 // save off the delegate
854 pThis->SetDelegate(pDelegate);
855
856 HELPER_METHOD_FRAME_END();
857}
858FCIMPLEND
859
860
861// Set whether or not this is a background thread.
862FCIMPL2(void, ThreadNative::SetBackground, ThreadBaseObject* pThisUNSAFE, CLR_BOOL isBackground)
863{
864 FCALL_CONTRACT;
865
866 if (pThisUNSAFE==NULL)
867 FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
868
869 // validate the thread
870 Thread *thread = pThisUNSAFE->GetInternal();
871
872 if (ThreadIsDead(thread))
873 FCThrowExVoid(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
874
875 HELPER_METHOD_FRAME_BEGIN_0();
876
877 thread->SetBackground(isBackground);
878
879 HELPER_METHOD_FRAME_END();
880}
881FCIMPLEND
882
883// Return whether or not this is a background thread.
884FCIMPL1(FC_BOOL_RET, ThreadNative::IsBackground, ThreadBaseObject* pThisUNSAFE)
885{
886 FCALL_CONTRACT;
887
888 if (pThisUNSAFE==NULL)
889 FCThrowRes(kNullReferenceException, W("NullReference_This"));
890
891 // validate the thread
892 Thread *thread = pThisUNSAFE->GetInternal();
893
894 if (ThreadIsDead(thread))
895 FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
896
897 FC_RETURN_BOOL(thread->IsBackground());
898}
899FCIMPLEND
900
901
902// Deliver the state of the thread as a consistent set of bits.
903// This copied in VM\EEDbgInterfaceImpl.h's
904// CorDebugUserState GetUserState( Thread *pThread )
905// , so propogate changes to both functions
906FCIMPL1(INT32, ThreadNative::GetThreadState, ThreadBaseObject* pThisUNSAFE)
907{
908 FCALL_CONTRACT;
909
910 INT32 res = 0;
911 Thread::ThreadState state;
912
913 if (pThisUNSAFE==NULL)
914 FCThrowRes(kNullReferenceException, W("NullReference_This"));
915
916 // validate the thread. Failure here implies that the thread was finalized
917 // and then resurrected.
918 Thread *thread = pThisUNSAFE->GetInternal();
919
920 if (!thread)
921 FCThrowEx(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
922
923 HELPER_METHOD_FRAME_BEGIN_RET_0();
924
925 // grab a snapshot
926 state = thread->GetSnapshotState();
927
928 if (state & Thread::TS_Background)
929 res |= ThreadBackground;
930
931 if (state & Thread::TS_Unstarted)
932 res |= ThreadUnstarted;
933
934 // Don't report a StopRequested if the thread has actually stopped.
935 if (state & Thread::TS_Dead)
936 {
937 if (state & Thread::TS_Aborted)
938 res |= ThreadAborted;
939 else
940 res |= ThreadStopped;
941 }
942 else
943 {
944 if (state & Thread::TS_AbortRequested)
945 res |= ThreadAbortRequested;
946 }
947
948 if (state & Thread::TS_Interruptible)
949 res |= ThreadWaitSleepJoin;
950
951 // CoreCLR does not support user-requested thread suspension
952 _ASSERTE(!(state & Thread::TS_UserSuspendPending));
953
954 HELPER_METHOD_POLL();
955 HELPER_METHOD_FRAME_END();
956
957 return res;
958}
959FCIMPLEND
960
961#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
962
963// Indicate whether the thread will host an STA (this may fail if the thread has
964// already been made part of the MTA, use GetApartmentState or the return state
965// from this routine to check for this).
966FCIMPL3(INT32, ThreadNative::SetApartmentState, ThreadBaseObject* pThisUNSAFE, INT32 iState, CLR_BOOL fireMDAOnMismatch)
967{
968 FCALL_CONTRACT;
969
970 if (pThisUNSAFE==NULL)
971 FCThrowRes(kNullReferenceException, W("NullReference_This"));
972
973 INT32 retVal = ApartmentUnknown;
974 BOOL ok = TRUE;
975 THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
976
977 HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
978
979 // Translate state input. ApartmentUnknown is not an acceptable input state.
980 // Throw an exception here rather than pass it through to the internal
981 // routine, which asserts.
982 Thread::ApartmentState state = Thread::AS_Unknown;
983 if (iState == ApartmentSTA)
984 state = Thread::AS_InSTA;
985 else if (iState == ApartmentMTA)
986 state = Thread::AS_InMTA;
987 else if (iState == ApartmentUnknown)
988 state = Thread::AS_Unknown;
989 else
990 COMPlusThrow(kArgumentOutOfRangeException, W("ArgumentOutOfRange_Enum"));
991
992 Thread *thread = pThis->GetInternal();
993 if (!thread)
994 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
995
996 {
997 pThis->EnterObjMonitor();
998
999 // We can only change the apartment if the thread is unstarted or
1000 // running, and if it's running we have to be in the thread's
1001 // context.
1002 if ((!ThreadNotStarted(thread) && !ThreadIsRunning(thread)) ||
1003 (!ThreadNotStarted(thread) && (GetThread() != thread)))
1004 ok = FALSE;
1005 else
1006 {
1007 EX_TRY
1008 {
1009 state = thread->SetApartment(state, fireMDAOnMismatch == TRUE);
1010 }
1011 EX_CATCH
1012 {
1013 pThis->LeaveObjMonitor();
1014 EX_RETHROW;
1015 }
1016 EX_END_CATCH_UNREACHABLE;
1017 }
1018
1019 pThis->LeaveObjMonitor();
1020 }
1021
1022
1023 // Now it's safe to throw exceptions again.
1024 if (!ok)
1025 COMPlusThrow(kThreadStateException);
1026
1027 // Translate state back into external form
1028 if (state == Thread::AS_InSTA)
1029 retVal = ApartmentSTA;
1030 else if (state == Thread::AS_InMTA)
1031 retVal = ApartmentMTA;
1032 else if (state == Thread::AS_Unknown)
1033 retVal = ApartmentUnknown;
1034 else
1035 _ASSERTE(!"Invalid state returned from SetApartment");
1036
1037 HELPER_METHOD_FRAME_END();
1038
1039 return retVal;
1040}
1041FCIMPLEND
1042
1043// Return whether the thread hosts an STA, is a member of the MTA or is not
1044// currently initialized for COM.
1045FCIMPL1(INT32, ThreadNative::GetApartmentState, ThreadBaseObject* pThisUNSAFE)
1046{
1047 FCALL_CONTRACT;
1048
1049 INT32 retVal = 0;
1050
1051 THREADBASEREF refThis = (THREADBASEREF) ObjectToOBJECTREF(pThisUNSAFE);
1052
1053 HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
1054
1055 if (refThis == NULL)
1056 {
1057 COMPlusThrow(kNullReferenceException, W("NullReference_This"));
1058 }
1059
1060 Thread* thread = refThis->GetInternal();
1061
1062 if (ThreadIsDead(thread))
1063 {
1064 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_DEAD_STATE);
1065 }
1066
1067 Thread::ApartmentState state = thread->GetApartment();
1068
1069#ifdef FEATURE_COMINTEROP
1070 if (state == Thread::AS_Unknown)
1071 {
1072 // If the CLR hasn't started COM yet, start it up and attempt the call again.
1073 // We do this in order to minimize the number of situations under which we return
1074 // ApartmentState.Unknown to our callers.
1075 if (!g_fComStarted)
1076 {
1077 EnsureComStarted();
1078 state = thread->GetApartment();
1079 }
1080 }
1081#endif // FEATURE_COMINTEROP
1082
1083 // Translate state into external form
1084 retVal = ApartmentUnknown;
1085 if (state == Thread::AS_InSTA)
1086 {
1087 retVal = ApartmentSTA;
1088 }
1089 else if (state == Thread::AS_InMTA)
1090 {
1091 retVal = ApartmentMTA;
1092 }
1093 else if (state == Thread::AS_Unknown)
1094 {
1095 retVal = ApartmentUnknown;
1096 }
1097 else
1098 {
1099 _ASSERTE(!"Invalid state returned from GetApartment");
1100 }
1101
1102 HELPER_METHOD_FRAME_END();
1103
1104 return retVal;
1105}
1106FCIMPLEND
1107
1108
1109// Attempt to eagerly set the apartment state during thread startup.
1110FCIMPL1(void, ThreadNative::StartupSetApartmentState, ThreadBaseObject* pThisUNSAFE)
1111{
1112 FCALL_CONTRACT;
1113
1114 THREADBASEREF refThis = (THREADBASEREF) ObjectToOBJECTREF(pThisUNSAFE);
1115
1116 HELPER_METHOD_FRAME_BEGIN_1(refThis);
1117
1118 if (refThis == NULL)
1119 {
1120 COMPlusThrow(kNullReferenceException, W("NullReference_This"));
1121 }
1122
1123 Thread* thread = refThis->GetInternal();
1124
1125 if (!ThreadNotStarted(thread))
1126 COMPlusThrow(kThreadStateException, IDS_EE_THREADSTART_STATE);
1127
1128 // Assert that the thread hasn't been started yet.
1129 _ASSERTE(Thread::TS_Unstarted & thread->GetSnapshotState());
1130
1131 Thread::ApartmentState as = thread->GetExplicitApartment();
1132 if (as == Thread::AS_Unknown)
1133 {
1134 thread->SetApartment(Thread::AS_InMTA, TRUE);
1135 }
1136
1137 HELPER_METHOD_FRAME_END();
1138}
1139FCIMPLEND
1140
1141#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
1142
1143void ReleaseThreadExternalCount(Thread * pThread)
1144{
1145 WRAPPER_NO_CONTRACT;
1146 pThread->DecExternalCount(FALSE);
1147}
1148
1149typedef Holder<Thread *, DoNothing, ReleaseThreadExternalCount> ThreadExternalCountHolder;
1150
1151// Wait for the thread to die
1152BOOL ThreadNative::DoJoin(THREADBASEREF DyingThread, INT32 timeout)
1153{
1154 CONTRACTL
1155 {
1156 THROWS;
1157 GC_TRIGGERS;
1158 MODE_COOPERATIVE;
1159 PRECONDITION(DyingThread != NULL);
1160 PRECONDITION((timeout >= 0) || (timeout == INFINITE_TIMEOUT));
1161 }
1162 CONTRACTL_END;
1163
1164 Thread * DyingInternal = DyingThread->GetInternal();
1165
1166 // Validate the handle. It's valid to Join a thread that's not running -- so
1167 // long as it was once started.
1168 if (DyingInternal == 0 ||
1169 !(DyingInternal->m_State & Thread::TS_LegalToJoin))
1170 {
1171 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_NOTSTARTED);
1172 }
1173
1174 // Don't grab the handle until we know it has started, to eliminate the race
1175 // condition.
1176 if (ThreadIsDead(DyingInternal) || !DyingInternal->HasValidThreadHandle())
1177 return TRUE;
1178
1179 DWORD dwTimeOut32 = (timeout == INFINITE_TIMEOUT
1180 ? INFINITE
1181 : (DWORD) timeout);
1182
1183 // There is a race here. DyingThread is going to close its thread handle.
1184 // If we grab the handle and then DyingThread closes it, we will wait forever
1185 // in DoAppropriateWait.
1186 int RefCount = DyingInternal->IncExternalCount();
1187 if (RefCount == 1)
1188 {
1189 // !!! We resurrect the Thread Object.
1190 // !!! We will keep the Thread ref count to be 1 so that we will not try
1191 // !!! to destroy the Thread Object again.
1192 // !!! Do not call DecExternalCount here!
1193 _ASSERTE (!DyingInternal->HasValidThreadHandle());
1194 return TRUE;
1195 }
1196
1197 ThreadExternalCountHolder dyingInternalHolder(DyingInternal);
1198
1199 if (!DyingInternal->HasValidThreadHandle())
1200 {
1201 return TRUE;
1202 }
1203
1204 GCX_PREEMP();
1205 DWORD rv = DyingInternal->JoinEx(dwTimeOut32, (WaitMode)(WaitMode_Alertable/*alertable*/|WaitMode_InDeadlock));
1206
1207 switch(rv)
1208 {
1209 case WAIT_OBJECT_0:
1210 return TRUE;
1211
1212 case WAIT_TIMEOUT:
1213 break;
1214
1215 case WAIT_FAILED:
1216 if(!DyingInternal->HasValidThreadHandle())
1217 return TRUE;
1218 break;
1219
1220 default:
1221 _ASSERTE(!"This return code is not understood \n");
1222 break;
1223 }
1224
1225 return FALSE;
1226}
1227
1228
1229// We don't get a constructor for ThreadBaseObject, so we rely on the fact that this
1230// method is only called once, out of SetStart. Since SetStart is private/native
1231// and only called from the constructor, we'll only get called here once to set it
1232// up and once (with NULL) to tear it down. The 'null' can only come from Finalize
1233// because the constructor throws if it doesn't get a valid delegate.
1234void ThreadBaseObject::SetDelegate(OBJECTREF delegate)
1235{
1236 CONTRACTL
1237 {
1238 GC_TRIGGERS;
1239 THROWS;
1240 MODE_COOPERATIVE;
1241 }
1242 CONTRACTL_END;
1243
1244#ifdef APPDOMAIN_STATE
1245 if (delegate != NULL)
1246 {
1247 AppDomain *pDomain = delegate->GetAppDomain();
1248 Thread *pThread = GetInternal();
1249 AppDomain *kickoffDomain = pThread->GetKickOffDomain();
1250 _ASSERTE_ALL_BUILDS("clr/src/VM/COMSynchronizable.cpp", !pDomain || pDomain == kickoffDomain);
1251 _ASSERTE_ALL_BUILDS("clr/src/VM/COMSynchronizable.cpp", kickoffDomain == GetThread()->GetDomain());
1252 }
1253#endif
1254
1255 SetObjectReferenceUnchecked( (OBJECTREF *)&m_Delegate, delegate );
1256
1257 // If the delegate is being set then initialize the other data members.
1258 if (m_Delegate != NULL)
1259 {
1260 // Initialize the thread priority to normal.
1261 m_Priority = ThreadNative::PRIORITY_NORMAL;
1262 }
1263}
1264
1265
1266// If the exposed object is created after-the-fact, for an existing thread, we call
1267// InitExisting on it. This is the other "construction", as opposed to SetDelegate.
1268void ThreadBaseObject::InitExisting()
1269{
1270 CONTRACTL
1271 {
1272 GC_NOTRIGGER;
1273 NOTHROW;
1274 MODE_COOPERATIVE;
1275 }
1276 CONTRACTL_END;
1277
1278 Thread *pThread = GetInternal();
1279 _ASSERTE (pThread);
1280 switch (pThread->GetThreadPriority())
1281 {
1282 case THREAD_PRIORITY_LOWEST:
1283 case THREAD_PRIORITY_IDLE:
1284 m_Priority = ThreadNative::PRIORITY_LOWEST;
1285 break;
1286
1287 case THREAD_PRIORITY_BELOW_NORMAL:
1288 m_Priority = ThreadNative::PRIORITY_BELOW_NORMAL;
1289 break;
1290
1291 case THREAD_PRIORITY_NORMAL:
1292 m_Priority = ThreadNative::PRIORITY_NORMAL;
1293 break;
1294
1295 case THREAD_PRIORITY_ABOVE_NORMAL:
1296 m_Priority = ThreadNative::PRIORITY_ABOVE_NORMAL;
1297 break;
1298
1299 case THREAD_PRIORITY_HIGHEST:
1300 case THREAD_PRIORITY_TIME_CRITICAL:
1301 m_Priority = ThreadNative::PRIORITY_HIGHEST;
1302 break;
1303
1304 case THREAD_PRIORITY_ERROR_RETURN:
1305 _ASSERTE(FALSE);
1306 m_Priority = ThreadNative::PRIORITY_NORMAL;
1307 break;
1308
1309 default:
1310 m_Priority = ThreadNative::PRIORITY_NORMAL;
1311 break;
1312 }
1313
1314}
1315
1316FCIMPL1(void, ThreadNative::Finalize, ThreadBaseObject* pThisUNSAFE)
1317{
1318 FCALL_CONTRACT;
1319
1320 // This function is intentionally blank.
1321 // See comment in code:MethodTable::CallFinalizer.
1322
1323 _ASSERTE (!"Should not be called");
1324
1325 FCUnique(0x21);
1326}
1327FCIMPLEND
1328
1329#ifdef FEATURE_COMINTEROP
1330FCIMPL1(void, ThreadNative::DisableComObjectEagerCleanup, ThreadBaseObject* pThisUNSAFE)
1331{
1332 FCALL_CONTRACT;
1333
1334 _ASSERTE(pThisUNSAFE != NULL);
1335 VALIDATEOBJECT(pThisUNSAFE);
1336 Thread *pThread = pThisUNSAFE->GetInternal();
1337
1338 HELPER_METHOD_FRAME_BEGIN_0();
1339
1340 if (pThread == NULL)
1341 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
1342
1343 pThread->SetDisableComObjectEagerCleanup();
1344
1345 HELPER_METHOD_FRAME_END();
1346}
1347FCIMPLEND
1348#endif //FEATURE_COMINTEROP
1349
1350void QCALLTYPE ThreadNative::InformThreadNameChange(QCall::ThreadHandle thread, LPCWSTR name, INT32 len)
1351{
1352 QCALL_CONTRACT;
1353
1354 BEGIN_QCALL;
1355
1356 Thread* pThread = &(*thread);
1357
1358#ifndef FEATURE_PAL
1359 // Set on Windows 10 Creators Update and later machines the unmanaged thread name as well. That will show up in ETW traces and debuggers which is very helpful
1360 // if more and more threads get a meaningful name
1361 if (len > 0 && name != NULL && pThread->GetThreadHandle() != INVALID_HANDLE_VALUE)
1362 {
1363 SetThreadName(pThread->GetThreadHandle(), name);
1364 }
1365#endif
1366
1367#ifdef PROFILING_SUPPORTED
1368 {
1369 BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
1370 if (name == NULL)
1371 {
1372 g_profControlBlock.pProfInterface->ThreadNameChanged((ThreadID)pThread, 0, NULL);
1373 }
1374 else
1375 {
1376 g_profControlBlock.pProfInterface->ThreadNameChanged((ThreadID)pThread, len, (WCHAR*)name);
1377 }
1378 END_PIN_PROFILER();
1379 }
1380#endif // PROFILING_SUPPORTED
1381
1382
1383#ifdef DEBUGGING_SUPPORTED
1384 if (CORDebuggerAttached())
1385 {
1386 _ASSERTE(NULL != g_pDebugInterface);
1387 g_pDebugInterface->NameChangeEvent(NULL, pThread);
1388 }
1389#endif // DEBUGGING_SUPPORTED
1390
1391 END_QCALL;
1392}
1393
1394UINT64 QCALLTYPE ThreadNative::GetProcessDefaultStackSize()
1395{
1396 QCALL_CONTRACT;
1397
1398 SIZE_T reserve = 0;
1399 SIZE_T commit = 0;
1400
1401 BEGIN_QCALL;
1402
1403 if (!Thread::GetProcessDefaultStackSize(&reserve, &commit))
1404 reserve = 1024 * 1024;
1405
1406 END_QCALL;
1407
1408 return (UINT64)reserve;
1409}
1410
1411
1412
1413FCIMPL1(FC_BOOL_RET, ThreadNative::IsThreadpoolThread, ThreadBaseObject* thread)
1414{
1415 FCALL_CONTRACT;
1416
1417 if (thread==NULL)
1418 FCThrowRes(kNullReferenceException, W("NullReference_This"));
1419
1420 Thread *pThread = thread->GetInternal();
1421
1422 if (pThread == NULL)
1423 FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
1424
1425 BOOL ret = pThread->IsThreadPoolThread();
1426
1427 FC_GC_POLL_RET();
1428
1429 FC_RETURN_BOOL(ret);
1430}
1431FCIMPLEND
1432
1433INT32 QCALLTYPE ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration()
1434{
1435 QCALL_CONTRACT;
1436
1437 INT32 optimalMaxNormalizedYieldsPerSpinIteration;
1438
1439 BEGIN_QCALL;
1440
1441 // RuntimeThread calls this function only once lazily and caches the result, so ensure initialization
1442 EnsureYieldProcessorNormalizedInitialized();
1443 optimalMaxNormalizedYieldsPerSpinIteration = g_optimalMaxNormalizedYieldsPerSpinIteration;
1444
1445 END_QCALL;
1446
1447 return optimalMaxNormalizedYieldsPerSpinIteration;
1448}
1449
1450FCIMPL1(void, ThreadNative::SpinWait, int iterations)
1451{
1452 FCALL_CONTRACT;
1453
1454 if (iterations <= 0)
1455 {
1456 return;
1457 }
1458
1459 //
1460 // If we're not going to spin for long, it's ok to remain in cooperative mode.
1461 // The threshold is determined by the cost of entering preemptive mode; if we're
1462 // spinning for less than that number of cycles, then switching to preemptive
1463 // mode won't help a GC start any faster.
1464 //
1465 if (iterations <= 100000)
1466 {
1467 YieldProcessorNormalized(YieldProcessorNormalizationInfo(), iterations);
1468 return;
1469 }
1470
1471 //
1472 // Too many iterations; better switch to preemptive mode to avoid stalling a GC.
1473 //
1474 HELPER_METHOD_FRAME_BEGIN_NOPOLL();
1475 GCX_PREEMP();
1476
1477 YieldProcessorNormalized(YieldProcessorNormalizationInfo(), iterations);
1478
1479 HELPER_METHOD_FRAME_END();
1480}
1481FCIMPLEND
1482
1483BOOL QCALLTYPE ThreadNative::YieldThread()
1484{
1485 QCALL_CONTRACT;
1486
1487 BOOL ret = FALSE;
1488
1489 BEGIN_QCALL
1490
1491 ret = __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1492
1493 END_QCALL
1494
1495 return ret;
1496}
1497
1498FCIMPL2(void, ThreadNative::SetAbortReason, ThreadBaseObject* pThisUNSAFE, Object* pObject)
1499{
1500 FCALL_CONTRACT;
1501
1502 if (pThisUNSAFE==NULL)
1503 FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
1504
1505 OBJECTREF refObject = static_cast<OBJECTREF>(pObject);
1506
1507 Thread *pThread = pThisUNSAFE->GetInternal();
1508
1509 // If the OBJECTHANDLE is not 0, already set so just return
1510 if (pThread != NULL && pThread->m_AbortReason != 0)
1511 return;
1512
1513 // Set up a frame in case of GC or EH
1514 HELPER_METHOD_FRAME_BEGIN_1(refObject)
1515
1516 if (pThread == NULL)
1517 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
1518
1519 // Get the AppDomain ID for the AppDomain on the currently running thread.
1520 // NOTE: the currently running thread may be different from this thread object!
1521 AppDomain *pCurrentDomain = GetThread()->GetDomain();
1522 ADID adid = pCurrentDomain->GetId();
1523
1524 // Create a OBJECTHANDLE for the object.
1525 OBJECTHANDLE oh = pCurrentDomain->CreateHandle(refObject);
1526
1527 // Scope the lock to peeking at and updating the two fields on the Thread object.
1528 { // Atomically check whether the OBJECTHANDLE has been set, and if not,
1529 // store it and the ADID of the object.
1530 // NOTE: get the lock on this thread object, not on the executing thread.
1531 Thread::AbortRequestLockHolder lock(pThread);
1532 if (pThread->m_AbortReason == 0)
1533 {
1534 pThread->m_AbortReason = oh;
1535 pThread->m_AbortReasonDomainID = adid;
1536 // Set the OBJECTHANDLE so we can know that we stored it on the Thread object.
1537 oh = 0;
1538 }
1539 }
1540
1541 // If the OBJECTHANDLE created above was not stored onto the Thread object, then
1542 // another thread beat this one to the update. Destroy the OBJECTHANDLE that
1543 // was not used, created above.
1544 if (oh != 0)
1545 {
1546 DestroyHandle(oh);
1547 }
1548
1549 HELPER_METHOD_FRAME_END()
1550
1551}
1552FCIMPLEND
1553
1554
1555FCIMPL1(void, ThreadNative::ClearAbortReason, ThreadBaseObject* pThisUNSAFE)
1556{
1557 FCALL_CONTRACT;
1558
1559 if (pThisUNSAFE==NULL)
1560 FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
1561
1562 Thread *pThread = pThisUNSAFE->GetInternal();
1563
1564 // Clearing from managed code can only happen on the current thread.
1565 _ASSERTE(pThread == GetThread());
1566
1567 HELPER_METHOD_FRAME_BEGIN_0();
1568
1569 if (pThread == NULL)
1570 COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
1571
1572 pThread->ClearAbortReason();
1573
1574 HELPER_METHOD_FRAME_END();
1575
1576}
1577FCIMPLEND
1578
1579
1580FCIMPL0(INT32, ThreadNative::GetCurrentProcessorNumber)
1581{
1582 FCALL_CONTRACT;
1583
1584 return ::GetCurrentProcessorNumber();
1585}
1586FCIMPLEND;
1587