Skip to content

Commit 82d9611

Browse files
committed
feat utils: mark containers with lifetimebound annotations
Annotations have been added to @ref concurrent::Variable, @ref rcu::Variable, @ref utils::LazySharedPtr, @ref utils::AnyMovable, @ref utils::AnyStorage, @ref utils::expected, @ref utils::FixedArray, @ref utils::SharedReadablePtr and some other container types. commit_hash:d544b8710ed6c4bc7116466a9d1ac2551aa2beb5
1 parent ff107a6 commit 82d9611

17 files changed

Lines changed: 152 additions & 118 deletions

File tree

core/include/userver/concurrent/variable.hpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <optional>
66
#include <shared_mutex> // for shared_lock
77

8+
#include <userver/compiler/impl/lifetime.hpp>
89
#include <userver/engine/mutex.hpp>
910

1011
USERVER_NAMESPACE_BEGIN
@@ -27,19 +28,27 @@ class LockedPtr final {
2728
data_(data)
2829
{}
2930

30-
Data& operator*() & { return data_; }
31-
const Data& operator*() const& { return data_; }
31+
Data& operator*() & USERVER_IMPL_LIFETIME_BOUND { return data_; }
32+
const Data& operator*() const& USERVER_IMPL_LIFETIME_BOUND { return data_; }
3233

3334
/// Don't use *tmp for temporary value, store it to variable.
3435
Data& operator*() && { return *GetOnRvalue(); }
3536

36-
Data* operator->() & { return &data_; }
37-
const Data* operator->() const& { return &data_; }
37+
Data* operator->() & USERVER_IMPL_LIFETIME_BOUND { return &data_; }
38+
const Data* operator->() const& USERVER_IMPL_LIFETIME_BOUND { return &data_; }
3839

3940
/// Don't use tmp-> for temporary value, store it to variable.
4041
Data* operator->() && { return GetOnRvalue(); }
4142

42-
Lock& GetLock() { return lock_; }
43+
/// For node-based containers, e.g. `std::unordered_map`, where container access needs to be locked,
44+
/// but after that the reference is stable and does not need the lock anymore.
45+
Data& GetUnsafeForStableSubobject() & { return data_; }
46+
const Data& GetUnsafeForStableSubobject() const& { return data_; }
47+
48+
/// Don't use *tmp for temporary value, store it to variable.
49+
Data& GetUnsafeForStableSubobject() && { return *GetOnRvalue(); }
50+
51+
Lock& GetLock() USERVER_IMPL_LIFETIME_BOUND { return lock_; }
4352

4453
private:
4554
const Data* GetOnRvalue() {

core/include/userver/rcu/rcu.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <optional>
1010
#include <utility>
1111

12+
#include <userver/compiler/impl/lifetime.hpp>
1213
#include <userver/concurrent/impl/asymmetric_fence.hpp>
1314
#include <userver/concurrent/impl/intrusive_hooks.hpp>
1415
#include <userver/concurrent/impl/intrusive_stack.hpp>
@@ -281,17 +282,17 @@ class [[nodiscard]] ReadablePtr final {
281282
ReadablePtr& operator=(const ReadablePtr& other) = default;
282283
~ReadablePtr() = default;
283284

284-
const T* Get() const& {
285+
const T* Get() const& USERVER_IMPL_LIFETIME_BOUND {
285286
UASSERT(ptr_);
286287
return ptr_;
287288
}
288289

289290
const T* Get() && { return GetOnRvalue(); }
290291

291-
const T* operator->() const& { return Get(); }
292+
const T* operator->() const& USERVER_IMPL_LIFETIME_BOUND { return Get(); }
292293
const T* operator->() && { return GetOnRvalue(); }
293294

294-
const T& operator*() const& { return *Get(); }
295+
const T& operator*() const& USERVER_IMPL_LIFETIME_BOUND { return *Get(); }
295296
const T& operator*() && { return *GetOnRvalue(); }
296297

297298
private:
@@ -352,17 +353,17 @@ class [[nodiscard]] WritablePtr final {
352353
lock_.unlock();
353354
}
354355

355-
T* Get() & {
356+
T* Get() & USERVER_IMPL_LIFETIME_BOUND {
356357
UASSERT(record_ != nullptr);
357358
return &*record_->data;
358359
}
359360

360361
T* Get() && { return GetOnRvalue(); }
361362

362-
T* operator->() & { return Get(); }
363+
T* operator->() & USERVER_IMPL_LIFETIME_BOUND { return Get(); }
363364
T* operator->() && { return GetOnRvalue(); }
364365

365-
T& operator*() & { return *Get(); }
366+
T& operator*() & USERVER_IMPL_LIFETIME_BOUND { return *Get(); }
366367
T& operator*() && { return *GetOnRvalue(); }
367368

368369
private:

core/include/userver/utils/lazy_shared_ptr.hpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <memory>
99
#include <utility>
1010

11+
#include <userver/compiler/impl/lifetime.hpp>
1112
#include <userver/engine/sleep.hpp>
1213
#include <userver/utils/assert.hpp>
1314
#include <userver/utils/shared_readable_ptr.hpp>
@@ -76,7 +77,7 @@ class LazySharedPtr final {
7677

7778
/// @brief Get a pointer to the data (may be null). Fetches the data on the
7879
/// first access.
79-
const T* Get() const& {
80+
const T* Get() const& USERVER_IMPL_LIFETIME_BOUND {
8081
if (const auto* current_value = value_.load(); current_value != kUnfilled) {
8182
return current_value;
8283
}
@@ -90,7 +91,7 @@ class LazySharedPtr final {
9091

9192
/// @brief Get a smart pointer to the data (may be null). Fetches the data on
9293
/// the first access.
93-
const utils::SharedReadablePtr<T>& GetShared() const& {
94+
const utils::SharedReadablePtr<T>& GetShared() const& USERVER_IMPL_LIFETIME_BOUND {
9495
if (value_ == kUnfilled) {
9596
auto readable = get_data_();
9697
if (const auto* expected = kUnfilled; value_.compare_exchange_strong(expected, readable.Get())) {
@@ -109,24 +110,24 @@ class LazySharedPtr final {
109110

110111
///@returns `*Get()`
111112
///@note `Get()` must not be `nullptr`.
112-
const T& operator*() const& {
113+
const T& operator*() const& USERVER_IMPL_LIFETIME_BOUND {
113114
const auto* res = Get();
114115
UASSERT(res);
115116
return *res;
116117
}
117118

118119
/// @returns `Get()`
119120
/// @note `Get()` must not be `nullptr`.
120-
const T* operator->() const& { return &**this; }
121+
const T* operator->() const& USERVER_IMPL_LIFETIME_BOUND { return &**this; }
121122

122123
/// @returns `Get() != nullptr`
123-
explicit operator bool() const& { return !!Get(); }
124+
explicit operator bool() const& USERVER_IMPL_LIFETIME_BOUND { return !!Get(); }
124125

125126
/// @returns `GetShared()`
126-
operator const utils::SharedReadablePtr<T>&() const& { return GetShared(); }
127+
operator const utils::SharedReadablePtr<T>&() const& USERVER_IMPL_LIFETIME_BOUND { return GetShared(); }
127128

128129
/// @returns `GetShared()`
129-
operator std::shared_ptr<const T>() const& { return GetShared(); }
130+
operator std::shared_ptr<const T>() const& USERVER_IMPL_LIFETIME_BOUND { return GetShared(); }
130131

131132
private:
132133
static inline const auto kUnfilled = reinterpret_cast<const T*>(std::uintptr_t{1});

kafka/functional_tests/balanced_consumer_groups/kafka_service.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ bool HandlerKafkaConsumerGroups::HasConsumer(const std::string& consumer_name) c
212212
kafka::ConsumerScope& HandlerKafkaConsumerGroups::GetConsumerScope(const std::string& consumer_name) const {
213213
auto consumer_by_name = consumer_by_name_.Lock();
214214

215-
return consumer_by_name->at(consumer_name);
215+
return consumer_by_name.GetUnsafeForStableSubobject().at(consumer_name);
216216
}
217217

218218
yaml_config::Schema HandlerKafkaConsumerGroups::GetStaticConfigSchema() {

universal/include/userver/utils/any_movable.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <type_traits>
1010
#include <utility>
1111

12+
#include <userver/compiler/impl/lifetime.hpp>
13+
1214
USERVER_NAMESPACE_BEGIN
1315

1416
/// Utilities
@@ -56,11 +58,11 @@ class AnyMovable final {
5658

5759
/// In-place constructs an object of the specified type
5860
template <typename ValueType, typename... Args>
59-
ValueType& Emplace(Args&&... args);
61+
ValueType& Emplace(Args&&... args) USERVER_IMPL_LIFETIME_BOUND;
6062

6163
/// In-place constructs an object of the specified type
6264
template <typename ValueType, typename Item, typename... Args>
63-
ValueType& Emplace(std::initializer_list<Item> list, Args&&... args);
65+
ValueType& Emplace(std::initializer_list<Item> list, Args&&... args) USERVER_IMPL_LIFETIME_BOUND;
6466

6567
private:
6668
struct HolderBase;
@@ -178,13 +180,13 @@ AnyMovable& AnyMovable::operator=(ValueType&& rhs) {
178180
}
179181

180182
template <typename ValueType, typename... Args>
181-
ValueType& AnyMovable::Emplace(Args&&... args) {
183+
ValueType& AnyMovable::Emplace(Args&&... args) USERVER_IMPL_LIFETIME_BOUND {
182184
content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
183185
return static_cast<Holder<ValueType>&>(*content_).held;
184186
}
185187

186188
template <typename ValueType, typename Item, typename... Args>
187-
ValueType& AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) {
189+
ValueType& AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) USERVER_IMPL_LIFETIME_BOUND {
188190
content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
189191
return static_cast<Holder<ValueType>&>(*content_).held;
190192
}

universal/include/userver/utils/any_storage.hpp

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <type_traits>
1111
#include <vector>
1212

13+
#include <userver/compiler/impl/lifetime.hpp>
14+
1315
USERVER_NAMESPACE_BEGIN
1416

1517
namespace utils {
@@ -86,31 +88,31 @@ class AnyStorage final {
8688

8789
/// @returns Stored data.
8890
template <typename Data>
89-
const Data& Get(const AnyStorageDataTag<StorageTag, Data>& tag) const;
91+
const Data& Get(const AnyStorageDataTag<StorageTag, Data>& tag) const USERVER_IMPL_LIFETIME_BOUND;
9092

9193
/// @returns Stored data.
9294
/// @throws std::runtime_error if no data was stored
9395
template <typename Data>
94-
Data& Get(const AnyStorageDataTag<StorageTag, Data>& tag);
96+
Data& Get(const AnyStorageDataTag<StorageTag, Data>& tag) USERVER_IMPL_LIFETIME_BOUND;
9597

9698
/// @brief Stores the data.
9799
template <typename Data>
98-
Data& Set(AnyStorageDataTag<StorageTag, Data> tag, Data data);
100+
Data& Set(AnyStorageDataTag<StorageTag, Data> tag, Data data) USERVER_IMPL_LIFETIME_BOUND;
99101

100102
/// @brief Emplaces the data. The data is rewritten if
101103
/// already stored.
102104
template <typename Data, typename... Args>
103-
Data& Emplace(const AnyStorageDataTag<StorageTag, Data>& tag, Args&&... args);
105+
Data& Emplace(const AnyStorageDataTag<StorageTag, Data>& tag, Args&&... args) USERVER_IMPL_LIFETIME_BOUND;
104106

105107
/// @returns Pointer to stored data or nullptr if
106108
/// no data is found.
107109
template <typename Data>
108-
Data* GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) noexcept;
110+
Data* GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) noexcept USERVER_IMPL_LIFETIME_BOUND;
109111

110112
/// @returns Pointer to stored data or nullptr if
111113
/// no data found.
112114
template <typename Data>
113-
const Data* GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) const noexcept;
115+
const Data* GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) const noexcept USERVER_IMPL_LIFETIME_BOUND;
114116

115117
/// @brief Erase data.
116118
template <typename Data>
@@ -139,7 +141,7 @@ any_storage::impl::Offset AnyStorage<StorageTag>::CalcOffset() noexcept {
139141

140142
template <typename StorageTag>
141143
AnyStorage<StorageTag>::AnyStorage()
142-
: raw_data_(new std::byte[CalcOffset() + sizeof(AllocRecord) * any_storage::impl::count<StorageTag>])
144+
: raw_data_(new std::byte[CalcOffset() + (sizeof(AllocRecord) * any_storage::impl::count<StorageTag>)])
143145
{
144146
static_assert(std::is_trivial_v<AllocRecord>);
145147

@@ -184,7 +186,8 @@ void AnyStorage<StorageTag>::Destroy() noexcept {
184186

185187
template <typename StorageTag>
186188
template <typename Data>
187-
Data& AnyStorage<StorageTag>::Set(const AnyStorageDataTag<StorageTag, Data> tag, Data data) {
189+
Data& AnyStorage<StorageTag>::Set(const AnyStorageDataTag<StorageTag, Data> tag, Data data)
190+
USERVER_IMPL_LIFETIME_BOUND {
188191
auto number = tag.number_;
189192
if (!GetRecords()[number].deleter) {
190193
return Emplace(tag, std::move(data));
@@ -196,7 +199,8 @@ Data& AnyStorage<StorageTag>::Set(const AnyStorageDataTag<StorageTag, Data> tag,
196199

197200
template <typename StorageTag>
198201
template <typename Data, typename... Args>
199-
Data& AnyStorage<StorageTag>::Emplace(const AnyStorageDataTag<StorageTag, Data>& tag, Args&&... args) {
202+
Data& AnyStorage<StorageTag>::Emplace(const AnyStorageDataTag<StorageTag, Data>& tag, Args&&... args)
203+
USERVER_IMPL_LIFETIME_BOUND {
200204
auto number = tag.number_;
201205
auto& record = GetRecords()[number];
202206
if (record.deleter) {
@@ -211,7 +215,7 @@ Data& AnyStorage<StorageTag>::Emplace(const AnyStorageDataTag<StorageTag, Data>&
211215

212216
template <typename StorageTag>
213217
template <typename Data>
214-
Data& AnyStorage<StorageTag>::Get(const AnyStorageDataTag<StorageTag, Data>& tag) {
218+
Data& AnyStorage<StorageTag>::Get(const AnyStorageDataTag<StorageTag, Data>& tag) USERVER_IMPL_LIFETIME_BOUND {
215219
auto ptr = GetOptional(tag);
216220
if (ptr) {
217221
return *ptr;
@@ -221,14 +225,16 @@ Data& AnyStorage<StorageTag>::Get(const AnyStorageDataTag<StorageTag, Data>& tag
221225

222226
template <typename StorageTag>
223227
template <typename Data>
224-
const Data& AnyStorage<StorageTag>::Get(const AnyStorageDataTag<StorageTag, Data>& tag) const {
228+
const Data& AnyStorage<StorageTag>::Get(const AnyStorageDataTag<StorageTag, Data>& tag
229+
) const USERVER_IMPL_LIFETIME_BOUND {
225230
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
226231
return const_cast<AnyStorage<StorageTag>*>(this)->Get<Data>(tag);
227232
}
228233

229234
template <typename StorageTag>
230235
template <typename Data>
231-
Data* AnyStorage<StorageTag>::GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) noexcept {
236+
Data* AnyStorage<StorageTag>::GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag
237+
) noexcept USERVER_IMPL_LIFETIME_BOUND {
232238
auto number = tag.number_;
233239
auto offset = tag.offset_;
234240
if (!GetRecords()[number].deleter) {
@@ -239,7 +245,8 @@ Data* AnyStorage<StorageTag>::GetOptional(const AnyStorageDataTag<StorageTag, Da
239245

240246
template <typename StorageTag>
241247
template <typename Data>
242-
const Data* AnyStorage<StorageTag>::GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag) const noexcept {
248+
const Data* AnyStorage<StorageTag>::GetOptional(const AnyStorageDataTag<StorageTag, Data>& tag
249+
) const noexcept USERVER_IMPL_LIFETIME_BOUND {
243250
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
244251
return const_cast<AnyStorage*>(this)->GetOptional<Data>(tag);
245252
}

0 commit comments

Comments
 (0)