blob: ee577dedfe731d97ea3334481a903a92e1818b88 [file] [log] [blame]
Patrick Williams5b5d15b2022-08-23 09:35:15 -05001/*
2 * Copyright (c) 2021-2022 Facebook, Inc. and its affiliates
3 * Copyright (c) 2021-2022 NVIDIA Corporation
4 *
5 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://llvm.org/LICENSE.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#pragma once
18
Patrick Williams5e7ef082023-01-05 11:08:53 -060019#include "__config.hpp"
20
Patrick Williams5b5d15b2022-08-23 09:35:15 -050021#include <cassert>
Patrick Williams8723a542024-02-12 11:18:32 -060022#include <cstddef>
Patrick Williams5b5d15b2022-08-23 09:35:15 -050023#include <utility>
24
25namespace stdexec
26{
27namespace __queue
28{
29template <auto _Next>
30class __intrusive_queue;
31
32template <class _Item, _Item* _Item::*_Next>
33class __intrusive_queue<_Next>
34{
35 public:
36 __intrusive_queue() noexcept = default;
37
38 __intrusive_queue(__intrusive_queue&& __other) noexcept :
39 __head_(std::exchange(__other.__head_, nullptr)),
40 __tail_(std::exchange(__other.__tail_, nullptr))
41 {}
42
Patrick Williams5932cd62023-11-16 11:13:00 -060043 __intrusive_queue(_Item* __head, _Item* __tail) noexcept :
44 __head_(__head), __tail_(__tail)
45 {}
46
Patrick Williams5b5d15b2022-08-23 09:35:15 -050047 __intrusive_queue& operator=(__intrusive_queue __other) noexcept
48 {
49 std::swap(__head_, __other.__head_);
50 std::swap(__tail_, __other.__tail_);
51 return *this;
52 }
53
54 ~__intrusive_queue()
55 {
Patrick Williams5e7ef082023-01-05 11:08:53 -060056 STDEXEC_ASSERT(empty());
Patrick Williams5b5d15b2022-08-23 09:35:15 -050057 }
58
59 static __intrusive_queue make_reversed(_Item* __list) noexcept
60 {
61 _Item* __new_head = nullptr;
62 _Item* __new_tail = __list;
63 while (__list != nullptr)
64 {
65 _Item* __next = __list->*_Next;
66 __list->*_Next = __new_head;
67 __new_head = __list;
68 __list = __next;
69 }
70
71 __intrusive_queue __result;
72 __result.__head_ = __new_head;
73 __result.__tail_ = __new_tail;
74 return __result;
75 }
76
Patrick Williams5932cd62023-11-16 11:13:00 -060077 static __intrusive_queue make(_Item* __list) noexcept
78 {
79 __intrusive_queue __result{};
80 __result.__head_ = __list;
81 __result.__tail_ = __list;
82 if (__list == nullptr)
83 {
84 return __result;
85 }
86 while (__result.__tail_->*_Next != nullptr)
87 {
88 __result.__tail_ = __result.__tail_->*_Next;
89 }
90 return __result;
91 }
92
Patrick Williams5b5d15b2022-08-23 09:35:15 -050093 [[nodiscard]] bool empty() const noexcept
94 {
95 return __head_ == nullptr;
96 }
97
Patrick Williams5932cd62023-11-16 11:13:00 -060098 void clear() noexcept
99 {
100 __head_ = nullptr;
101 __tail_ = nullptr;
102 }
103
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500104 [[nodiscard]] _Item* pop_front() noexcept
105 {
Patrick Williams5e7ef082023-01-05 11:08:53 -0600106 STDEXEC_ASSERT(!empty());
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500107 _Item* __item = std::exchange(__head_, __head_->*_Next);
Patrick Williams56252bb2023-02-13 20:13:15 -0600108 // This should test if __head_ == nullptr, but due to a bug in
109 // nvc++'s optimization, `__head_` isn't assigned until later.
110 // Filed as NVBug#3952534.
111 if (__item->*_Next == nullptr)
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500112 {
113 __tail_ = nullptr;
114 }
115 return __item;
116 }
117
118 void push_front(_Item* __item) noexcept
119 {
Patrick Williams5e7ef082023-01-05 11:08:53 -0600120 STDEXEC_ASSERT(__item != nullptr);
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500121 __item->*_Next = __head_;
122 __head_ = __item;
123 if (__tail_ == nullptr)
124 {
125 __tail_ = __item;
126 }
127 }
128
129 void push_back(_Item* __item) noexcept
130 {
Patrick Williams5e7ef082023-01-05 11:08:53 -0600131 STDEXEC_ASSERT(__item != nullptr);
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500132 __item->*_Next = nullptr;
133 if (__tail_ == nullptr)
134 {
135 __head_ = __item;
136 }
137 else
138 {
139 __tail_->*_Next = __item;
140 }
141 __tail_ = __item;
142 }
143
144 void append(__intrusive_queue __other) noexcept
145 {
146 if (__other.empty())
147 return;
148 auto* __other_head = std::exchange(__other.__head_, nullptr);
149 if (empty())
150 {
151 __head_ = __other_head;
152 }
153 else
154 {
155 __tail_->*_Next = __other_head;
156 }
157 __tail_ = std::exchange(__other.__tail_, nullptr);
158 }
159
160 void prepend(__intrusive_queue __other) noexcept
161 {
162 if (__other.empty())
163 return;
164
165 __other.__tail_->*_Next = __head_;
166 __head_ = __other.__head_;
167 if (__tail_ == nullptr)
168 {
169 __tail_ = __other.__tail_;
170 }
171
172 __other.__tail_ = nullptr;
173 __other.__head_ = nullptr;
174 }
175
Patrick Williams5932cd62023-11-16 11:13:00 -0600176 struct iterator
177 {
178 using difference_type = std::ptrdiff_t;
179 using value_type = _Item*;
180
181 _Item* __predecessor_ = nullptr;
182 _Item* __item_ = nullptr;
183
184 iterator() noexcept = default;
185
186 explicit iterator(_Item* __pred, _Item* __item) noexcept :
187 __predecessor_(__pred), __item_(__item)
188 {}
189
190 [[nodiscard]] _Item* operator*() const noexcept
191 {
192 STDEXEC_ASSERT(__item_ != nullptr);
193 return __item_;
194 }
195
196 [[nodiscard]] _Item** operator->() const noexcept
197 {
198 STDEXEC_ASSERT(__item_ != nullptr);
199 return &__item_;
200 }
201
202 iterator& operator++() noexcept
203 {
204 __predecessor_ = __item_;
205 if (__item_)
206 {
207 __item_ = __item_->*_Next;
208 }
209 return *this;
210 }
211
212 iterator operator++(int) noexcept
213 {
214 iterator __result = *this;
215 ++*this;
216 return __result;
217 }
218
219 friend bool operator==(const iterator&,
220 const iterator&) noexcept = default;
221 };
222
223 [[nodiscard]] iterator begin() const noexcept
224 {
225 return iterator(nullptr, __head_);
226 }
227
228 [[nodiscard]] iterator end() const noexcept
229 {
230 return iterator(__tail_, nullptr);
231 }
232
233 void splice(iterator pos, __intrusive_queue& other, iterator first,
234 iterator last) noexcept
235 {
236 if (first == last)
237 {
238 return;
239 }
240 STDEXEC_ASSERT(first.__item_ != nullptr);
241 STDEXEC_ASSERT(last.__predecessor_ != nullptr);
242 if (other.__head_ == first.__item_)
243 {
244 other.__head_ = last.__item_;
245 if (other.__head_ == nullptr)
246 {
247 other.__tail_ = nullptr;
248 }
249 }
250 else
251 {
252 STDEXEC_ASSERT(first.__predecessor_ != nullptr);
253 first.__predecessor_->*_Next = last.__item_;
254 last.__predecessor_->*_Next = pos.__item_;
255 }
256 if (empty())
257 {
258 __head_ = first.__item_;
259 __tail_ = last.__predecessor_;
260 }
261 else
262 {
263 pos.__predecessor_->*_Next = first.__item_;
264 if (pos.__item_ == nullptr)
265 {
266 __tail_ = last.__predecessor_;
267 }
268 }
269 }
270
271 _Item* front() const noexcept
272 {
273 return __head_;
274 }
275
276 _Item* back() const noexcept
277 {
278 return __tail_;
279 }
280
Patrick Williams5b5d15b2022-08-23 09:35:15 -0500281 private:
282 _Item* __head_ = nullptr;
283 _Item* __tail_ = nullptr;
284};
285} // namespace __queue
286
287using __queue::__intrusive_queue;
288
289} // namespace stdexec