Skip to content

Commit 39716d3

Browse files
committed
Address feedback
1 parent a4cc7b0 commit 39716d3

File tree

6 files changed

+135
-109
lines changed

6 files changed

+135
-109
lines changed

src/coreclr/vm/proftoeeinterfaceimpl.cpp

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6855,6 +6855,13 @@ HRESULT ProfToEEInterfaceImpl::SuspendRuntime()
68556855
(LF_CORPROF,
68566856
LL_INFO1000,
68576857
"**PROF: SuspendRuntime\n"));
6858+
if (!IsCalledAsynchronously() && !AreCallbackStateFlagsSet(COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE))
6859+
{
6860+
LOG((LF_CORPROF,
6861+
LL_ERROR,
6862+
"**PROF: ERROR: Returning CORPROF_E_UNSUPPORTED_CALL_SEQUENCE due to illegal asynchronous profiler call\n"));
6863+
return CORPROF_E_UNSUPPORTED_CALL_SEQUENCE;
6864+
}
68586865

68596866
if (!g_fEEStarted)
68606867
{
@@ -6887,6 +6894,13 @@ HRESULT ProfToEEInterfaceImpl::ResumeRuntime()
68876894
(LF_CORPROF,
68886895
LL_INFO1000,
68896896
"**PROF: ResumeRuntime\n"));
6897+
if (!IsCalledAsynchronously() && !AreCallbackStateFlagsSet(COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE))
6898+
{
6899+
LOG((LF_CORPROF,
6900+
LL_ERROR,
6901+
"**PROF: ERROR: Returning CORPROF_E_UNSUPPORTED_CALL_SEQUENCE due to illegal asynchronous profiler call\n"));
6902+
return CORPROF_E_UNSUPPORTED_CALL_SEQUENCE;
6903+
}
68906904

68916905
if (!g_fEEStarted)
68926906
{
@@ -7644,6 +7658,13 @@ HRESULT ProfToEEInterfaceImpl::EnumerateGCHeapObjects(ObjectCallback callback, v
76447658
(LF_CORPROF,
76457659
LL_INFO1000,
76467660
"**PROF: EnumerateGCHeapObjects.\n"));
7661+
if (!IsCalledAsynchronously() && !AreCallbackStateFlagsSet(COR_PRF_CALLBACKSTATE_IN_TRIGGERS_SCOPE))
7662+
{
7663+
LOG((LF_CORPROF,
7664+
LL_ERROR,
7665+
"**PROF: ERROR: Returning CORPROF_E_UNSUPPORTED_CALL_SEQUENCE due to illegal asynchronous profiler call\n"));
7666+
return CORPROF_E_UNSUPPORTED_CALL_SEQUENCE;
7667+
}
76477668

76487669
if (callback == nullptr)
76497670
{
@@ -7655,22 +7676,38 @@ HRESULT ProfToEEInterfaceImpl::EnumerateGCHeapObjects(ObjectCallback callback, v
76557676
return CORPROF_E_RUNTIME_UNINITIALIZED;
76567677
}
76577678

7658-
bool ownEESuspension = FALSE;
7659-
if (!ThreadSuspend::SysIsSuspendInProgress() && (ThreadSuspend::GetSuspensionThread() == 0))
7679+
bool ownEESuspension = false;
7680+
bool suspendedByThisThread = (ThreadSuspend::GetSuspensionThread() == GetThreadNULLOk());
7681+
if (suspendedByThisThread && !g_profControlBlock.fProfilerRequestedRuntimeSuspend)
76607682
{
7661-
g_profControlBlock.fProfilerRequestedRuntimeSuspend = TRUE;
7662-
ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_FOR_PROFILER);
7663-
ownEESuspension = TRUE;
7683+
// This thread is responsible for suspending the runtime so we can't block
7684+
// waiting for the runtime to resume. However it wasn't the profiler call that did
7685+
// the suspend so we don't know what state the GC heap is in. Other threads also might
7686+
// be modifying it concurrently. In the future more analysis or coordination with other
7687+
// suspenders might let us narrow the scope of this error condition, but we have no need
7688+
// to do this now.
7689+
return CORPROF_E_SUSPENSION_IN_PROGRESS;
76647690
}
7665-
else if (!g_profControlBlock.fProfilerRequestedRuntimeSuspend)
7691+
else if (suspendedByThisThread && g_profControlBlock.fProfilerRequestedRuntimeSuspend)
76667692
{
7667-
return CORPROF_E_SUSPENSION_IN_PROGRESS;
7693+
// This thread previously invoked ICorProfiler::SuspendRuntime(). Our caller
7694+
// has the responsibility to resume the runtime no earlier than when this API returns
7695+
// and to preserve the GC heap in a consistent state. We should avoid invoking
7696+
// SuspendEE/ResumeEE again in this function because those APIs do not support
7697+
// re-entrant suspends.
76687698
}
76697699
else
76707700
{
7671-
// The profiler requested a runtime suspension before invoking this API,
7672-
// so the responsibility of resuming the runtime is outside the scope of this API.
7673-
// Given that the profiler owns the suspension, walking the GC Heap is safe.
7701+
_ASSERTE(!suspendedByThisThread);
7702+
// Its possible some background threads are suspending and resuming the runtime
7703+
// concurrently. We need to suspend the runtime on this thread to be certain the heap
7704+
// stays in a walkable state for the duration that we need it to. Our call to
7705+
// SuspendEE() may race with other threads by design and this thread may block
7706+
// arbitrarily long inside SuspendEE() for other threads to complete their own
7707+
// suspensions.
7708+
g_profControlBlock.fProfilerRequestedRuntimeSuspend = TRUE;
7709+
ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_FOR_PROFILER);
7710+
ownEESuspension = TRUE;
76747711
}
76757712

76767713
// Suspending EE ensures safe object inspection. We permit the GC Heap walk callback to

src/tests/profiler/gcheapenumeration/gcheapenumeration.cs

Lines changed: 10 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,15 @@ class GCHeapEnumerationTests
1515
static List<object> _rootObjects = new List<object>();
1616

1717
[DllImport("Profiler")]
18-
private static extern void EnumerateGCHeapObjects();
19-
20-
[DllImport("Profiler")]
21-
private static extern void EnumerateHeapObjectsInBackgroundThread();
18+
private static extern void EnumerateGCHeapObjectsWithoutProfilerRequestedRuntimeSuspension();
2219

2320
[DllImport("Profiler")]
2421
private static extern void EnumerateGCHeapObjectsWithinProfilerRequestedRuntimeSuspension();
2522

2623
public static int EnumerateGCHeapObjectsSingleThreadNoPriorSuspension()
2724
{
2825
_rootObjects.Add(new CustomGCHeapObject());
29-
EnumerateGCHeapObjects();
26+
EnumerateGCHeapObjectsWithoutProfilerRequestedRuntimeSuspension();
3027
return 100;
3128
}
3229

@@ -37,39 +34,15 @@ public static int EnumerateGCHeapObjectsSingleThreadWithinProfilerRequestedRunti
3734
return 100;
3835
}
3936

40-
public static int EnumerateGCHeapObjectsInBackgroundThreadWithRuntimeSuspension()
37+
// Test invoking ProfToEEInterfaceImpl::EnumerateGCHeapObjects during non-profiler requested runtime suspension, e.g. during GC
38+
// ProfToEEInterfaceImpl::EnumerateGCHeapObjects should be invoked by the GarbageCollectionStarted callback
39+
public static int EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension()
4140
{
4241
_rootObjects.Add(new CustomGCHeapObject());
43-
EnumerateHeapObjectsInBackgroundThread();
4442
GC.Collect();
4543
return 100;
4644
}
4745

48-
public static int EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension()
49-
{
50-
ManualResetEvent startEvent = new ManualResetEvent(false);
51-
Thread gcThread = new Thread(() =>
52-
{
53-
startEvent.WaitOne();
54-
GC.Collect();
55-
});
56-
gcThread.Name = "GC.Collect";
57-
gcThread.Start();
58-
59-
Thread enumerateThread = new Thread(() =>
60-
{
61-
startEvent.WaitOne();
62-
Thread.Sleep(1000);
63-
_rootObjects.Add(new CustomGCHeapObject());
64-
EnumerateGCHeapObjects();
65-
});
66-
enumerateThread.Name = "EnumerateGCHeapObjects";
67-
enumerateThread.Start();
68-
69-
startEvent.Set();
70-
return 100;
71-
}
72-
7346
public static int Main(string[] args)
7447
{
7548
if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase))
@@ -82,38 +55,30 @@ public static int Main(string[] args)
8255
case nameof(EnumerateGCHeapObjectsSingleThreadWithinProfilerRequestedRuntimeSuspension):
8356
return EnumerateGCHeapObjectsSingleThreadWithinProfilerRequestedRuntimeSuspension();
8457

85-
case nameof(EnumerateGCHeapObjectsInBackgroundThreadWithRuntimeSuspension):
86-
return EnumerateGCHeapObjectsInBackgroundThreadWithRuntimeSuspension();
87-
8858
case nameof(EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension):
8959
return EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension();
9060
}
9161
}
9262

93-
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsSingleThreadNoPriorSuspension), "FALSE"))
63+
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsSingleThreadNoPriorSuspension), false))
9464
{
9565
return 101;
9666
}
9767

98-
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsSingleThreadWithinProfilerRequestedRuntimeSuspension), "FALSE"))
68+
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsSingleThreadWithinProfilerRequestedRuntimeSuspension), false))
9969
{
10070
return 102;
10171
}
10272

103-
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsInBackgroundThreadWithRuntimeSuspension), "TRUE"))
73+
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension), true))
10474
{
10575
return 103;
10676
}
10777

108-
if (!RunProfilerTest(nameof(EnumerateGCHeapObjectsMultiThreadWithCompetingRuntimeSuspension), "FALSE"))
109-
{
110-
return 104;
111-
}
112-
11378
return 100;
11479
}
11580

116-
private static bool RunProfilerTest(string testName, string shouldSetMonitorGCEventMask)
81+
private static bool RunProfilerTest(string testName, bool shouldSetMonitorGCEventMask)
11782
{
11883
try
11984
{
@@ -123,7 +88,7 @@ private static bool RunProfilerTest(string testName, string shouldSetMonitorGCEv
12388
profileeArguments: testName,
12489
envVars: new Dictionary<string, string>
12590
{
126-
{ShouldSetMonitorGCEventMaskEnvVar, shouldSetMonitorGCEventMask},
91+
{ShouldSetMonitorGCEventMaskEnvVar, shouldSetMonitorGCEventMask ? "TRUE" : "FALSE"},
12792
}
12893
) == 100;
12994
}

0 commit comments

Comments
 (0)