// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_
#define CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_

#include <memory>
#include <utility>

namespace cc {

// ProtectedSequenceSynchronizer can be used to enforce thread safety of data
// that are owned and produced by an "owner" thread; and then passed by
// reference to another thread to use for a limited duration (a "protected
// sequence"). A protected sequence must be initiated on the owning thread, and
// it must be concluded on the non-owning thread. See the code comment above
// InProtectedSequence() for more on this requirement.
class ProtectedSequenceSynchronizer {
 public:
  ProtectedSequenceSynchronizer() = default;
  ProtectedSequenceSynchronizer(const ProtectedSequenceSynchronizer&) = delete;
  ProtectedSequenceSynchronizer& operator=(
      const ProtectedSequenceSynchronizer&) = delete;
  virtual ~ProtectedSequenceSynchronizer() = default;

  // Returns true if the current thread is the owner and producer of these data.
  virtual bool IsOwnerThread() const = 0;

  // Returns true if a non-owner thread is currently running a protected
  // sequence. The owner thread must be responsible for transitioning the return
  // value from false to true, and the non-owner thread must be responsible for
  // transitioning from true to false. Failure to adhere to these guidelines
  // will likely cause race conditions and/or deadlock.
  virtual bool InProtectedSequence() const = 0;

  // Blocks execution of the owner thread until a non-owner thread finishes
  // executing a protected sequence. It is an error for this to be called on a
  // non-owner thread.
  virtual void WaitForProtectedSequenceCompletion() const = 0;
};

// ProtectedSequenceForbidden values cannot be accessed for read or write by any
// non-owner thread. There are no restrictions on access by the owner thread.
template <typename T>
class ProtectedSequenceForbidden {
 public:
  template <typename... Args>
  explicit ProtectedSequenceForbidden(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceForbidden(const ProtectedSequenceForbidden&) = delete;
  ProtectedSequenceForbidden& operator=(const ProtectedSequenceForbidden&) =
      delete;

  const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    DCHECK(synchronizer.IsOwnerThread());
    return value_;
  }

  T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread());
    return value_;
  }

 private:
  T value_;
};

// ProtectedSequenceReadable values are...
//   - readable by the owner thread at any time without blocking
//   - writable by the owner thread, but not during a protected sequence
//   - readable by a non-owner thread during a protected sequence
//   - never writable by a non-owner thread
template <typename T>
class ProtectedSequenceReadable {
 public:
  template <typename... Args>
  explicit ProtectedSequenceReadable(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceReadable(const ProtectedSequenceReadable&) = delete;
  ProtectedSequenceReadable& operator=(const ProtectedSequenceReadable&) =
      delete;

  const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
    return value_;
  }

  T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread());
    synchronizer.WaitForProtectedSequenceCompletion();
    return value_;
  }

 private:
  T value_;
};

// ProtectedSequenceWritable values are...
//   - readable by the owner thread, but not during a protected sequence
//   - writable by the owner thread, but not during a protected sequence
//   - readable by a non-owner thread during a protected sequence
//   - writable by a non-owner thread during a protected sequence
//
// Note that it is not safe to use ProtectedSequenceWritable values concurrently
// on two or more non-owner threads.
template <typename T>
class ProtectedSequenceWritable {
 public:
  template <typename... Args>
  explicit ProtectedSequenceWritable(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceWritable(const ProtectedSequenceWritable&) = delete;
  ProtectedSequenceWritable& operator=(const ProtectedSequenceWritable&) =
      delete;

  const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
    if (synchronizer.IsOwnerThread())
      synchronizer.WaitForProtectedSequenceCompletion();
    return value_;
  }

  T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
    if (synchronizer.IsOwnerThread())
      synchronizer.WaitForProtectedSequenceCompletion();
    return value_;
  }

 private:
  T value_;
};

// Type specializations for various containers.

template <typename T>
class ProtectedSequenceForbidden<std::unique_ptr<T>> {
 public:
  template <typename... Args>
  explicit ProtectedSequenceForbidden(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceForbidden(const ProtectedSequenceForbidden&) = delete;
  ProtectedSequenceForbidden& operator=(const ProtectedSequenceForbidden&) =
      delete;

  const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    DCHECK(synchronizer.IsOwnerThread());
    return value_.get();
  }

  std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread());
    return value_;
  }

 private:
  std::unique_ptr<T> value_;
};

template <typename T>
class ProtectedSequenceReadable<std::unique_ptr<T>> {
 public:
  template <typename... Args>
  explicit ProtectedSequenceReadable(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceReadable(const ProtectedSequenceReadable&) = delete;
  ProtectedSequenceReadable& operator=(const ProtectedSequenceReadable&) =
      delete;

  const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    return value_.get();
  }

  std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread());
    synchronizer.WaitForProtectedSequenceCompletion();
    return value_;
  }

 private:
  std::unique_ptr<T> value_;
};

template <typename T>
class ProtectedSequenceWritable<std::unique_ptr<T>> {
 public:
  template <typename... Args>
  explicit ProtectedSequenceWritable(Args&&... args)
      : value_(std::forward<Args>(args)...) {}

  ProtectedSequenceWritable(const ProtectedSequenceWritable&) = delete;
  ProtectedSequenceWritable& operator=(const ProtectedSequenceWritable&) =
      delete;

  const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
    DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
    if (synchronizer.IsOwnerThread())
      synchronizer.WaitForProtectedSequenceCompletion();
    return value_.get();
  }

  std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
    DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
    if (synchronizer.IsOwnerThread())
      synchronizer.WaitForProtectedSequenceCompletion();
    return value_;
  }

 private:
  std::unique_ptr<T> value_;
};

}  // namespace cc

#endif  // CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_
