blob: a936d68ddb033df1ffe6a77344e02f5c3fb7d12f [file] [log] [blame]
// 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_