From: Sean Gillespie Date: Fri, 14 Apr 2017 16:51:10 +0000 (-0700) Subject: [Local GC] Move operations on CLREventStatic to the EE interface (dotnet/coreclr... X-Git-Tag: submit/tizen/20210909.063632~11030^2~7260 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7ad5804999874c4be1990b12251ce02410c37b1a;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [Local GC] Move operations on CLREventStatic to the EE interface (dotnet/coreclr#10813) * [Local GC] Move operations on CLREventStatic to the EE and add their functionality to the interface * Fix a missed case * Split GetWaitForGCEvent into two smaller interface methods to avoid exposing the event itself on the interface * Initial implementation for Unix * Complete unix implementation * Make it work on Windows * Remove redudant methods from GCToEEInterface * Fix the Linux build * First part of code review feedback: make GCEvent dispatch statically (Windows) * Second part of code review feedback: make GCEvent dispatch statically (Unix) * Standardize implementation across Windows/Unix (apparently MSVC is more lenient about constructor names than clang) * Address code review feedback: Add Create*Event methods back onto GCEvent and remove them from GCToOSInterface * Address code review feedback: remove a dead define * Remove a bad comment, remove an unnecessary friend class, fix some formatting issues * Fix an issue when initializing a GCEvent on Linux (should not be allocating an Impl in the constructor) * Fix the same issue on Windows (less bad, just leaks memory instead of asserting) Commit migrated from https://github.com/dotnet/coreclr/commit/13812bfd0f6eda442c944f416e8c871b18d99e3f --- diff --git a/src/coreclr/src/gc/env/gcenv.os.h b/src/coreclr/src/gc/env/gcenv.os.h index 6a126f2..6ea35e3 100644 --- a/src/coreclr/src/gc/env/gcenv.os.h +++ b/src/coreclr/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/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 1aba30b..6d53c89 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/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/coreclr/src/gc/gcee.cpp b/src/coreclr/src/gc/gcee.cpp index 889f940..0404058 100644 --- a/src/coreclr/src/gc/gcee.cpp +++ b/src/coreclr/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/coreclr/src/gc/gcimpl.h b/src/coreclr/src/gc/gcimpl.h index 2a51d47..8ac16c5 100644 --- a/src/coreclr/src/gc/gcimpl.h +++ b/src/coreclr/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/coreclr/src/gc/gcinterface.h b/src/coreclr/src/gc/gcinterface.h index cac2ba7..4ebb5e1 100644 --- a/src/coreclr/src/gc/gcinterface.h +++ b/src/coreclr/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/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 108045c..a2ec64b 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/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/coreclr/src/gc/unix/CMakeLists.txt b/src/coreclr/src/gc/unix/CMakeLists.txt index 3e1aa5a..1025810 100644 --- a/src/coreclr/src/gc/unix/CMakeLists.txt +++ b/src/coreclr/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/coreclr/src/gc/unix/config.h.in b/src/coreclr/src/gc/unix/config.h.in index 7578c74..21980a7 100644 --- a/src/coreclr/src/gc/unix/config.h.in +++ b/src/coreclr/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/coreclr/src/gc/unix/configure.cmake b/src/coreclr/src/gc/unix/configure.cmake index 6e1e8fe..5f2bdbd 100644 --- a/src/coreclr/src/gc/unix/configure.cmake +++ b/src/coreclr/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 + #include + 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/coreclr/src/gc/unix/events.cpp b/src/coreclr/src/gc/unix/events.cpp new file mode 100644 index 0000000..f51eae8 --- /dev/null +++ b/src/coreclr/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 +#include +#include +#include +#include +#include +#include +#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 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 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/coreclr/src/gc/unix/gcenv.unix.cpp b/src/coreclr/src/gc/unix/gcenv.unix.cpp index cad83a3..5fc63f4 100644 --- a/src/coreclr/src/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/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 #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 #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_yield #include #include // 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/coreclr/src/gc/unix/globals.h b/src/coreclr/src/gc/unix/globals.h new file mode 100644 index 0000000..bc3dc49 --- /dev/null +++ b/src/coreclr/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 +#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/coreclr/src/gc/windows/gcenv.windows.cpp b/src/coreclr/src/gc/windows/gcenv.windows.cpp index 30232bf..0f3fd71 100644 --- a/src/coreclr/src/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/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 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 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/coreclr/src/vm/gcenv.os.cpp b/src/coreclr/src/vm/gcenv.os.cpp index 6c08558..d519c3c 100644 --- a/src/coreclr/src/vm/gcenv.os.cpp +++ b/src/coreclr/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 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 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 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 event = new (nothrow) GCEvent::Impl(); + if (!event) + { + return false; + } + + event->CreateOSManualEvent(initialState); + m_impl = event.Extract(); + return IsValid(); +} + diff --git a/src/coreclr/src/vm/threadsuspend.cpp b/src/coreclr/src/vm/threadsuspend.cpp index ab1f2bbf..6549594 100644 --- a/src/coreclr/src/vm/threadsuspend.cpp +++ b/src/coreclr/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 -