311 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			311 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2017 The Abseil Authors.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
// Implementation of a small subset of Mutex and CondVar functionality
 | 
						|
// for platforms where the production implementation hasn't been fully
 | 
						|
// ported yet.
 | 
						|
 | 
						|
#include "absl/synchronization/mutex.h"
 | 
						|
 | 
						|
#if defined(_WIN32)
 | 
						|
#include <chrono>  // NOLINT(build/c++11)
 | 
						|
#else
 | 
						|
#include <sys/time.h>
 | 
						|
#include <time.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include "absl/base/internal/raw_logging.h"
 | 
						|
#include "absl/time/time.h"
 | 
						|
 | 
						|
namespace absl {
 | 
						|
namespace synchronization_internal {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
// Return the current time plus the timeout.
 | 
						|
absl::Time DeadlineFromTimeout(absl::Duration timeout) {
 | 
						|
  return absl::Now() + timeout;
 | 
						|
}
 | 
						|
 | 
						|
// Limit the deadline to a positive, 32-bit time_t value to accommodate
 | 
						|
// implementation restrictions.  This also deals with InfinitePast and
 | 
						|
// InfiniteFuture.
 | 
						|
absl::Time LimitedDeadline(absl::Time deadline) {
 | 
						|
  deadline = std::max(absl::FromTimeT(0), deadline);
 | 
						|
  deadline = std::min(deadline, absl::FromTimeT(0x7fffffff));
 | 
						|
  return deadline;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
#if defined(_WIN32)
 | 
						|
 | 
						|
MutexImpl::MutexImpl() {}
 | 
						|
 | 
						|
MutexImpl::~MutexImpl() {
 | 
						|
  if (locked_) {
 | 
						|
    std_mutex_.unlock();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MutexImpl::Lock() {
 | 
						|
  std_mutex_.lock();
 | 
						|
  locked_ = true;
 | 
						|
}
 | 
						|
 | 
						|
bool MutexImpl::TryLock() {
 | 
						|
  bool locked = std_mutex_.try_lock();
 | 
						|
  if (locked) locked_ = true;
 | 
						|
  return locked;
 | 
						|
}
 | 
						|
 | 
						|
void MutexImpl::Unlock() {
 | 
						|
  locked_ = false;
 | 
						|
  released_.SignalAll();
 | 
						|
  std_mutex_.unlock();
 | 
						|
}
 | 
						|
 | 
						|
CondVarImpl::CondVarImpl() {}
 | 
						|
 | 
						|
CondVarImpl::~CondVarImpl() {}
 | 
						|
 | 
						|
void CondVarImpl::Signal() { std_cv_.notify_one(); }
 | 
						|
 | 
						|
void CondVarImpl::SignalAll() { std_cv_.notify_all(); }
 | 
						|
 | 
						|
void CondVarImpl::Wait(MutexImpl* mu) {
 | 
						|
  mu->released_.SignalAll();
 | 
						|
  std_cv_.wait(mu->std_mutex_);
 | 
						|
}
 | 
						|
 | 
						|
bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) {
 | 
						|
  mu->released_.SignalAll();
 | 
						|
  time_t when = ToTimeT(deadline);
 | 
						|
  int64_t nanos = ToInt64Nanoseconds(deadline - absl::FromTimeT(when));
 | 
						|
  std::chrono::system_clock::time_point deadline_tp =
 | 
						|
      std::chrono::system_clock::from_time_t(when) +
 | 
						|
      std::chrono::duration_cast<std::chrono::system_clock::duration>(
 | 
						|
          std::chrono::nanoseconds(nanos));
 | 
						|
  auto deadline_since_epoch =
 | 
						|
      std::chrono::duration_cast<std::chrono::duration<double>>(
 | 
						|
          deadline_tp - std::chrono::system_clock::from_time_t(0));
 | 
						|
  return std_cv_.wait_until(mu->std_mutex_, deadline_tp) ==
 | 
						|
         std::cv_status::timeout;
 | 
						|
}
 | 
						|
 | 
						|
#else  // ! _WIN32
 | 
						|
 | 
						|
MutexImpl::MutexImpl() {
 | 
						|
  ABSL_RAW_CHECK(pthread_mutex_init(&pthread_mutex_, nullptr) == 0,
 | 
						|
                 "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
MutexImpl::~MutexImpl() {
 | 
						|
  if (locked_) {
 | 
						|
    ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error");
 | 
						|
  }
 | 
						|
  ABSL_RAW_CHECK(pthread_mutex_destroy(&pthread_mutex_) == 0, "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
void MutexImpl::Lock() {
 | 
						|
  ABSL_RAW_CHECK(pthread_mutex_lock(&pthread_mutex_) == 0, "pthread error");
 | 
						|
  locked_ = true;
 | 
						|
}
 | 
						|
 | 
						|
bool MutexImpl::TryLock() {
 | 
						|
  bool locked = (0 == pthread_mutex_trylock(&pthread_mutex_));
 | 
						|
  if (locked) locked_ = true;
 | 
						|
  return locked;
 | 
						|
}
 | 
						|
 | 
						|
void MutexImpl::Unlock() {
 | 
						|
  locked_ = false;
 | 
						|
  released_.SignalAll();
 | 
						|
  ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
CondVarImpl::CondVarImpl() {
 | 
						|
  ABSL_RAW_CHECK(pthread_cond_init(&pthread_cv_, nullptr) == 0,
 | 
						|
                 "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
CondVarImpl::~CondVarImpl() {
 | 
						|
  ABSL_RAW_CHECK(pthread_cond_destroy(&pthread_cv_) == 0, "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
void CondVarImpl::Signal() {
 | 
						|
  ABSL_RAW_CHECK(pthread_cond_signal(&pthread_cv_) == 0, "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
void CondVarImpl::SignalAll() {
 | 
						|
  ABSL_RAW_CHECK(pthread_cond_broadcast(&pthread_cv_) == 0, "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
void CondVarImpl::Wait(MutexImpl* mu) {
 | 
						|
  mu->released_.SignalAll();
 | 
						|
  ABSL_RAW_CHECK(pthread_cond_wait(&pthread_cv_, &mu->pthread_mutex_) == 0,
 | 
						|
                 "pthread error");
 | 
						|
}
 | 
						|
 | 
						|
bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) {
 | 
						|
  mu->released_.SignalAll();
 | 
						|
  struct timespec ts = ToTimespec(deadline);
 | 
						|
  int rc = pthread_cond_timedwait(&pthread_cv_, &mu->pthread_mutex_, &ts);
 | 
						|
  if (rc == ETIMEDOUT) return true;
 | 
						|
  ABSL_RAW_CHECK(rc == 0, "pthread error");
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
#endif  // ! _WIN32
 | 
						|
 | 
						|
void MutexImpl::Await(const Condition& cond) {
 | 
						|
  if (cond.Eval()) return;
 | 
						|
  released_.SignalAll();
 | 
						|
  do {
 | 
						|
    released_.Wait(this);
 | 
						|
  } while (!cond.Eval());
 | 
						|
}
 | 
						|
 | 
						|
bool MutexImpl::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
 | 
						|
  if (cond.Eval()) return true;
 | 
						|
  released_.SignalAll();
 | 
						|
  while (true) {
 | 
						|
    if (released_.WaitWithDeadline(this, deadline)) return false;
 | 
						|
    if (cond.Eval()) return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace synchronization_internal
 | 
						|
 | 
						|
Mutex::Mutex() {}
 | 
						|
 | 
						|
Mutex::~Mutex() {}
 | 
						|
 | 
						|
void Mutex::Lock() { impl()->Lock(); }
 | 
						|
 | 
						|
void Mutex::Unlock() { impl()->Unlock(); }
 | 
						|
 | 
						|
bool Mutex::TryLock() { return impl()->TryLock(); }
 | 
						|
 | 
						|
void Mutex::ReaderLock() { Lock(); }
 | 
						|
 | 
						|
void Mutex::ReaderUnlock() { Unlock(); }
 | 
						|
 | 
						|
void Mutex::Await(const Condition& cond) { impl()->Await(cond); }
 | 
						|
 | 
						|
void Mutex::LockWhen(const Condition& cond) {
 | 
						|
  Lock();
 | 
						|
  Await(cond);
 | 
						|
}
 | 
						|
 | 
						|
bool Mutex::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
 | 
						|
  return impl()->AwaitWithDeadline(
 | 
						|
      cond, synchronization_internal::LimitedDeadline(deadline));
 | 
						|
}
 | 
						|
 | 
						|
bool Mutex::AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
 | 
						|
  return AwaitWithDeadline(
 | 
						|
      cond, synchronization_internal::DeadlineFromTimeout(timeout));
 | 
						|
}
 | 
						|
 | 
						|
bool Mutex::LockWhenWithDeadline(const Condition& cond, absl::Time deadline) {
 | 
						|
  Lock();
 | 
						|
  return AwaitWithDeadline(cond, deadline);
 | 
						|
}
 | 
						|
 | 
						|
bool Mutex::LockWhenWithTimeout(const Condition& cond, absl::Duration timeout) {
 | 
						|
  return LockWhenWithDeadline(
 | 
						|
      cond, synchronization_internal::DeadlineFromTimeout(timeout));
 | 
						|
}
 | 
						|
 | 
						|
bool Mutex::ReaderLockWhenWithTimeout(const Condition& cond,
 | 
						|
                                      absl::Duration timeout) {
 | 
						|
  return LockWhenWithTimeout(cond, timeout);
 | 
						|
}
 | 
						|
bool Mutex::ReaderLockWhenWithDeadline(const Condition& cond,
 | 
						|
                                       absl::Time deadline) {
 | 
						|
  return LockWhenWithDeadline(cond, deadline);
 | 
						|
}
 | 
						|
 | 
						|
void Mutex::EnableDebugLog(const char*) {}
 | 
						|
void Mutex::EnableInvariantDebugging(void (*)(void*), void*) {}
 | 
						|
void Mutex::ForgetDeadlockInfo() {}
 | 
						|
void Mutex::AssertHeld() const {}
 | 
						|
void Mutex::AssertReaderHeld() const {}
 | 
						|
void Mutex::AssertNotHeld() const {}
 | 
						|
 | 
						|
CondVar::CondVar() {}
 | 
						|
 | 
						|
CondVar::~CondVar() {}
 | 
						|
 | 
						|
void CondVar::Signal() { impl()->Signal(); }
 | 
						|
 | 
						|
void CondVar::SignalAll() { impl()->SignalAll(); }
 | 
						|
 | 
						|
void CondVar::Wait(Mutex* mu) { return impl()->Wait(mu->impl()); }
 | 
						|
 | 
						|
bool CondVar::WaitWithDeadline(Mutex* mu, absl::Time deadline) {
 | 
						|
  return impl()->WaitWithDeadline(
 | 
						|
      mu->impl(), synchronization_internal::LimitedDeadline(deadline));
 | 
						|
}
 | 
						|
 | 
						|
bool CondVar::WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
 | 
						|
  return WaitWithDeadline(mu, absl::Now() + timeout);
 | 
						|
}
 | 
						|
 | 
						|
void CondVar::EnableDebugLog(const char*) {}
 | 
						|
 | 
						|
#ifdef THREAD_SANITIZER
 | 
						|
extern "C" void __tsan_read1(void *addr);
 | 
						|
#else
 | 
						|
#define __tsan_read1(addr)  // do nothing if TSan not enabled
 | 
						|
#endif
 | 
						|
 | 
						|
// A function that just returns its argument, dereferenced
 | 
						|
static bool Dereference(void *arg) {
 | 
						|
  // ThreadSanitizer does not instrument this file for memory accesses.
 | 
						|
  // This function dereferences a user variable that can participate
 | 
						|
  // in a data race, so we need to manually tell TSan about this memory access.
 | 
						|
  __tsan_read1(arg);
 | 
						|
  return *(static_cast<bool *>(arg));
 | 
						|
}
 | 
						|
 | 
						|
Condition::Condition() {}   // null constructor, used for kTrue only
 | 
						|
const Condition Condition::kTrue;
 | 
						|
 | 
						|
Condition::Condition(bool (*func)(void *), void *arg)
 | 
						|
    : eval_(&CallVoidPtrFunction),
 | 
						|
      function_(func),
 | 
						|
      method_(nullptr),
 | 
						|
      arg_(arg) {}
 | 
						|
 | 
						|
bool Condition::CallVoidPtrFunction(const Condition *c) {
 | 
						|
  return (*c->function_)(c->arg_);
 | 
						|
}
 | 
						|
 | 
						|
Condition::Condition(const bool *cond)
 | 
						|
    : eval_(CallVoidPtrFunction),
 | 
						|
      function_(Dereference),
 | 
						|
      method_(nullptr),
 | 
						|
      // const_cast is safe since Dereference does not modify arg
 | 
						|
      arg_(const_cast<bool *>(cond)) {}
 | 
						|
 | 
						|
bool Condition::Eval() const {
 | 
						|
  // eval_ == null for kTrue
 | 
						|
  return (this->eval_ == nullptr) || (*this->eval_)(this);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace absl
 |