Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
Original file line number Diff line number Diff line change
Expand Up @@ -1636,7 +1636,12 @@ ep_rt_thread_set_activity_id (
}

#undef EP_YIELD_WHILE
#define EP_YIELD_WHILE(condition) {}//YIELD_WHILE(condition)
#define EP_YIELD_WHILE(condition) do { \
uint32_t switch_count = 0; \
while (condition) { \
minipal_switch_to_thread (++switch_count); \
} \
} while (0)

/*
* Volatile.
Expand Down
29 changes: 2 additions & 27 deletions src/coreclr/vm/hosting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,33 +231,8 @@ BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount)
return TRUE;
}

// In deciding when to insert sleeps, we wait until we have been spinning
// for a long time and then always sleep. The former is to let short perf-critical
// __SwitchToThread loops avoid context switches. The latter is to ensure
// that if many threads are spinning waiting for a lower-priority thread
// to run that they will eventually all be asleep at the same time.
//
// The specific values are derived from the NDP 2.0 SP1 fix: it waits for
// 8 million cycles of __SwitchToThread calls where each takes ~300-500,
// which means we should wait in the neighborhood of 25000 calls.
//
// As of early 2011, ARM CPUs are much slower, so we need a lower threshold.
// The following two values appear to yield roughly equivalent spin times
// on their respective platforms.
//
#ifdef TARGET_ARM
#define SLEEP_START_THRESHOLD (5 * 1024)
#else
#define SLEEP_START_THRESHOLD (32 * 1024)
#endif

_ASSERTE(CALLER_LIMITS_SPINNING < SLEEP_START_THRESHOLD);
if (dwSwitchCount >= SLEEP_START_THRESHOLD)
{
minipal_sleep(1);
}

return SwitchToThread();
_ASSERTE(CALLER_LIMITS_SPINNING == 0);
return minipal_switch_to_thread(dwSwitchCount);
}

// Locking routines supplied by the EE to the other DLLs of the CLR. In a _DEBUG
Expand Down
24 changes: 24 additions & 0 deletions src/native/minipal/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,30 @@ void minipal_sleep(uint32_t milliseconds)
#endif
}

bool minipal_switch_to_thread(uint32_t switchCount)
{
// Short yield loops avoid sleeps; prolonged contention must eventually
// sleep so that a lower-priority thread can make progress. These thresholds
// correspond to roughly the same spinning time on ARM and other CPUs.
#if defined(HOST_ARM)
const uint32_t sleepStartThreshold = 5 * 1024;
#else
const uint32_t sleepStartThreshold = 32 * 1024;
#endif
if (switchCount >= sleepStartThreshold)
{
minipal_sleep(1);
}

#if HOST_WINDOWS
return SwitchToThread() != 0;
#elif defined(TARGET_WASM) && !defined(FEATURE_MULTITHREADING)
return false;
#else
return sched_yield() == 0;
#endif
}

void minipal_microdelay(uint32_t usecs, uint32_t* usecsSinceYield)
{
#if HOST_WINDOWS
Expand Down
5 changes: 5 additions & 0 deletions src/native/minipal/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#ifndef HAVE_MINIPAL_TIME_H
#define HAVE_MINIPAL_TIME_H

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
Expand All @@ -25,6 +26,10 @@ extern "C"
// of the thread's time slice.
void minipal_sleep(uint32_t milliseconds);

// Yield to another thread, periodically sleeping after repeated yields.
// Returns whether the OS yield succeeded.
bool minipal_switch_to_thread(uint32_t switchCount);

// Delays execution of current thread by `usecs` microseconds.
// The delay is best-effort and may take longer than desired.
// Some delays, depending on OS and duration, could be implemented via busy waiting.
Expand Down
Loading