summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gc/env/gcenv.os.h75
-rw-r--r--src/gc/gc.cpp38
-rw-r--r--src/gc/gcee.cpp11
-rw-r--r--src/gc/gcimpl.h7
-rw-r--r--src/gc/gcinterface.h8
-rw-r--r--src/gc/gcpriv.h28
-rw-r--r--src/gc/unix/CMakeLists.txt1
-rw-r--r--src/gc/unix/config.h.in4
-rw-r--r--src/gc/unix/configure.cmake17
-rw-r--r--src/gc/unix/events.cpp329
-rw-r--r--src/gc/unix/gcenv.unix.cpp25
-rw-r--r--src/gc/unix/globals.h30
-rw-r--r--src/gc/windows/gcenv.windows.cpp148
-rw-r--r--src/vm/gcenv.os.cpp211
-rw-r--r--src/vm/threadsuspend.cpp4
15 files changed, 874 insertions, 62 deletions
diff --git a/src/gc/env/gcenv.os.h b/src/gc/env/gcenv.os.h
index 6a126f29ed..6ea35e3142 100644
--- a/src/gc/env/gcenv.os.h
+++ b/src/gc/env/gcenv.os.h
@@ -47,6 +47,81 @@ struct GCThreadAffinity
int Processor;
};
+// An event is a synchronization object whose state can be set and reset
+// indicating that an event has occured. It is used pervasively throughout
+// the GC.
+class GCEvent {
+private:
+ class Impl;
+ Impl *m_impl;
+
+public:
+ // Constructs a new uninitialized event.
+ GCEvent();
+
+ // Destructs an event.
+ ~GCEvent();
+
+ // Closes the event. Attempting to use the event past calling CloseEvent
+ // is a logic error.
+ void CloseEvent();
+
+ // "Sets" the event, indicating that a particular event has occured. May
+ // wake up other threads waiting on this event. Depending on whether or
+ // not this event is an auto-reset event, the state of the event may
+ // or may not be automatically reset after Set is called.
+ void Set();
+
+ // Resets the event, resetting it back to a non-signalled state. Auto-reset
+ // events automatically reset once the event is set, while manual-reset
+ // events do not reset until Reset is called. It is a no-op to call Reset
+ // on an auto-reset event.
+ void Reset();
+
+ // Waits for some period of time for this event to be signalled. The
+ // period of time may be infinite (if the timeout argument is INFINITE) or
+ // it may be a specified period of time, in milliseconds.
+ // Returns:
+ // One of three values, depending on how why this thread was awoken:
+ // WAIT_OBJECT_0 - This event was signalled and woke up this thread.
+ // WAIT_TIMEOUT - The timeout interval expired without this event being signalled.
+ // WAIT_FAILED - The wait failed.
+ uint32_t Wait(uint32_t timeout, bool alertable);
+
+ // Determines whether or not this event is valid.
+ // Returns:
+ // true if this event is invalid (i.e. it has not yet been initialized or
+ // has already been closed), false otherwise
+ bool IsValid() const
+ {
+ return m_impl != nullptr;
+ }
+
+ // Initializes this event to be a host-aware manual reset event with the
+ // given initial state.
+ // Returns:
+ // true if the initialization succeeded, false if it did not
+ bool CreateManualEventNoThrow(bool initialState);
+
+ // Initializes this event to be a host-aware auto-resetting event with the
+ // given initial state.
+ // Returns:
+ // true if the initialization succeeded, false if it did not
+ bool CreateAutoEventNoThrow(bool initialState);
+
+ // Initializes this event to be a host-unaware manual reset event with the
+ // given initial state.
+ // Returns:
+ // true if the initialization succeeded, false if it did not
+ bool CreateOSManualEventNoThrow(bool initialState);
+
+ // Initializes this event to be a host-unaware auto-resetting event with the
+ // given initial state.
+ // Returns:
+ // true if the initialization succeeded, false if it did not
+ bool CreateOSAutoEventNoThrow(bool initialState);
+};
+
// GC thread function prototype
typedef void (*GCThreadFunction)(void* param);
diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp
index 1aba30bf4b..6d53c897fb 100644
--- a/src/gc/gc.cpp
+++ b/src/gc/gc.cpp
@@ -621,7 +621,7 @@ enum gc_join_flavor
#define first_thread_arrived 2
struct join_structure
{
- CLREvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
+ GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
VOLATILE(int32_t) join_lock;
VOLATILE(int32_t) r_join_lock;
VOLATILE(int32_t) join_restart;
@@ -1201,8 +1201,8 @@ class recursive_gc_sync
static VOLATILE(BOOL) gc_background_running; //initial state FALSE
static VOLATILE(int32_t) foreground_count; // initial state 0;
static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
- static CLREvent foreground_complete;//Auto Reset
- static CLREvent foreground_allowed;//Auto Reset
+ static GCEvent foreground_complete;//Auto Reset
+ static GCEvent foreground_allowed;//Auto Reset
public:
static void begin_background();
static void end_background();
@@ -1218,8 +1218,8 @@ VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial stat
VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
-CLREvent recursive_gc_sync::foreground_complete;//Auto Reset
-CLREvent recursive_gc_sync::foreground_allowed;//Manual Reset
+GCEvent recursive_gc_sync::foreground_complete;//Auto Reset
+GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset
BOOL recursive_gc_sync::init ()
{
@@ -2308,7 +2308,7 @@ sorted_table* gc_heap::seg_table;
#endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
#ifdef MULTIPLE_HEAPS
-CLREvent gc_heap::ee_suspend_event;
+GCEvent gc_heap::ee_suspend_event;
size_t gc_heap::min_balance_threshold = 0;
#endif //MULTIPLE_HEAPS
@@ -2316,7 +2316,7 @@ VOLATILE(BOOL) gc_heap::gc_started;
#ifdef MULTIPLE_HEAPS
-CLREvent gc_heap::gc_start_event;
+GCEvent gc_heap::gc_start_event;
bool gc_heap::gc_thread_no_affinitize_p = false;
@@ -2385,13 +2385,13 @@ uint64_t gc_heap::total_physical_mem;
uint64_t gc_heap::entry_available_physical_mem;
#ifdef BACKGROUND_GC
-CLREvent gc_heap::bgc_start_event;
+GCEvent gc_heap::bgc_start_event;
gc_mechanisms gc_heap::saved_bgc_settings;
-CLREvent gc_heap::background_gc_done_event;
+GCEvent gc_heap::background_gc_done_event;
-CLREvent gc_heap::ee_proceed_event;
+GCEvent gc_heap::ee_proceed_event;
bool gc_heap::gc_can_use_concurrent = false;
@@ -2403,7 +2403,7 @@ BOOL gc_heap::dont_restart_ee_p = FALSE;
BOOL gc_heap::keep_bgc_threads_p = FALSE;
-CLREvent gc_heap::bgc_threads_sync_event;
+GCEvent gc_heap::bgc_threads_sync_event;
BOOL gc_heap::do_ephemeral_gc_p = FALSE;
@@ -2589,7 +2589,7 @@ BOOL gc_heap::bgc_thread_running;
CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
-CLREvent gc_heap::gc_lh_block_event;
+GCEvent gc_heap::gc_lh_block_event;
#endif //BACKGROUND_GC
@@ -2685,9 +2685,9 @@ int gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
#endif //FEATURE_LOH_COMPACTION
-CLREvent gc_heap::full_gc_approach_event;
+GCEvent gc_heap::full_gc_approach_event;
-CLREvent gc_heap::full_gc_end_event;
+GCEvent gc_heap::full_gc_end_event;
uint32_t gc_heap::fgn_maxgen_percent = 0;
@@ -10310,7 +10310,7 @@ gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
VOLATILE(int32_t) gc_heap::gc_done_event_lock;
VOLATILE(bool) gc_heap::gc_done_event_set;
-CLREvent gc_heap::gc_done_event;
+GCEvent gc_heap::gc_done_event;
#endif //!MULTIPLE_HEAPS
VOLATILE(bool) gc_heap::internal_gc_done;
@@ -11739,7 +11739,7 @@ void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
}
}
-wait_full_gc_status gc_heap::full_gc_wait (CLREvent *event, int time_out_ms)
+wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
{
if (fgn_maxgen_percent == 0)
{
@@ -32413,7 +32413,7 @@ void gc_heap::descr_generations (BOOL begin_gc_p)
VOLATILE(BOOL) GCHeap::GcInProgress = FALSE;
//GCTODO
//CMCSafeLock* GCHeap::fGcLock;
-CLREvent *GCHeap::WaitForGCEvent = NULL;
+GCEvent *GCHeap::WaitForGCEvent = NULL;
//GCTODO
#ifdef TRACE_GC
unsigned int GCHeap::GcDuration;
@@ -33687,7 +33687,7 @@ HRESULT GCHeap::Initialize ()
gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
#endif // BIT64
- WaitForGCEvent = new (nothrow) CLREvent;
+ WaitForGCEvent = new (nothrow) GCEvent;
if (!WaitForGCEvent)
{
@@ -33887,7 +33887,7 @@ bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
STATIC_CONTRACT_SO_TOLERANT;
// removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment
- // no longer calls CLREvent::Wait which eventually takes a lock.
+ // no longer calls GCEvent::Wait which eventually takes a lock.
uint8_t* object = (uint8_t*) vpObject;
#ifndef FEATURE_BASICFREEZE
diff --git a/src/gc/gcee.cpp b/src/gc/gcee.cpp
index 889f940973..0404058cde 100644
--- a/src/gc/gcee.cpp
+++ b/src/gc/gcee.cpp
@@ -428,9 +428,14 @@ void GCHeap::SetGCInProgress(bool fInProgress)
GcInProgress = fInProgress;
}
-CLREvent * GCHeap::GetWaitForGCEvent()
+void GCHeap::SetWaitForGCEvent()
{
- return WaitForGCEvent;
+ WaitForGCEvent->Set();
+}
+
+void GCHeap::ResetWaitForGCEvent()
+{
+ WaitForGCEvent->Reset();
}
void GCHeap::WaitUntilConcurrentGCComplete()
@@ -520,7 +525,7 @@ void gc_heap::fire_etw_pin_object_event (uint8_t* object, uint8_t** ppObject)
}
#endif // FEATURE_EVENT_TRACE
-uint32_t gc_heap::user_thread_wait (CLREvent *event, BOOL no_mode_change, int time_out_ms)
+uint32_t gc_heap::user_thread_wait (GCEvent *event, BOOL no_mode_change, int time_out_ms)
{
Thread* pCurThread = NULL;
bool mode = false;
diff --git a/src/gc/gcimpl.h b/src/gc/gcimpl.h
index 2a51d477b0..8ac16c5107 100644
--- a/src/gc/gcimpl.h
+++ b/src/gc/gcimpl.h
@@ -6,8 +6,6 @@
#ifndef GCIMPL_H_
#define GCIMPL_H_
-#define CLREvent CLREventStatic
-
#ifdef SERVER_GC
#define MULTIPLE_HEAPS 1
#endif // SERVER_GC
@@ -93,7 +91,8 @@ public:
bool RuntimeStructuresValid();
- CLREvent * GetWaitForGCEvent();
+ void SetWaitForGCEvent();
+ void ResetWaitForGCEvent();
HRESULT Initialize ();
@@ -242,7 +241,7 @@ public: // FIX
void TemporaryDisableConcurrentGC();
bool IsConcurrentGCEnabled();
- PER_HEAP_ISOLATED CLREvent *WaitForGCEvent; // used for syncing w/GC
+ PER_HEAP_ISOLATED GCEvent *WaitForGCEvent; // used for syncing w/GC
PER_HEAP_ISOLATED CFinalize* m_Finalize;
diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h
index cac2ba7114..4ebb5e15d7 100644
--- a/src/gc/gcinterface.h
+++ b/src/gc/gcinterface.h
@@ -687,9 +687,11 @@ public:
// background GC as the BGC threads also need to walk LOH.
virtual void PublishObject(uint8_t* obj) = 0;
- // Gets the event that suspended threads will use to wait for the
- // end of a GC.
- virtual CLREventStatic* GetWaitForGCEvent() = 0;
+ // Signals the WaitForGCEvent event, indicating that a GC has completed.
+ virtual void SetWaitForGCEvent() = 0;
+
+ // Resets the state of the WaitForGCEvent back to an unsignalled state.
+ virtual void ResetWaitForGCEvent() = 0;
/*
===========================================================================
diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h
index 108045cd37..a2ec64b614 100644
--- a/src/gc/gcpriv.h
+++ b/src/gc/gcpriv.h
@@ -197,8 +197,6 @@ void GCLogConfig (const char *fmt, ... );
//Please leave these definitions intact.
-#define CLREvent CLREventStatic
-
// hosted api
#ifdef memcpy
#undef memcpy
@@ -2766,7 +2764,7 @@ public:
BOOL dont_restart_ee_p;
PER_HEAP_ISOLATED
- CLREvent bgc_start_event;
+ GCEvent bgc_start_event;
#endif //BACKGROUND_GC
// The variables in this block are known to the DAC and must come first
@@ -2833,9 +2831,9 @@ public:
PER_HEAP
#ifndef MULTIPLE_HEAPS
- CLREvent gc_done_event;
+ GCEvent gc_done_event;
#else // MULTIPLE_HEAPS
- CLREvent gc_done_event;
+ GCEvent gc_done_event;
#endif // MULTIPLE_HEAPS
PER_HEAP
@@ -2890,10 +2888,10 @@ public:
// notification feature which is only enabled if concurrent
// GC is disabled.
PER_HEAP_ISOLATED
- CLREvent full_gc_approach_event;
+ GCEvent full_gc_approach_event;
PER_HEAP_ISOLATED
- CLREvent full_gc_end_event;
+ GCEvent full_gc_end_event;
// Full GC Notification percentages.
PER_HEAP_ISOLATED
@@ -2913,9 +2911,9 @@ public:
PER_HEAP
size_t fgn_last_alloc;
- static uint32_t user_thread_wait (CLREvent *event, BOOL no_mode_change, int time_out_ms=INFINITE);
+ static uint32_t user_thread_wait (GCEvent *event, BOOL no_mode_change, int time_out_ms=INFINITE);
- static wait_full_gc_status full_gc_wait (CLREvent *event, int time_out_ms);
+ static wait_full_gc_status full_gc_wait (GCEvent *event, int time_out_ms);
PER_HEAP
uint8_t* demotion_low;
@@ -2943,10 +2941,10 @@ public:
bool gc_thread_no_affinitize_p;
PER_HEAP_ISOLATED
- CLREvent gc_start_event;
+ GCEvent gc_start_event;
PER_HEAP_ISOLATED
- CLREvent ee_suspend_event;
+ GCEvent ee_suspend_event;
PER_HEAP
heap_segment* new_heap_segment;
@@ -3133,7 +3131,7 @@ protected:
// we need to create them on the thread that called
// SuspendEE which is heap 0.
PER_HEAP_ISOLATED
- CLREvent bgc_threads_sync_event;
+ GCEvent bgc_threads_sync_event;
PER_HEAP
Thread* bgc_thread;
@@ -3142,13 +3140,13 @@ protected:
CLRCriticalSection bgc_threads_timeout_cs;
PER_HEAP_ISOLATED
- CLREvent background_gc_done_event;
+ GCEvent background_gc_done_event;
PER_HEAP_ISOLATED
- CLREvent ee_proceed_event;
+ GCEvent ee_proceed_event;
PER_HEAP
- CLREvent gc_lh_block_event;
+ GCEvent gc_lh_block_event;
PER_HEAP_ISOLATED
bool gc_can_use_concurrent;
diff --git a/src/gc/unix/CMakeLists.txt b/src/gc/unix/CMakeLists.txt
index 3e1aa5ad19..10258108c6 100644
--- a/src/gc/unix/CMakeLists.txt
+++ b/src/gc/unix/CMakeLists.txt
@@ -6,6 +6,7 @@ include(configure.cmake)
set(GC_PAL_SOURCES
gcenv.unix.cpp
+ events.cpp
cgroup.cpp)
add_library(gc_unix STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH})
diff --git a/src/gc/unix/config.h.in b/src/gc/unix/config.h.in
index 7578c74c05..21980a7d08 100644
--- a/src/gc/unix/config.h.in
+++ b/src/gc/unix/config.h.in
@@ -10,5 +10,7 @@
#cmakedefine01 HAVE_PTHREAD_THREADID_NP
#cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP
#cmakedefine01 HAVE_SCHED_GETCPU
+#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK
+#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
-#endif // __CONFIG_H__ \ No newline at end of file
+#endif // __CONFIG_H__
diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake
index 6e1e8fe27d..5f2bdbd8b3 100644
--- a/src/gc/unix/configure.cmake
+++ b/src/gc/unix/configure.cmake
@@ -37,4 +37,19 @@ check_cxx_source_runs("
}
" HAVE_SCHED_GETCPU)
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) \ No newline at end of file
+check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK)
+
+check_cxx_source_runs("
+ #include <stdlib.h>
+ #include <mach/mach_time.h>
+ int main()
+ {
+ int ret;
+ mach_timebase_info_data_t timebaseInfo;
+ ret = mach_timebase_info(&timebaseInfo);
+ mach_absolute_time();
+ exit(ret);
+ }
+ " HAVE_MACH_ABSOLUTE_TIME)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/src/gc/unix/events.cpp b/src/gc/unix/events.cpp
new file mode 100644
index 0000000000..f51eae80c3
--- /dev/null
+++ b/src/gc/unix/events.cpp
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <cstdint>
+#include <cstddef>
+#include <cassert>
+#include <memory>
+#include <mutex>
+#include <pthread.h>
+#include <errno.h>
+#include "config.h"
+
+#ifndef __out_z
+#define __out_z
+#endif // __out_z
+
+#include "gcenv.structs.h"
+#include "gcenv.base.h"
+#include "gcenv.os.h"
+#include "globals.h"
+
+#if HAVE_MACH_ABSOLUTE_TIME
+mach_timebase_info_data_t g_TimebaseInfo;
+#endif // MACH_ABSOLUTE_TIME
+
+namespace
+{
+
+#if HAVE_PTHREAD_CONDATTR_SETCLOCK
+void TimeSpecAdd(timespec* time, uint32_t milliseconds)
+{
+ uint64_t nsec = time->tv_nsec + (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds;
+ if (nsec >= tccSecondsToNanoSeconds)
+ {
+ time->tv_sec += nsec / tccSecondsToNanoSeconds;
+ nsec %= tccSecondsToNanoSeconds;
+ }
+
+ time->tv_nsec = nsec;
+}
+#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+#if HAVE_MACH_ABSOLUTE_TIME
+// Convert nanoseconds to the timespec structure
+// Parameters:
+// nanoseconds - time in nanoseconds to convert
+// t - the target timespec structure
+void NanosecondsToTimeSpec(uint64_t nanoseconds, timespec* t)
+{
+ t->tv_sec = nanoseconds / tccSecondsToNanoSeconds;
+ t->tv_nsec = nanoseconds % tccSecondsToNanoSeconds;
+}
+#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+} // anonymous namespace
+
+class GCEvent::Impl
+{
+ pthread_cond_t m_condition;
+ pthread_mutex_t m_mutex;
+ bool m_manualReset;
+ bool m_state;
+ bool m_isValid;
+
+public:
+
+ Impl(bool manualReset, bool initialState)
+ : m_manualReset(manualReset),
+ m_state(initialState),
+ m_isValid(false)
+ {
+ }
+
+ bool Initialize()
+ {
+ pthread_condattr_t attrs;
+ int st = pthread_condattr_init(&attrs);
+ if (st != 0)
+ {
+ assert(!"Failed to initialize UnixEvent condition attribute");
+ return false;
+ }
+
+ // TODO(segilles) implement this for CoreCLR
+ //PthreadCondAttrHolder attrsHolder(&attrs);
+
+#if HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME
+ // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC
+ st = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ if (st != 0)
+ {
+ assert(!"Failed to set UnixEvent condition variable wait clock");
+ return false;
+ }
+#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME
+
+ st = pthread_mutex_init(&m_mutex, NULL);
+ if (st != 0)
+ {
+ assert(!"Failed to initialize UnixEvent mutex");
+ return false;
+ }
+
+ st = pthread_cond_init(&m_condition, &attrs);
+ if (st != 0)
+ {
+ assert(!"Failed to initialize UnixEvent condition variable");
+
+ st = pthread_mutex_destroy(&m_mutex);
+ assert(st == 0 && "Failed to destroy UnixEvent mutex");
+ return false;
+ }
+
+ m_isValid = true;
+
+ return true;
+ }
+
+ void CloseEvent()
+ {
+ if (m_isValid)
+ {
+ int st = pthread_mutex_destroy(&m_mutex);
+ assert(st == 0 && "Failed to destroy UnixEvent mutex");
+
+ st = pthread_cond_destroy(&m_condition);
+ assert(st == 0 && "Failed to destroy UnixEvent condition variable");
+ }
+ }
+
+ uint32_t Wait(uint32_t milliseconds, bool alertable)
+ {
+ UNREFERENCED_PARAMETER(alertable);
+
+ timespec endTime;
+#if HAVE_MACH_ABSOLUTE_TIME
+ uint64_t endMachTime;
+ if (milliseconds != INFINITE)
+ {
+ uint64_t nanoseconds = (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds;
+ NanosecondsToTimeSpec(nanoseconds, &endTime);
+ endMachTime = mach_absolute_time() + nanoseconds * g_TimebaseInfo.denom / g_TimebaseInfo.numer;
+ }
+#elif HAVE_PTHREAD_CONDATTR_SETCLOCK
+ if (milliseconds != INFINITE)
+ {
+ clock_gettime(CLOCK_MONOTONIC, &endTime);
+ TimeSpecAdd(&endTime, milliseconds);
+ }
+#else
+#error Don't know how to perfom timed wait on this platform
+#endif
+
+ int st = 0;
+
+ pthread_mutex_lock(&m_mutex);
+ while (!m_state)
+ {
+ if (milliseconds == INFINITE)
+ {
+ st = pthread_cond_wait(&m_condition, &m_mutex);
+ }
+ else
+ {
+#if HAVE_MACH_ABSOLUTE_TIME
+ // Since OSX doesn't support CLOCK_MONOTONIC, we use relative variant of the
+ // timed wait and we need to handle spurious wakeups properly.
+ st = pthread_cond_timedwait_relative_np(&m_condition, &m_mutex, &endTime);
+ if ((st == 0) && !m_state)
+ {
+ uint64_t machTime = mach_absolute_time();
+ if (machTime < endMachTime)
+ {
+ // The wake up was spurious, recalculate the relative endTime
+ uint64_t remainingNanoseconds = (endMachTime - machTime) * g_TimebaseInfo.numer / g_TimebaseInfo.denom;
+ NanosecondsToTimeSpec(remainingNanoseconds, &endTime);
+ }
+ else
+ {
+ // Although the timed wait didn't report a timeout, time calculated from the
+ // mach time shows we have already reached the end time. It can happen if
+ // the wait was spuriously woken up right before the timeout.
+ st = ETIMEDOUT;
+ }
+ }
+#else // HAVE_MACH_ABSOLUTE_TIME
+ st = pthread_cond_timedwait(&m_condition, &m_mutex, &endTime);
+#endif // HAVE_MACH_ABSOLUTE_TIME
+ // Verify that if the wait timed out, the event was not set
+ assert((st != ETIMEDOUT) || !m_state);
+ }
+
+ if (st != 0)
+ {
+ // wait failed or timed out
+ break;
+ }
+ }
+
+ if ((st == 0) && !m_manualReset)
+ {
+ // Clear the state for auto-reset events so that only one waiter gets released
+ m_state = false;
+ }
+
+ pthread_mutex_unlock(&m_mutex);
+
+ uint32_t waitStatus;
+
+ if (st == 0)
+ {
+ waitStatus = WAIT_OBJECT_0;
+ }
+ else if (st == ETIMEDOUT)
+ {
+ waitStatus = WAIT_TIMEOUT;
+ }
+ else
+ {
+ waitStatus = WAIT_FAILED;
+ }
+
+ return waitStatus;
+ }
+
+ void Set()
+ {
+ pthread_mutex_lock(&m_mutex);
+ m_state = true;
+ pthread_mutex_unlock(&m_mutex);
+
+ // Unblock all threads waiting for the condition variable
+ pthread_cond_broadcast(&m_condition);
+ }
+
+ void Reset()
+ {
+ pthread_mutex_lock(&m_mutex);
+ m_state = false;
+ pthread_mutex_unlock(&m_mutex);
+ }
+};
+
+GCEvent::GCEvent()
+ : m_impl(nullptr)
+{
+}
+
+GCEvent::~GCEvent()
+{
+ delete m_impl;
+ m_impl = nullptr;
+}
+
+void GCEvent::CloseEvent()
+{
+ assert(m_impl != nullptr);
+ m_impl->CloseEvent();
+}
+
+void GCEvent::Set()
+{
+ assert(m_impl != nullptr);
+ m_impl->Set();
+}
+
+void GCEvent::Reset()
+{
+ assert(m_impl != nullptr);
+ m_impl->Reset();
+}
+
+uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
+{
+ assert(m_impl != nullptr);
+ return m_impl->Wait(timeout, alertable);
+}
+
+bool GCEvent::CreateAutoEventNoThrow(bool initialState)
+{
+ // This implementation of GCEvent makes no distinction between
+ // host-aware and non-host-aware events (since there will be no host).
+ return CreateOSAutoEventNoThrow(initialState);
+}
+
+bool GCEvent::CreateManualEventNoThrow(bool initialState)
+{
+ // This implementation of GCEvent makes no distinction between
+ // host-aware and non-host-aware events (since there will be no host).
+ return CreateOSManualEventNoThrow(initialState);
+}
+
+bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
+{
+ assert(m_impl == nullptr);
+ std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl(false, initialState));
+ if (!event)
+ {
+ return false;
+ }
+
+ if (!event->Initialize())
+ {
+ return false;
+ }
+
+ m_impl = event.release();
+ return true;
+}
+
+bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
+{
+ assert(m_impl == nullptr);
+ std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl(true, initialState));
+ if (!event)
+ {
+ return false;
+ }
+
+ if (!event->Initialize())
+ {
+ return false;
+ }
+
+ m_impl = event.release();
+ return true;
+}
+
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index cad83a342b..5fc63f47d3 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -36,13 +36,13 @@ static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes");
#error "A GC-private implementation of GCToOSInterface should only be used with FEATURE_STANDALONE_GC"
#endif // FEATURE_STANDALONE_GC
-#ifdef HAVE_SYS_TIME_H
+#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#error "sys/time.h required by GC PAL for the time being"
#endif // HAVE_SYS_TIME_
-#ifdef HAVE_SYS_MMAN_H
+#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#else
#error "sys/mman.h required by GC PAL"
@@ -56,18 +56,7 @@ static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes");
#include <sched.h> // sched_yield
#include <errno.h>
#include <unistd.h> // sysconf
-
-// The number of milliseconds in a second.
-static const int tccSecondsToMilliSeconds = 1000;
-
-// The number of microseconds in a second.
-static const int tccSecondsToMicroSeconds = 1000000;
-
-// The number of microseconds in a millisecond.
-static const int tccMilliSecondsToMicroSeconds = 1000;
-
-// The number of nanoseconds in a millisecond.
-static const int tccMilliSecondsToNanoSeconds = 1000000;
+#include "globals.h"
// The cachced number of logical CPUs observed.
static uint32_t g_logicalCpuCount = 0;
@@ -117,6 +106,14 @@ bool GCToOSInterface::Initialize()
return false;
}
+#if HAVE_MACH_ABSOLUTE_TIME
+ kern_return_t machRet;
+ if ((machRet = mach_timebase_info(&g_TimebaseInfo)) != KERN_SUCCESS)
+ {
+ return false;
+ }
+#endif // HAVE_MACH_ABSOLUTE_TIME
+
return true;
}
diff --git a/src/gc/unix/globals.h b/src/gc/unix/globals.h
new file mode 100644
index 0000000000..bc3dc49918
--- /dev/null
+++ b/src/gc/unix/globals.h
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#if HAVE_MACH_ABSOLUTE_TIME
+#include <mach/mach_time.h>
+#endif // HAVE_MACH_ABSOLUTE_TIME
+
+const int tccSecondsToMilliSeconds = 1000;
+
+// The number of microseconds in a second.
+const int tccSecondsToMicroSeconds = 1000000;
+
+// The number of nanoseconds in a second.
+const int tccSecondsToNanoSeconds = 1000000000;
+
+// The number of microseconds in a millisecond.
+const int tccMilliSecondsToMicroSeconds = 1000;
+
+// The number of nanoseconds in a millisecond.
+const int tccMilliSecondsToNanoSeconds = 1000000;
+
+#if HAVE_MACH_ABSOLUTE_TIME
+extern mach_timebase_info_data_t g_TimebaseInfo;
+#endif // HAVE_MACH_ABSOLUTE_TIME
+
+#endif // __GLOBALS_H__
diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index 30232bfb09..0f3fd710e3 100644
--- a/src/gc/windows/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp
@@ -626,3 +626,151 @@ void CLRCriticalSection::Leave()
{
::LeaveCriticalSection(&m_cs);
}
+
+// WindowsEvent is an implementation of GCEvent that forwards
+// directly to Win32 APIs.
+class GCEvent::Impl
+{
+private:
+ HANDLE m_hEvent;
+
+public:
+ Impl() : m_hEvent(INVALID_HANDLE_VALUE) {}
+
+ bool IsValid() const
+ {
+ return m_hEvent != INVALID_HANDLE_VALUE;
+ }
+
+ void Set()
+ {
+ assert(IsValid());
+ BOOL result = SetEvent(m_hEvent);
+ assert(result && "SetEvent failed");
+ }
+
+ void Reset()
+ {
+ assert(IsValid());
+ BOOL result = ResetEvent(m_hEvent);
+ assert(result && "ResetEvent failed");
+ }
+
+ uint32_t Wait(uint32_t timeout, bool alertable)
+ {
+ UNREFERENCED_PARAMETER(alertable);
+ assert(IsValid());
+
+ return WaitForSingleObject(m_hEvent, timeout);
+ }
+
+ void CloseEvent()
+ {
+ assert(IsValid());
+ BOOL result = CloseHandle(m_hEvent);
+ assert(result && "CloseHandle failed");
+ m_hEvent = INVALID_HANDLE_VALUE;
+ }
+
+ bool CreateAutoEvent(bool initialState)
+ {
+ m_hEvent = CreateEvent(nullptr, false, initialState, nullptr);
+ return IsValid();
+ }
+
+ bool CreateManualEvent(bool initialState)
+ {
+ m_hEvent = CreateEvent(nullptr, true, initialState, nullptr);
+ return IsValid();
+ }
+};
+
+GCEvent::GCEvent()
+ : m_impl(nullptr)
+{
+}
+
+GCEvent::~GCEvent()
+{
+ delete m_impl;
+ m_impl = nullptr;
+}
+
+void GCEvent::CloseEvent()
+{
+ assert(m_impl != nullptr);
+ m_impl->CloseEvent();
+}
+
+void GCEvent::Set()
+{
+ assert(m_impl != nullptr);
+ m_impl->Set();
+}
+
+void GCEvent::Reset()
+{
+ assert(m_impl != nullptr);
+ m_impl->Reset();
+}
+
+uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
+{
+ assert(m_impl != nullptr);
+ return m_impl->Wait(timeout, alertable);
+}
+
+bool GCEvent::CreateAutoEventNoThrow(bool initialState)
+{
+ // [DESKTOP TODO] The difference between events and OS events is
+ // whether or not the hosting API is made aware of them. When (if)
+ // we implement hosting support for Local GC, we will need to be
+ // aware of the host here.
+ return CreateOSAutoEventNoThrow(initialState);
+}
+
+bool GCEvent::CreateManualEventNoThrow(bool initialState)
+{
+ // [DESKTOP TODO] The difference between events and OS events is
+ // whether or not the hosting API is made aware of them. When (if)
+ // we implement hosting support for Local GC, we will need to be
+ // aware of the host here.
+ return CreateOSManualEventNoThrow(initialState);
+}
+
+bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
+{
+ assert(m_impl == nullptr);
+ std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl());
+ if (!event)
+ {
+ return false;
+ }
+
+ if (!event->CreateAutoEvent(initialState))
+ {
+ return false;
+ }
+
+ m_impl = event.release();
+ return true;
+}
+
+bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
+{
+ assert(m_impl == nullptr);
+ std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl());
+ if (!event)
+ {
+ return false;
+ }
+
+ if (!event->CreateManualEvent(initialState))
+ {
+ return false;
+ }
+
+ m_impl = event.release();
+ return true;
+}
+
diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp
index 6c08558e03..d519c3cda4 100644
--- a/src/vm/gcenv.os.cpp
+++ b/src/vm/gcenv.os.cpp
@@ -700,3 +700,214 @@ void CLRCriticalSection::Leave()
WRAPPER_NO_CONTRACT;
UnsafeLeaveCriticalSection(&m_cs);
}
+
+// An implementatino of GCEvent that delegates to
+// a CLREvent, which in turn delegates to the PAL. This event
+// is also host-aware.
+class GCEvent::Impl
+{
+private:
+ CLREvent m_event;
+
+public:
+ Impl() = default;
+
+ bool IsValid()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ return !!m_event.IsValid();
+ }
+
+ void CloseEvent()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_event.IsValid());
+ m_event.CloseEvent();
+ }
+
+ void Set()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_event.IsValid());
+ m_event.Set();
+ }
+
+ void Reset()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_event.IsValid());
+ m_event.Reset();
+ }
+
+ uint32_t Wait(uint32_t timeout, bool alertable)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_event.IsValid());
+ return m_event.Wait(timeout, alertable);
+ }
+
+ bool CreateAutoEvent(bool initialState)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ return !!m_event.CreateAutoEventNoThrow(initialState);
+ }
+
+ bool CreateManualEvent(bool initialState)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ return !!m_event.CreateManualEventNoThrow(initialState);
+ }
+
+ bool CreateOSAutoEvent(bool initialState)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ return !!m_event.CreateOSAutoEventNoThrow(initialState);
+ }
+
+ bool CreateOSManualEvent(bool initialState)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ return !!m_event.CreateOSManualEventNoThrow(initialState);
+ }
+};
+
+GCEvent::GCEvent()
+ : m_impl(nullptr)
+{
+}
+
+GCEvent::~GCEvent()
+{
+ delete m_impl;
+ m_impl = nullptr;
+}
+
+void GCEvent::CloseEvent()
+{
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_impl != nullptr);
+ m_impl->CloseEvent();
+}
+
+void GCEvent::Set()
+{
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_impl != nullptr);
+ m_impl->Set();
+}
+
+void GCEvent::Reset()
+{
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_impl != nullptr);
+ m_impl->Reset();
+}
+
+uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
+{
+ WRAPPER_NO_CONTRACT;
+
+ assert(m_impl != nullptr);
+ return m_impl->Wait(timeout, alertable);
+}
+
+bool GCEvent::CreateManualEventNoThrow(bool initialState)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ assert(m_impl == nullptr);
+ NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
+ if (!event)
+ {
+ return false;
+ }
+
+ event->CreateManualEvent(initialState);
+ m_impl = event.Extract();
+ return true;
+}
+
+bool GCEvent::CreateAutoEventNoThrow(bool initialState)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ assert(m_impl == nullptr);
+ NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
+ if (!event)
+ {
+ return false;
+ }
+
+ event->CreateAutoEvent(initialState);
+ m_impl = event.Extract();
+ return IsValid();
+}
+
+bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ assert(m_impl == nullptr);
+ NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
+ if (!event)
+ {
+ return false;
+ }
+
+ event->CreateOSAutoEvent(initialState);
+ m_impl = event.Extract();
+ return IsValid();
+}
+
+bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ assert(m_impl == nullptr);
+ NewHolder<GCEvent::Impl> event = new (nothrow) GCEvent::Impl();
+ if (!event)
+ {
+ return false;
+ }
+
+ event->CreateOSManualEvent(initialState);
+ m_impl = event.Extract();
+ return IsValid();
+}
+
diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp
index ab1f2bbff5..65495940ed 100644
--- a/src/vm/threadsuspend.cpp
+++ b/src/vm/threadsuspend.cpp
@@ -7181,7 +7181,7 @@ void ThreadSuspend::RestartEE(BOOL bFinishedGC, BOOL SuspendSucceded)
//
// Any threads that are waiting in WaitUntilGCComplete will continue now.
//
- GCHeapUtilities::GetGCHeap()->GetWaitForGCEvent()->Set();
+ GCHeapUtilities::GetGCHeap()->SetWaitForGCEvent();
_ASSERTE(IsGCSpecialThread() || ThreadStore::HoldingThreadStore());
ResumeRuntime(bFinishedGC, SuspendSucceded);
@@ -7307,7 +7307,7 @@ retry_for_debugger:
//
// First, we reset the event that we're about to tell other threads to wait for.
//
- GCHeapUtilities::GetGCHeap()->GetWaitForGCEvent()->Reset();
+ GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent();
//
// Remember that we're the one doing the GC. Actually, maybe we're not doing a GC -