Skip to content

Commit b18871c

Browse files
committed
Share the sleep impl between all runtimes on Windows and unix
1 parent 3965419 commit b18871c

File tree

8 files changed

+50
-157
lines changed

8 files changed

+50
-157
lines changed

src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,6 @@ private void StartCallback()
112112
startHelper.Run();
113113
}
114114

115-
/// <summary>
116-
/// Suspends the current thread for timeout milliseconds. If timeout == 0,
117-
/// forces the thread to give up the remainder of its timeslice. If timeout
118-
/// == Timeout.Infinite, no timeout will occur.
119-
/// </summary>
120-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Sleep")]
121-
private static partial void SleepInternal(int millisecondsTimeout);
122-
123115
// Max iterations to be done in SpinWait without switching GC modes.
124116
private const int SpinWaitCoopThreshold = 1024;
125117

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,59 +26,6 @@ public sealed partial class Thread
2626

2727
partial void PlatformSpecificInitialize();
2828

29-
internal static void SleepInternal(int millisecondsTimeout)
30-
{
31-
Debug.Assert(millisecondsTimeout >= Timeout.Infinite);
32-
33-
CheckForPendingInterrupt();
34-
35-
Thread currentThread = CurrentThread;
36-
if (millisecondsTimeout == Timeout.Infinite)
37-
{
38-
// Infinite wait - use alertable wait
39-
currentThread.SetWaitSleepJoinState();
40-
uint result;
41-
while (true)
42-
{
43-
result = Interop.Kernel32.SleepEx(Timeout.UnsignedInfinite, true);
44-
if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
45-
{
46-
break;
47-
}
48-
CheckForPendingInterrupt();
49-
}
50-
51-
currentThread.ClearWaitSleepJoinState();
52-
}
53-
else
54-
{
55-
// Timed wait - use alertable wait
56-
currentThread.SetWaitSleepJoinState();
57-
long startTime = Environment.TickCount64;
58-
while (true)
59-
{
60-
uint result = Interop.Kernel32.SleepEx((uint)millisecondsTimeout, true);
61-
if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
62-
{
63-
break;
64-
}
65-
// Check if this was our interrupt APC
66-
CheckForPendingInterrupt();
67-
// Handle APC completion by adjusting timeout and retrying
68-
long currentTime = Environment.TickCount64;
69-
long elapsed = currentTime - startTime;
70-
if (elapsed >= millisecondsTimeout)
71-
{
72-
break;
73-
}
74-
millisecondsTimeout -= (int)elapsed;
75-
startTime = currentTime;
76-
}
77-
78-
currentThread.ClearWaitSleepJoinState();
79-
}
80-
}
81-
8229
// Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start
8330
private void PlatformSpecificInitializeExistingThread()
8431
{

src/coreclr/vm/comsynchronizable.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -857,17 +857,6 @@ extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread)
857857
END_QCALL;
858858
}
859859

860-
extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime)
861-
{
862-
QCALL_CONTRACT;
863-
864-
BEGIN_QCALL;
865-
866-
GetThread()->UserSleep(iTime);
867-
868-
END_QCALL;
869-
}
870-
871860
#ifdef FEATURE_COMINTEROP
872861
extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread)
873862
{

src/coreclr/vm/comsynchronizable.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ extern "C" void QCALLTYPE ThreadNative_Abort(QCall::ThreadHandle thread);
7171
extern "C" void QCALLTYPE ThreadNative_ResetAbort();
7272
extern "C" void QCALLTYPE ThreadNative_SpinWait(INT32 iterations);
7373
extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread);
74-
extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime);
7574
#ifdef FEATURE_COMINTEROP
7675
extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread);
7776
#endif // FEATURE_COMINTEROP

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,6 @@ static const Entry s_QCall[] =
301301
DllImportEntry(ThreadNative_ResetAbort)
302302
DllImportEntry(ThreadNative_SpinWait)
303303
DllImportEntry(ThreadNative_Interrupt)
304-
DllImportEntry(ThreadNative_Sleep)
305304
DllImportEntry(ThreadNative_PollGC)
306305
#ifdef FEATURE_COMINTEROP
307306
DllImportEntry(ThreadNative_DisableComObjectEagerCleanup)

src/coreclr/vm/threads.cpp

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3703,82 +3703,6 @@ void Thread::UserInterrupt(ThreadInterruptMode mode)
37033703
}
37043704
}
37053705

3706-
// Implementation of Thread.Sleep().
3707-
void Thread::UserSleep(INT32 time)
3708-
{
3709-
CONTRACTL {
3710-
THROWS;
3711-
GC_TRIGGERS;
3712-
}
3713-
CONTRACTL_END;
3714-
3715-
INCONTRACT(_ASSERTE(!GetThread()->GCNoTrigger()));
3716-
3717-
DWORD res;
3718-
3719-
// Before going to pre-emptive mode the thread needs to be flagged as waiting for
3720-
// the debugger. This used to be accomplished by the TS_Interruptible flag but that
3721-
// doesn't work reliably, see DevDiv Bugs 699245.
3722-
ThreadStateNCStackHolder tsNC(TRUE, TSNC_DebuggerSleepWaitJoin);
3723-
GCX_PREEMP();
3724-
3725-
// A word about ordering for Interrupt. If someone tries to interrupt a thread
3726-
// that's in the interruptible state, we queue an APC. But if they try to interrupt
3727-
// a thread that's not in the interruptible state, we just record that fact. So
3728-
// we have to set TS_Interruptible before we test to see whether someone wants to
3729-
// interrupt us or else we have a race condition that causes us to skip the APC.
3730-
SetThreadState(TS_Interruptible);
3731-
3732-
// If someone has interrupted us, we should not enter the wait.
3733-
if (IsUserInterrupted())
3734-
{
3735-
HandleThreadInterrupt();
3736-
}
3737-
3738-
ThreadStateHolder tsh(TRUE, TS_Interruptible | TS_Interrupted);
3739-
3740-
ResetThreadState(TS_Interrupted);
3741-
3742-
DWORD dwTime = (DWORD)time;
3743-
retry:
3744-
3745-
ULONGLONG start = minipal_lowres_ticks();
3746-
3747-
res = ClrSleepEx (dwTime, TRUE);
3748-
3749-
if (res == WAIT_IO_COMPLETION)
3750-
{
3751-
// We could be woken by some spurious APC or an EE APC queued to
3752-
// interrupt us. In the latter case the TS_Interrupted bit will be set
3753-
// in the thread state bits. Otherwise we just go back to sleep again.
3754-
if ((m_State & TS_Interrupted))
3755-
{
3756-
HandleThreadInterrupt();
3757-
}
3758-
3759-
if (dwTime == INFINITE)
3760-
{
3761-
goto retry;
3762-
}
3763-
else
3764-
{
3765-
ULONGLONG actDuration = minipal_lowres_ticks() - start;
3766-
3767-
if (dwTime > actDuration)
3768-
{
3769-
dwTime -= (DWORD)actDuration;
3770-
goto retry;
3771-
}
3772-
else
3773-
{
3774-
res = WAIT_TIMEOUT;
3775-
}
3776-
}
3777-
}
3778-
_ASSERTE(res == WAIT_TIMEOUT || res == WAIT_OBJECT_0);
3779-
}
3780-
3781-
37823706
// Correspondence between an EE Thread and an exposed System.Thread:
37833707
OBJECTREF Thread::GetExposedObject()
37843708
{

src/coreclr/vm/threads.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,8 +1803,6 @@ class Thread
18031803
static bool SysSweepThreadsForDebug(bool forceSync);
18041804
static void SysResumeFromDebug(AppDomain *pAppDomain);
18051805

1806-
void UserSleep(INT32 time);
1807-
18081806
private:
18091807

18101808
// Specifies type of thread abort.

src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,58 @@ public sealed partial class Thread
1818
{
1919
internal static void UninterruptibleSleep0() => Interop.Kernel32.Sleep(0);
2020

21-
#if MONO
22-
private static void SleepInternal(int millisecondsTimeout)
21+
internal static void SleepInternal(int millisecondsTimeout)
2322
{
24-
Debug.Assert(millisecondsTimeout >= -1);
25-
Interop.Kernel32.Sleep((uint)millisecondsTimeout);
23+
Debug.Assert(millisecondsTimeout >= Timeout.Infinite);
24+
25+
CheckForPendingInterrupt();
26+
27+
Thread currentThread = CurrentThread;
28+
if (millisecondsTimeout == Timeout.Infinite)
29+
{
30+
// Infinite wait - use alertable wait
31+
currentThread.SetWaitSleepJoinState();
32+
uint result;
33+
while (true)
34+
{
35+
result = Interop.Kernel32.SleepEx(Timeout.UnsignedInfinite, true);
36+
if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
37+
{
38+
break;
39+
}
40+
CheckForPendingInterrupt();
41+
}
42+
43+
currentThread.ClearWaitSleepJoinState();
44+
}
45+
else
46+
{
47+
// Timed wait - use alertable wait
48+
currentThread.SetWaitSleepJoinState();
49+
long startTime = Environment.TickCount64;
50+
while (true)
51+
{
52+
uint result = Interop.Kernel32.SleepEx((uint)millisecondsTimeout, true);
53+
if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
54+
{
55+
break;
56+
}
57+
// Check if this was our interrupt APC
58+
CheckForPendingInterrupt();
59+
// Handle APC completion by adjusting timeout and retrying
60+
long currentTime = Environment.TickCount64;
61+
long elapsed = currentTime - startTime;
62+
if (elapsed >= millisecondsTimeout)
63+
{
64+
break;
65+
}
66+
millisecondsTimeout -= (int)elapsed;
67+
startTime = currentTime;
68+
}
69+
70+
currentThread.ClearWaitSleepJoinState();
71+
}
2672
}
27-
#endif
2873

2974
internal static int GetCurrentProcessorNumber()
3075
{

0 commit comments

Comments
 (0)