| 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. | 
|---|
| 40 | struct 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. | 
|---|
| 85 | static inline BOOL ThreadNotStarted(Thread *t) | 
|---|
| 86 | { | 
|---|
| 87 | WRAPPER_NO_CONTRACT; | 
|---|
| 88 | return (t && t->IsUnstarted() && !t->HasValidThreadHandle()); | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 | static 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 |  | 
|---|
| 99 | static 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. | 
|---|
| 107 | static 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. | 
|---|
| 149 | INT32 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 |  | 
|---|
| 185 | void 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 | 
|---|
| 257 | static 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. | 
|---|
| 283 | ULONG 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 |  | 
|---|
| 390 | FCIMPL1(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 | } | 
|---|
| 400 | FCIMPLEND | 
|---|
| 401 |  | 
|---|
| 402 | // Start up a thread, which by now should be in the ThreadStore's Unstarted list. | 
|---|
| 403 | void 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 |  | 
|---|
| 540 | FCIMPL1(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 | } | 
|---|
| 568 | FCIMPLEND | 
|---|
| 569 |  | 
|---|
| 570 | FCIMPL1(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 | } | 
|---|
| 592 | FCIMPLEND | 
|---|
| 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. | 
|---|
| 598 | FCIMPL1(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 | } | 
|---|
| 611 | FCIMPLEND | 
|---|
| 612 |  | 
|---|
| 613 | FCIMPL2(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 | } | 
|---|
| 653 | FCIMPLEND | 
|---|
| 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. | 
|---|
| 657 | FCIMPL1(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 | } | 
|---|
| 675 | FCIMPLEND | 
|---|
| 676 |  | 
|---|
| 677 | FCIMPL1(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 | } | 
|---|
| 706 | FCIMPLEND | 
|---|
| 707 |  | 
|---|
| 708 | FCIMPL2(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 | } | 
|---|
| 730 | FCIMPLEND | 
|---|
| 731 |  | 
|---|
| 732 | #undef Sleep | 
|---|
| 733 | FCIMPL1(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 | } | 
|---|
| 755 | FCIMPLEND | 
|---|
| 756 |  | 
|---|
| 757 | #define Sleep(dwMilliseconds) Dont_Use_Sleep(dwMilliseconds) | 
|---|
| 758 |  | 
|---|
| 759 | FCIMPL1(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 | } | 
|---|
| 768 | FCIMPLEND | 
|---|
| 769 |  | 
|---|
| 770 | NOINLINE 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 |  | 
|---|
| 784 | FCIMPL0(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 | } | 
|---|
| 795 | FCIMPLEND | 
|---|
| 796 |  | 
|---|
| 797 | UINT64 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 |  | 
|---|
| 819 | FCIMPL3(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 | } | 
|---|
| 858 | FCIMPLEND | 
|---|
| 859 |  | 
|---|
| 860 |  | 
|---|
| 861 | // Set whether or not this is a background thread. | 
|---|
| 862 | FCIMPL2(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 | } | 
|---|
| 881 | FCIMPLEND | 
|---|
| 882 |  | 
|---|
| 883 | // Return whether or not this is a background thread. | 
|---|
| 884 | FCIMPL1(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 | } | 
|---|
| 899 | FCIMPLEND | 
|---|
| 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 | 
|---|
| 906 | FCIMPL1(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 | } | 
|---|
| 959 | FCIMPLEND | 
|---|
| 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). | 
|---|
| 966 | FCIMPL3(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 | } | 
|---|
| 1041 | FCIMPLEND | 
|---|
| 1042 |  | 
|---|
| 1043 | // Return whether the thread hosts an STA, is a member of the MTA or is not | 
|---|
| 1044 | // currently initialized for COM. | 
|---|
| 1045 | FCIMPL1(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 | } | 
|---|
| 1106 | FCIMPLEND | 
|---|
| 1107 |  | 
|---|
| 1108 |  | 
|---|
| 1109 | // Attempt to eagerly set the apartment state during thread startup. | 
|---|
| 1110 | FCIMPL1(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 | } | 
|---|
| 1139 | FCIMPLEND | 
|---|
| 1140 |  | 
|---|
| 1141 | #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT | 
|---|
| 1142 |  | 
|---|
| 1143 | void ReleaseThreadExternalCount(Thread * pThread) | 
|---|
| 1144 | { | 
|---|
| 1145 | WRAPPER_NO_CONTRACT; | 
|---|
| 1146 | pThread->DecExternalCount(FALSE); | 
|---|
| 1147 | } | 
|---|
| 1148 |  | 
|---|
| 1149 | typedef Holder<Thread *, DoNothing, ReleaseThreadExternalCount> ThreadExternalCountHolder; | 
|---|
| 1150 |  | 
|---|
| 1151 | // Wait for the thread to die | 
|---|
| 1152 | BOOL 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. | 
|---|
| 1234 | void 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. | 
|---|
| 1268 | void 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 |  | 
|---|
| 1316 | FCIMPL1(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 | } | 
|---|
| 1327 | FCIMPLEND | 
|---|
| 1328 |  | 
|---|
| 1329 | #ifdef FEATURE_COMINTEROP | 
|---|
| 1330 | FCIMPL1(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 | } | 
|---|
| 1347 | FCIMPLEND | 
|---|
| 1348 | #endif //FEATURE_COMINTEROP | 
|---|
| 1349 |  | 
|---|
| 1350 | void 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 |  | 
|---|
| 1394 | UINT64 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 |  | 
|---|
| 1413 | FCIMPL1(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 | } | 
|---|
| 1431 | FCIMPLEND | 
|---|
| 1432 |  | 
|---|
| 1433 | INT32 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 |  | 
|---|
| 1450 | FCIMPL1(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 | } | 
|---|
| 1481 | FCIMPLEND | 
|---|
| 1482 |  | 
|---|
| 1483 | BOOL 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 |  | 
|---|
| 1498 | FCIMPL2(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 | } | 
|---|
| 1552 | FCIMPLEND | 
|---|
| 1553 |  | 
|---|
| 1554 |  | 
|---|
| 1555 | FCIMPL1(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 | } | 
|---|
| 1577 | FCIMPLEND | 
|---|
| 1578 |  | 
|---|
| 1579 |  | 
|---|
| 1580 | FCIMPL0(INT32, ThreadNative::GetCurrentProcessorNumber) | 
|---|
| 1581 | { | 
|---|
| 1582 | FCALL_CONTRACT; | 
|---|
| 1583 |  | 
|---|
| 1584 | return ::GetCurrentProcessorNumber(); | 
|---|
| 1585 | } | 
|---|
| 1586 | FCIMPLEND; | 
|---|
| 1587 |  | 
|---|