blob: db0fe25f706603dddcaf0771185b492b21e70bcc [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
#define BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
#include <memory>
#include <type_traits>
#include <utility>
#include "base/base_export.h"
#include "base/threading/sequence_local_storage_map.h"
#include "third_party/abseil-cpp/absl/meta/type_traits.h"
namespace base {
namespace internal {
BASE_EXPORT int GetNextSequenceLocalStorageSlotNumber();
}
// SequenceLocalStorageSlot allows arbitrary values to be stored and retrieved
// from a sequence. Values are deleted when the sequence is deleted.
//
// Example usage:
//
// int& GetSequenceLocalStorage()
// static SequenceLocalStorageSlot<int> sls_value;
// return sls_value->GetOrCreateValue();
// }
//
// void Read() {
// int value = GetSequenceLocalStorage();
// ...
// }
//
// void Write() {
// GetSequenceLocalStorage() = 42;
// }
//
// void PostTasks() {
// // Since Read() runs on the same sequence as Write(), it
// // will read the value "42". A Read() running on a different
// // sequence would not see that value.
// scoped_refptr<base::SequencedTaskRunner> task_runner = ...;
// task_runner->PostTask(FROM_HERE, base::BindOnce(&Write));
// task_runner->PostTask(FROM_HERE, base::BindOnce(&Read));
// }
//
// SequenceLocalStorageSlot must be used within the scope of a
// ScopedSetSequenceLocalStorageMapForCurrentThread object.
// Note: this is true on all ThreadPool workers and on threads bound to a
// MessageLoop.
// SequenceLocalStorageSlot is implemented by either [Generic/Small]
// variants depending on the type. SequenceLocalStorageSlot itself
// doesn't support forward declared types and thus the variant
// [Generic/Small] needs to be specified explicitly.
// Generic implementation for SequenceLocalStorageSlot.
template <typename T, typename Deleter = std::default_delete<T>>
class GenericSequenceLocalStorageSlot {
public:
GenericSequenceLocalStorageSlot()
: slot_id_(internal::GetNextSequenceLocalStorageSlotNumber()) {}
GenericSequenceLocalStorageSlot(const GenericSequenceLocalStorageSlot&) =
delete;
GenericSequenceLocalStorageSlot& operator=(
const GenericSequenceLocalStorageSlot&) = delete;
~GenericSequenceLocalStorageSlot() = default;
explicit operator bool() const {
return internal::SequenceLocalStorageMap::GetForCurrentThread().Has(
slot_id_);
}
// Default-constructs the value for the current sequence if not
// already constructed. Then, returns the value.
T& GetOrCreateValue() {
auto* slot =
internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
if (!slot) {
return emplace();
}
return slot->external_value.value_as<T>();
}
// Returns a pointer to the value for the current sequence. May be
// nullptr if the value was not constructed on the current sequence.
T* GetValuePointer() {
auto* value =
internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
if (value) {
return std::addressof(value->external_value.value_as<T>());
}
return nullptr;
}
const T* GetValuePointer() const {
return const_cast<GenericSequenceLocalStorageSlot*>(this)
->GetValuePointer();
}
T* operator->() { return GetValuePointer(); }
const T* operator->() const { return GetValuePointer(); }
T& operator*() { return *GetValuePointer(); }
const T& operator*() const { return *GetValuePointer(); }
void reset() {
internal::SequenceLocalStorageMap::GetForCurrentThread().Reset(slot_id_);
}
// Constructs this slot's sequence-local value with |args...| and returns a
// pointer to the created object.
template <class... Args>
T& emplace(Args&&... args) {
T* value_ptr = new T(std::forward<Args>(args)...);
Adopt(value_ptr);
return *value_ptr;
}
private:
// Takes ownership of |value_ptr|.
void Adopt(T* value_ptr) {
// Since SequenceLocalStorageMap needs to store values of various types
// within the same map, the type of value_destructor_pair.value is void*
// (std::unique_ptr<void> is invalid). Memory is freed by calling
// |value_destructor_pair.destructor| in the destructor of
// ValueDestructorPair which is invoked when the value is overwritten by
// another call to SequenceLocalStorageMap::Set or when the
// SequenceLocalStorageMap is deleted.
internal::SequenceLocalStorageMap::ExternalValue value;
value.emplace(value_ptr);
internal::SequenceLocalStorageMap::ValueDestructorPair
value_destructor_pair(
std::move(value),
internal::SequenceLocalStorageMap::MakeExternalDestructor<
T, Deleter>());
internal::SequenceLocalStorageMap::GetForCurrentThread().Set(
slot_id_, std::move(value_destructor_pair));
}
// |slot_id_| is used as a key in SequenceLocalStorageMap
const int slot_id_;
};
// Implementation for SequenceLocalStorageSlot optimized for small and trivial
// objects.
template <class T>
class SmallSequenceLocalStorageSlot {
public:
SmallSequenceLocalStorageSlot()
: slot_id_(internal::GetNextSequenceLocalStorageSlotNumber()) {}
SmallSequenceLocalStorageSlot(const SmallSequenceLocalStorageSlot&) = delete;
SmallSequenceLocalStorageSlot& operator=(
const SmallSequenceLocalStorageSlot&) = delete;
~SmallSequenceLocalStorageSlot() = default;
explicit operator bool() const {
return internal::SequenceLocalStorageMap::GetForCurrentThread().Has(
slot_id_);
}
// Default-constructs the value for the current sequence if not
// already constructed. Then, returns the value.
T& GetOrCreateValue() {
auto* slot =
internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
if (!slot) {
return emplace();
}
return slot->inline_value.value_as<T>();
}
// Returns a pointer to the value for the current sequence. May be
// nullptr if the value was not constructed on the current sequence.
T* GetValuePointer() {
auto* slot =
internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
if (!slot) {
return nullptr;
}
return &slot->inline_value.value_as<T>();
}
const T* GetValuePointer() const {
return const_cast<SmallSequenceLocalStorageSlot*>(this)->GetValuePointer();
}
T* operator->() { return GetValuePointer(); }
const T* operator->() const { return GetValuePointer(); }
T& operator*() { return *GetValuePointer(); }
const T& operator*() const { return *GetValuePointer(); }
void reset() {
internal::SequenceLocalStorageMap::GetForCurrentThread().Reset(slot_id_);
}
// Constructs this slot's sequence-local value with |args...| and returns a
// pointer to the created object.
template <class... Args>
T& emplace(Args&&... args) {
internal::SequenceLocalStorageMap::InlineValue value;
value.emplace<T>(std::forward<Args>(args)...);
internal::SequenceLocalStorageMap::ValueDestructorPair
value_destructor_pair(
std::move(value),
internal::SequenceLocalStorageMap::MakeInlineDestructor<T>());
return internal::SequenceLocalStorageMap::GetForCurrentThread()
.Set(slot_id_, std::move(value_destructor_pair))
->inline_value.value_as<T>();
}
private:
// |slot_id_| is used as a key in SequenceLocalStorageMap
const int slot_id_;
};
template <typename T,
typename Deleter = std::default_delete<T>,
bool IsSmall =
sizeof(T) <= sizeof(void*) && absl::is_trivially_relocatable<T>()>
struct SequenceLocalStorageSlot;
template <typename T, typename Deleter>
struct SequenceLocalStorageSlot<T, Deleter, false>
: GenericSequenceLocalStorageSlot<T, Deleter> {};
template <typename T>
struct SequenceLocalStorageSlot<T, std::default_delete<T>, true>
: SmallSequenceLocalStorageSlot<T> {};
} // namespace base
#endif // BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_