1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
18 #include "gcenv.structs.h"
19 #include "gcenv.base.h"
23 #if HAVE_MACH_ABSOLUTE_TIME
24 mach_timebase_info_data_t g_TimebaseInfo;
25 #endif // MACH_ABSOLUTE_TIME
30 #if HAVE_PTHREAD_CONDATTR_SETCLOCK
31 void TimeSpecAdd(timespec* time, uint32_t milliseconds)
33 uint64_t nsec = time->tv_nsec + (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds;
34 if (nsec >= tccSecondsToNanoSeconds)
36 time->tv_sec += nsec / tccSecondsToNanoSeconds;
37 nsec %= tccSecondsToNanoSeconds;
42 #endif // HAVE_PTHREAD_CONDATTR_SETCLOCK
44 #if HAVE_MACH_ABSOLUTE_TIME
45 // Convert nanoseconds to the timespec structure
47 // nanoseconds - time in nanoseconds to convert
48 // t - the target timespec structure
49 void NanosecondsToTimeSpec(uint64_t nanoseconds, timespec* t)
51 t->tv_sec = nanoseconds / tccSecondsToNanoSeconds;
52 t->tv_nsec = nanoseconds % tccSecondsToNanoSeconds;
54 #endif // HAVE_PTHREAD_CONDATTR_SETCLOCK
56 } // anonymous namespace
60 pthread_cond_t m_condition;
61 pthread_mutex_t m_mutex;
68 Impl(bool manualReset, bool initialState)
69 : m_manualReset(manualReset),
70 m_state(initialState),
77 pthread_condattr_t attrs;
78 int st = pthread_condattr_init(&attrs);
81 assert(!"Failed to initialize UnixEvent condition attribute");
85 // TODO(segilles) implement this for CoreCLR
86 //PthreadCondAttrHolder attrsHolder(&attrs);
88 #if HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME
89 // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC
90 st = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
93 assert(!"Failed to set UnixEvent condition variable wait clock");
96 #endif // HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME
98 st = pthread_mutex_init(&m_mutex, NULL);
101 assert(!"Failed to initialize UnixEvent mutex");
105 st = pthread_cond_init(&m_condition, &attrs);
108 assert(!"Failed to initialize UnixEvent condition variable");
110 st = pthread_mutex_destroy(&m_mutex);
111 assert(st == 0 && "Failed to destroy UnixEvent mutex");
124 int st = pthread_mutex_destroy(&m_mutex);
125 assert(st == 0 && "Failed to destroy UnixEvent mutex");
127 st = pthread_cond_destroy(&m_condition);
128 assert(st == 0 && "Failed to destroy UnixEvent condition variable");
132 uint32_t Wait(uint32_t milliseconds, bool alertable)
134 UNREFERENCED_PARAMETER(alertable);
137 #if HAVE_MACH_ABSOLUTE_TIME
138 uint64_t endMachTime;
139 if (milliseconds != INFINITE)
141 uint64_t nanoseconds = (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds;
142 NanosecondsToTimeSpec(nanoseconds, &endTime);
143 endMachTime = mach_absolute_time() + nanoseconds * g_TimebaseInfo.denom / g_TimebaseInfo.numer;
145 #elif HAVE_PTHREAD_CONDATTR_SETCLOCK
146 if (milliseconds != INFINITE)
148 clock_gettime(CLOCK_MONOTONIC, &endTime);
149 TimeSpecAdd(&endTime, milliseconds);
152 #error Don't know how to perfom timed wait on this platform
157 pthread_mutex_lock(&m_mutex);
160 if (milliseconds == INFINITE)
162 st = pthread_cond_wait(&m_condition, &m_mutex);
166 #if HAVE_MACH_ABSOLUTE_TIME
167 // Since OSX doesn't support CLOCK_MONOTONIC, we use relative variant of the
168 // timed wait and we need to handle spurious wakeups properly.
169 st = pthread_cond_timedwait_relative_np(&m_condition, &m_mutex, &endTime);
170 if ((st == 0) && !m_state)
172 uint64_t machTime = mach_absolute_time();
173 if (machTime < endMachTime)
175 // The wake up was spurious, recalculate the relative endTime
176 uint64_t remainingNanoseconds = (endMachTime - machTime) * g_TimebaseInfo.numer / g_TimebaseInfo.denom;
177 NanosecondsToTimeSpec(remainingNanoseconds, &endTime);
181 // Although the timed wait didn't report a timeout, time calculated from the
182 // mach time shows we have already reached the end time. It can happen if
183 // the wait was spuriously woken up right before the timeout.
187 #else // HAVE_MACH_ABSOLUTE_TIME
188 st = pthread_cond_timedwait(&m_condition, &m_mutex, &endTime);
189 #endif // HAVE_MACH_ABSOLUTE_TIME
190 // Verify that if the wait timed out, the event was not set
191 assert((st != ETIMEDOUT) || !m_state);
196 // wait failed or timed out
201 if ((st == 0) && !m_manualReset)
203 // Clear the state for auto-reset events so that only one waiter gets released
207 pthread_mutex_unlock(&m_mutex);
213 waitStatus = WAIT_OBJECT_0;
215 else if (st == ETIMEDOUT)
217 waitStatus = WAIT_TIMEOUT;
221 waitStatus = WAIT_FAILED;
229 pthread_mutex_lock(&m_mutex);
231 pthread_mutex_unlock(&m_mutex);
233 // Unblock all threads waiting for the condition variable
234 pthread_cond_broadcast(&m_condition);
239 pthread_mutex_lock(&m_mutex);
241 pthread_mutex_unlock(&m_mutex);
250 void GCEvent::CloseEvent()
252 assert(m_impl != nullptr);
253 m_impl->CloseEvent();
258 assert(m_impl != nullptr);
262 void GCEvent::Reset()
264 assert(m_impl != nullptr);
268 uint32_t GCEvent::Wait(uint32_t timeout, bool alertable)
270 assert(m_impl != nullptr);
271 return m_impl->Wait(timeout, alertable);
274 bool GCEvent::CreateAutoEventNoThrow(bool initialState)
276 // This implementation of GCEvent makes no distinction between
277 // host-aware and non-host-aware events (since there will be no host).
278 return CreateOSAutoEventNoThrow(initialState);
281 bool GCEvent::CreateManualEventNoThrow(bool initialState)
283 // This implementation of GCEvent makes no distinction between
284 // host-aware and non-host-aware events (since there will be no host).
285 return CreateOSManualEventNoThrow(initialState);
288 bool GCEvent::CreateOSAutoEventNoThrow(bool initialState)
290 assert(m_impl == nullptr);
291 std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl(false, initialState));
297 if (!event->Initialize())
302 m_impl = event.release();
306 bool GCEvent::CreateOSManualEventNoThrow(bool initialState)
308 assert(m_impl == nullptr);
309 std::unique_ptr<GCEvent::Impl> event(new (std::nothrow) GCEvent::Impl(true, initialState));
315 if (!event->Initialize())
320 m_impl = event.release();