]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | /* | |
19 | * Copyright (C) 2016 ScyllaDB | |
20 | */ | |
21 | ||
22 | #pragma once | |
23 | ||
24 | #include <boost/intrusive/list.hpp> | |
25 | ||
26 | namespace seastar { | |
27 | ||
28 | /// A non-owning reference to an object. | |
29 | /// | |
30 | /// weak_ptr allows one to keep a non-owning reference to an object. When the | |
31 | /// object is destroyed, it notifies all weak_ptr instances pointing to it. | |
32 | /// A weak_ptr instance pointing to a destroyed object is equivalent to a | |
33 | /// `nullptr`. | |
34 | /// | |
35 | /// The referenced object must inherit from weakly_referencable. | |
36 | /// weak_ptr instances can only be obtained by calling weak_from_this() on | |
37 | /// the to-be-referenced object. | |
38 | /// | |
39 | /// \see weakly_referencable | |
40 | template<typename T> | |
41 | class weak_ptr { | |
42 | template<typename U> | |
43 | friend class weakly_referencable; | |
44 | private: | |
45 | using hook_type = boost::intrusive::list_member_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>; | |
46 | hook_type _hook; | |
47 | T* _ptr = nullptr; | |
f67539c2 | 48 | weak_ptr(T* p) noexcept : _ptr(p) {} |
20effc67 TL |
49 | void clear() noexcept { |
50 | _hook = {}; | |
51 | _ptr = nullptr; | |
52 | } | |
53 | void swap(weak_ptr&& o) noexcept { | |
54 | _hook.swap_nodes(o._hook); | |
55 | std::swap(_ptr, o._ptr); | |
56 | } | |
11fdf7f2 | 57 | public: |
f67539c2 TL |
58 | // Note: The default constructor's body is implemented as no-op |
59 | // rather than `noexcept = default` due to a bug with gcc 9.3.1 | |
60 | // that deletes the constructor since boost::intrusive::list_member_hook | |
61 | // is not default_nothrow_constructible. | |
62 | weak_ptr() noexcept {} | |
63 | weak_ptr(std::nullptr_t) noexcept : weak_ptr() {} | |
11fdf7f2 | 64 | weak_ptr(weak_ptr&& o) noexcept |
11fdf7f2 | 65 | { |
20effc67 TL |
66 | swap(std::move(o)); |
67 | } | |
68 | weak_ptr(const weak_ptr& o) noexcept { | |
69 | if (o._ptr) { | |
70 | swap(o._ptr->weak_from_this()); | |
71 | } | |
11fdf7f2 TL |
72 | } |
73 | weak_ptr& operator=(weak_ptr&& o) noexcept { | |
74 | if (this != &o) { | |
20effc67 TL |
75 | clear(); |
76 | swap(std::move(o)); | |
77 | } | |
78 | return *this; | |
79 | } | |
80 | weak_ptr& operator=(const weak_ptr& o) noexcept { | |
81 | if (this != &o) { | |
82 | clear(); | |
83 | if (o._ptr) { | |
84 | swap(o._ptr->weak_from_this()); | |
85 | } | |
11fdf7f2 TL |
86 | } |
87 | return *this; | |
88 | } | |
f67539c2 | 89 | explicit operator bool() const noexcept { return _ptr != nullptr; } |
11fdf7f2 TL |
90 | T* operator->() const noexcept { return _ptr; } |
91 | T& operator*() const noexcept { return *_ptr; } | |
92 | T* get() const noexcept { return _ptr; } | |
f67539c2 TL |
93 | bool operator==(const weak_ptr& o) const noexcept { return _ptr == o._ptr; } |
94 | bool operator!=(const weak_ptr& o) const noexcept { return _ptr != o._ptr; } | |
11fdf7f2 TL |
95 | }; |
96 | ||
97 | /// Allows obtaining a non-owning reference (weak_ptr) to the object. | |
98 | /// | |
99 | /// A live weak_ptr object doesn't prevent the referenced object form being destroyed. | |
100 | /// | |
101 | /// The underlying pointer held by weak_ptr is valid as long as the referenced object is alive. | |
102 | /// When the object dies, all weak_ptr objects associated with it are emptied. | |
103 | /// | |
104 | /// A weak reference is obtained like this: | |
105 | /// | |
106 | /// class X : public weakly_referencable<X> {}; | |
107 | /// auto x = std::make_unique<X>(); | |
108 | /// weak_ptr<X> ptr = x->weak_from_this(); | |
109 | /// | |
110 | /// The user of weak_ptr can check if it still holds a valid pointer like this: | |
111 | /// | |
112 | /// if (ptr) ptr->do_something(); | |
113 | /// | |
114 | template<typename T> | |
115 | class weakly_referencable { | |
116 | boost::intrusive::list<weak_ptr<T>, | |
117 | boost::intrusive::member_hook<weak_ptr<T>, typename weak_ptr<T>::hook_type, &weak_ptr<T>::_hook>, | |
118 | boost::intrusive::constant_time_size<false>> _ptr_list; | |
119 | public: | |
f67539c2 TL |
120 | // Note: The default constructor's body is implemented as no-op |
121 | // rather than `noexcept = default` due to a bug with gcc 9.3.1 | |
122 | // that deletes the constructor since boost::intrusive::member_hook | |
123 | // is not default_nothrow_constructible. | |
124 | weakly_referencable() noexcept {} | |
11fdf7f2 TL |
125 | weakly_referencable(weakly_referencable&&) = delete; // pointer to this is captured and passed to weak_ptr |
126 | weakly_referencable(const weakly_referencable&) = delete; | |
127 | ~weakly_referencable() noexcept { | |
128 | _ptr_list.clear_and_dispose([] (weak_ptr<T>* wp) noexcept { | |
129 | wp->_ptr = nullptr; | |
130 | }); | |
131 | } | |
132 | weak_ptr<T> weak_from_this() noexcept { | |
133 | weak_ptr<T> ptr(static_cast<T*>(this)); | |
134 | _ptr_list.push_back(ptr); | |
135 | return ptr; | |
136 | } | |
137 | }; | |
138 | ||
139 | } | |
140 |