blob: b5d86595b7bdaca512b2c804ab5dfe822c389da7 [file] [log] [blame]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h" // NOLINT
#if !defined(DART_USE_ABSL) && \
(defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_FUCHSIA) || \
defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_ANDROID))
#include "platform/synchronization.h"
#include <errno.h> // NOLINT
#include <stdio.h>
#include <sys/time.h> // NOLINT
#include "platform/utils.h"
namespace dart {
Mutex::Mutex() {
pthread_mutexattr_t attr;
int result = pthread_mutexattr_init(&attr);
VALIDATE_PTHREAD_RESULT(result);
#if defined(DEBUG)
result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
VALIDATE_PTHREAD_RESULT(result);
#endif // defined(DEBUG)
result = pthread_mutex_init(&mutex_, &attr);
// Verify that creating a pthread_mutex succeeded.
VALIDATE_PTHREAD_RESULT(result);
result = pthread_mutexattr_destroy(&attr);
VALIDATE_PTHREAD_RESULT(result);
}
Mutex::~Mutex() {
int result = pthread_mutex_destroy(&mutex_);
// Verify that the pthread_mutex was destroyed.
VALIDATE_PTHREAD_RESULT(result);
}
void Mutex::Lock() {
DEBUG_ASSERT(!DisallowMutexLockingScope::is_active());
int result = pthread_mutex_lock(&mutex_);
// Specifically check for dead lock to help debugging.
ASSERT(result != EDEADLK);
ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
owner_.Acquire();
}
bool Mutex::TryLock() {
DEBUG_ASSERT(!DisallowMutexLockingScope::is_active());
int result = pthread_mutex_trylock(&mutex_);
// Return false if the lock is busy and locking failed.
if (result == EBUSY) {
return false;
}
ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
owner_.Acquire();
return true;
}
void Mutex::Unlock() {
owner_.Release();
int result = pthread_mutex_unlock(&mutex_);
// Specifically check for wrong thread unlocking to aid debugging.
ASSERT(result != EPERM);
ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
}
ConditionVariable::ConditionVariable() {
pthread_condattr_t cond_attr;
int result = pthread_condattr_init(&cond_attr);
VALIDATE_PTHREAD_RESULT(result);
#if !defined(DART_HOST_OS_MACOS)
result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
VALIDATE_PTHREAD_RESULT(result);
#endif
result = pthread_cond_init(&cv_, &cond_attr);
VALIDATE_PTHREAD_RESULT(result);
result = pthread_condattr_destroy(&cond_attr);
VALIDATE_PTHREAD_RESULT(result);
}
ConditionVariable::~ConditionVariable() {
int result = pthread_cond_destroy(&cv_);
VALIDATE_PTHREAD_RESULT(result);
}
ConditionVariable::WaitResult ConditionVariable::Wait(Mutex* mutex,
int64_t millis) {
static_assert(kNoTimeout * kMicrosecondsPerMillisecond == kNoTimeout);
return WaitMicros(mutex, millis * kMicrosecondsPerMillisecond);
}
static int TimedWait(pthread_cond_t* cv,
pthread_mutex_t* mutex,
int64_t timeout_micros) {
const int64_t secs = timeout_micros / kMicrosecondsPerSecond;
const int64_t nanos = (timeout_micros - (secs * kMicrosecondsPerSecond)) *
kNanosecondsPerMicrosecond;
struct timespec ts;
#if defined(DART_HOST_OS_MACOS)
// On Mac OS X we can use non-portable pthread_cond_timedwait_relative_np
// instead of computing timespec for the wakeup moment.
ts.tv_sec = static_cast<int32_t>(
Utils::Minimum(static_cast<int64_t>(kMaxInt32), secs));
ts.tv_nsec = static_cast<long>(nanos); // NOLINT (long used in timespec).
return pthread_cond_timedwait_relative_np(cv, mutex, &ts);
#else
// Otherwise we need to compute absolute timespec.
int result = clock_gettime(CLOCK_MONOTONIC, &ts);
if (result != 0) {
return result;
}
ts.tv_sec += secs;
ts.tv_nsec += nanos;
if (ts.tv_nsec >= kNanosecondsPerSecond) {
ts.tv_sec += 1;
ts.tv_nsec -= kNanosecondsPerSecond;
}
return pthread_cond_timedwait(cv, mutex, &ts);
#endif
}
ConditionVariable::WaitResult ConditionVariable::WaitMicros(
Mutex* mutex,
int64_t timeout_micros) {
mutex->owner_.Release();
Monitor::WaitResult retval = kNotified;
if (timeout_micros == kNoTimeout) {
// Wait forever.
int result = pthread_cond_wait(&cv_, &mutex->mutex_);
VALIDATE_PTHREAD_RESULT(result);
} else {
int result = TimedWait(&cv_, &mutex->mutex_, timeout_micros);
ASSERT((result == 0) || (result == ETIMEDOUT));
if (result == ETIMEDOUT) {
retval = kTimedOut;
}
}
mutex->owner_.Acquire();
return retval;
}
void ConditionVariable::Notify() {
int result = pthread_cond_signal(&cv_);
VALIDATE_PTHREAD_RESULT(result);
}
void ConditionVariable::NotifyAll() {
int result = pthread_cond_broadcast(&cv_);
VALIDATE_PTHREAD_RESULT(result);
}
} // namespace dart
#endif // !defined(DART_USE_ABSL) && (defined(DART_HOST_OS_LINUX) || \
// defined(DART_HOST_OS_FUCHSIA) || \
// defined(DART_HOST_OS_MACOS) || \
// defined(DART_HOST_OS_ANDROID))