Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum ThreadState
Unstarted = 0x00000400, // Thread has never been started
Stopped = 0x00010000, // Thread has started to shut down
ThreadPoolWorker = 0x01000000, // is this a threadpool worker thread?
WaitSleepJoin = 0x02000000, // Thread is in a Sleep(), Wait(), Join()
Detached = unchecked((int)0x80000000), // Thread was detached
}

Expand Down
6 changes: 1 addition & 5 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5693,11 +5693,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetPartialUserState(VMPTR_Thread

// Don't report Thread::TS_AbortRequested

Comment thread
barosiak marked this conversation as resolved.
Outdated
// The interruptible flag is unreliable (see issue 699245)
// The Debugger_SleepWaitJoin is always accurate when it is present, but it is still
// just a band-aid fix to cover some of the race conditions interruptible has.

if (ts & Thread::TS_Interruptible || pThread->HasThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin))
if (ts & Thread::TS_WaitSleepJoin)
{
result |= USER_WAIT_SLEEP_JOIN;
}
Comment thread
barosiak marked this conversation as resolved.
Expand Down
8 changes: 3 additions & 5 deletions src/coreclr/vm/comsynchronizable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle threa
if (state & Thread::TS_AbortRequested)
res |= ThreadNative::ThreadAbortRequested;

if (state & Thread::TS_Interruptible)
if (state & Thread::TS_WaitSleepJoin)
res |= ThreadNative::ThreadWaitSleepJoin;

return res;
Expand All @@ -436,8 +436,7 @@ extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle
CONTRACTL_END;

// Set the state bits.
thread->SetThreadState(Thread::TS_Interruptible);
thread->SetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin);
thread->SetThreadState(Thread::TS_WaitSleepJoin);
}

extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread)
Expand All @@ -450,8 +449,7 @@ extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHand
CONTRACTL_END;

// Clear the state bits.
thread->ResetThreadState(Thread::TS_Interruptible);
thread->ResetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin);
thread->ResetThreadState(Thread::TS_WaitSleepJoin);
}

#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/eedbginterfaceimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,7 @@ CorDebugUserState EEDbgInterfaceImpl::GetPartialUserState(Thread *pThread)
ret |= (unsigned)USER_STOPPED;
}

if (ts & Thread::TS_Interruptible)
if (ts & Thread::TS_WaitSleepJoin)
{
ret |= (unsigned)USER_WAIT_SLEEP_JOIN;
}
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2992,7 +2992,7 @@ void Thread::UserInterrupt(ThreadInterruptMode mode)
InterlockedOr(&m_UserInterrupt, mode);

if (HasValidThreadHandle() &&
HasThreadState (TS_Interruptible))
HasThreadState (TS_WaitSleepJoin))
{
HANDLE handle = GetThreadHandle();
if (handle != INVALID_HANDLE_VALUE)
Expand Down Expand Up @@ -4463,7 +4463,7 @@ void Thread::HandleThreadInterrupt ()
}
if ((m_UserInterrupt & TI_Interrupt) != 0)
{
ResetThreadState ((ThreadState)(TS_Interrupted | TS_Interruptible));
ResetThreadState ((ThreadState)(TS_Interrupted | TS_WaitSleepJoin));
InterlockedAnd (&m_UserInterrupt, ~TI_Interrupt);

COMPlusThrow(kThreadInterruptedException);
Expand Down
8 changes: 3 additions & 5 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ class Thread
// unused = 0x00800000,
TS_TPWorkerThread = 0x01000000, // is this a threadpool worker thread? [cDAC] [Thread]: Contract depends on this value.

TS_Interruptible = 0x02000000, // sitting in a Sleep(), Wait(), Join()
TS_WaitSleepJoin = 0x02000000, // sitting in a Sleep(), Wait(), Join(). [cDAC] [Thread]: Contract depends on this value.
TS_Interrupted = 0x04000000, // was awakened by an interrupt APC. !!! This can be moved to TSNC

// unused
Expand Down Expand Up @@ -583,7 +583,7 @@ class Thread
// <TODO>@TODO: its possible that the ThreadTasks from above and these flags should be merged.</TODO>
enum ThreadStateNoConcurrency
{
TSNC_Unknown = 0x00000000, // threads are initialized this way
TSNC_Unknown = 0x00000000, // threads are initialized this way [cDAC] [Thread]: Contract depends on this value.
Comment thread
barosiak marked this conversation as resolved.
Outdated

TSNC_DebuggerUserSuspend = 0x00000001, // marked "suspended" by the debugger
// unused = 0x00000002,
Expand Down Expand Up @@ -623,9 +623,7 @@ class Thread
//
// Once we are completely independent of the OS UEF, we could remove this.
TSNC_SkipManagedPersonalityRoutine = 0x02000000, // Ignore the ProcessCLRException calls when propagating exception to external native code
TSNC_DebuggerSleepWaitJoin = 0x04000000, // Indicates to the debugger that this thread is in a sleep wait or join state
// This almost mirrors the TS_Interruptible state however that flag can change
// during GC-preemptive mode whereas this one cannot.
// unused = 0x04000000,
// unused = 0x08000000,
TSNC_TSLTakenForStartup = 0x10000000, // The ThreadStoreLock (TSL) is held by another mechanism during
// thread startup so can be skipped.
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/threadsuspend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1538,7 +1538,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout)

// If the thread is in sleep, wait, or join interrupt it
// However, we do NOT want to interrupt if the thread is already processing an exception
if (m_State & TS_Interruptible)
if (m_State & TS_WaitSleepJoin)
{
UserInterrupt(TI_Abort); // if the user wakes up because of this, it will read the
// abort requested bit and initiate the abort
Expand Down Expand Up @@ -2221,7 +2221,7 @@ void Thread::HandleThreadAbort ()

if (ReadyForAbort())
{
ResetThreadState ((ThreadState)(TS_Interrupted | TS_Interruptible));
ResetThreadState ((ThreadState)(TS_Interrupted | TS_WaitSleepJoin));
// We are going to abort. Abort satisfies Thread.Interrupt requirement.
InterlockedExchange (&m_UserInterrupt, 0);

Expand Down Expand Up @@ -2267,7 +2267,7 @@ void Thread::PreWorkForThreadAbort()
SetAbortInitiated();
// if an abort and interrupt happen at the same time (e.g. on a sleeping thread),
// the abort is favored. But we do need to reset the interrupt bits.
ResetThreadState((ThreadState)(TS_Interruptible | TS_Interrupted));
ResetThreadState((ThreadState)(TS_WaitSleepJoin | TS_Interrupted));
ResetUserInterrupted();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public enum ThreadState
Unstarted = 0x00000400, // Thread has never been started
Stopped = 0x00010000, // Thread has started to shut down
ThreadPoolWorker = 0x01000000, // Thread is a thread pool worker thread
WaitSleepJoin = 0x02000000, // Thread is in a Sleep(), Wait(), Join()
Detached = unchecked((int)0x80000000), // Thread was detached
}

Comment thread
barosiak marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ private enum ThreadState_1
Unstarted = 0x400,
Stopped = 0x10000,
ThreadPoolWorker = 0x1000000,
WaitSleepJoin = 0x2000000,
Detached = unchecked((int)0x80000000)
}

Expand Down Expand Up @@ -74,6 +75,8 @@ private static Contracts.ThreadState GetThreadState(ThreadState_1 state)
result |= Contracts.ThreadState.Unstarted;
if (state.HasFlag(ThreadState_1.Stopped))
result |= Contracts.ThreadState.Stopped;
if (state.HasFlag(ThreadState_1.WaitSleepJoin))
result |= Contracts.ThreadState.WaitSleepJoin;
if (state.HasFlag(ThreadState_1.ThreadPoolWorker))
result |= Contracts.ThreadState.ThreadPoolWorker;
if (state.HasFlag(ThreadState_1.Detached))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,50 @@ public int HasUnhandledException(ulong vmThread, Interop.BOOL* pResult)
public int GetUserState(ulong vmThread, int* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetUserState(vmThread, pRetVal) : HResults.E_NOTIMPL;

public int GetPartialUserState(ulong vmThread, int* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetPartialUserState(vmThread, pRetVal) : HResults.E_NOTIMPL;
public int GetPartialUserState(ulong vmThread, CorDebugUserState* pRetVal)
{
*pRetVal = default;
int hr = HResults.S_OK;
try
Comment thread
barosiak marked this conversation as resolved.
{
TargetPointer threadPtr = new TargetPointer(vmThread);
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(threadPtr);
Contracts.ThreadState threadState = threadData.State;

CorDebugUserState result = default;
if ((threadState & Contracts.ThreadState.Background) != 0)
result |= CorDebugUserState.USER_BACKGROUND;

if ((threadState & Contracts.ThreadState.Unstarted) != 0)
result |= CorDebugUserState.USER_UNSTARTED;

if ((threadState & Contracts.ThreadState.Stopped) != 0)
result |= CorDebugUserState.USER_STOPPED;

if ((threadState & Contracts.ThreadState.WaitSleepJoin) != 0)
result |= CorDebugUserState.USER_WAIT_SLEEP_JOIN;

if ((threadState & Contracts.ThreadState.ThreadPoolWorker) != 0)
result |= CorDebugUserState.USER_THREADPOOL;

*pRetVal = result;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
CorDebugUserState retValLocal;
int hrLocal = _legacy.GetPartialUserState(vmThread, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal}, DAC: {retValLocal}");
}
#endif
return hr;
}

public int GetConnectionID(ulong vmThread, uint* pRetVal)
{
Expand Down Expand Up @@ -2034,4 +2076,5 @@ public int GetAsyncLocals(ulong vmMethod, ulong codeAddr, uint state, nint pAsyn

public int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetGenericArgTokenIndex(vmMethod, pIndex) : HResults.E_NOTIMPL;

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

Expand Down Expand Up @@ -145,6 +146,16 @@ public enum DynamicMethodType
kLCGMethod = 2,
}

[Flags]
public enum CorDebugUserState
{
USER_BACKGROUND = 0x04,
USER_UNSTARTED = 0x08,
USER_STOPPED = 0x10,
USER_WAIT_SLEEP_JOIN = 0x20,
USER_THREADPOOL = 0x100,
}
Comment thread
barosiak marked this conversation as resolved.

// Name-surface projection of IDacDbiInterface in native method order for COM binding validation.
// Parameter shapes are intentionally coarse placeholders and will be refined with method implementation work.
[GeneratedComInterface]
Expand Down Expand Up @@ -248,7 +259,7 @@ public unsafe partial interface IDacDbiInterface
int GetUserState(ulong vmThread, int* pRetVal);

[PreserveSig]
int GetPartialUserState(ulong vmThread, int* pRetVal);
int GetPartialUserState(ulong vmThread, CorDebugUserState* pRetVal);

[PreserveSig]
int GetConnectionID(ulong vmThread, uint* pRetVal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,35 @@ public unsafe void GetCurrentException_AtLeastOneThreadHasException(TestConfigur
Assert.True(foundException, "Expected at least one thread to have a current exception in the FailFast dump.");
}

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
public unsafe void GetPartialUserState_CrossValidateWithContract(TestConfiguration config)
{
InitializeDumpTest(config);
DacDbiImpl dbi = CreateDacDbi();

IThread threadContract = Target.Contracts.Thread;
ThreadStoreData storeData = threadContract.GetThreadStoreData();

TargetPointer current = storeData.FirstThread;
while (current != TargetPointer.Null)
{
CorDebugUserState userState;
int hr = dbi.GetPartialUserState(current, &userState);
Assert.Equal(System.HResults.S_OK, hr);

ThreadData data = threadContract.GetThreadData(current);

Assert.Equal((data.State & ThreadState.Background) != 0, userState.HasFlag(CorDebugUserState.USER_BACKGROUND));
Assert.Equal((data.State & ThreadState.Unstarted) != 0, userState.HasFlag(CorDebugUserState.USER_UNSTARTED));
Assert.Equal((data.State & ThreadState.Stopped) != 0, userState.HasFlag(CorDebugUserState.USER_STOPPED));
Assert.Equal((data.State & ThreadState.WaitSleepJoin) != 0, userState.HasFlag(CorDebugUserState.USER_WAIT_SLEEP_JOIN));
Assert.Equal((data.State & ThreadState.ThreadPoolWorker) != 0, userState.HasFlag(CorDebugUserState.USER_THREADPOOL));

current = data.NextThread;
}
}

[UnmanagedCallersOnly]
private static unsafe void CountThreadCallback(ulong addr, nint userData)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ public ulong OSId
set => WritePointerField(OSIdFieldName, value);
}

public uint State
{
get => ReadUInt32Field(StateFieldName);
set => WriteUInt32Field(StateFieldName, value);
}

public ulong RuntimeThreadLocals
{
get => ReadPointerField(RuntimeThreadLocalsFieldName);
Expand Down
24 changes: 24 additions & 0 deletions src/native/managed/cdac/tests/ThreadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ public void GetThreadData(MockTarget.Architecture arch)
Assert.Equal(new TargetNUInt(osId), data.OSId);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetThreadData_MapsStateFlags(MockTarget.Architecture arch)
{
const uint id = 1;
const ulong osId = 1234;
const uint state = 0x02000200; // WaitSleepJoin | Background
Comment thread
barosiak marked this conversation as resolved.
Outdated
MockThread? thread = null;

TestPlaceholderTarget target = CreateTarget(
arch,
threadBuilder =>
{
thread = threadBuilder.AddThread(id, osId);
thread.State = state;
});

IThread contract = target.Contracts.Thread;
ThreadData data = contract.GetThreadData(new TargetPointer(thread!.Address));
Assert.True(data.State.HasFlag(ThreadState.Background));
Assert.True(data.State.HasFlag(ThreadState.WaitSleepJoin));
Assert.False(data.State.HasFlag(ThreadState.Stopped));
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void IterateThreads(MockTarget.Architecture arch)
Expand Down
Loading