xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2021-2022 Facebook, Inc. and its affiliates
3  * Copyright (c) 2021-2024 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 
19 #include "__config.hpp"
20 
21 #include <cstddef>
22 #include <cassert>
23 #include <utility>
24 
25 namespace stdexec {
26   namespace __queue {
27     template <auto _Next>
28     class __intrusive_queue;
29 
30     template <class _Item, _Item* _Item::* _Next>
31     class __intrusive_queue<_Next> {
32      public:
33       __intrusive_queue() noexcept = default;
34 
__intrusive_queue(__intrusive_queue && __other)35       __intrusive_queue(__intrusive_queue&& __other) noexcept
36         : __head_(std::exchange(__other.__head_, nullptr))
37         , __tail_(std::exchange(__other.__tail_, nullptr)) {
38       }
39 
__intrusive_queue(_Item * __head,_Item * __tail)40       __intrusive_queue(_Item* __head, _Item* __tail) noexcept
41         : __head_(__head)
42         , __tail_(__tail) {
43       }
44 
operator =(__intrusive_queue __other)45       auto operator=(__intrusive_queue __other) noexcept -> __intrusive_queue& {
46         std::swap(__head_, __other.__head_);
47         std::swap(__tail_, __other.__tail_);
48         return *this;
49       }
50 
~__intrusive_queue()51       ~__intrusive_queue() {
52         STDEXEC_ASSERT(empty());
53       }
54 
make_reversed(_Item * __list)55       static auto make_reversed(_Item* __list) noexcept -> __intrusive_queue {
56         _Item* __new_head = nullptr;
57         _Item* __new_tail = __list;
58         while (__list != nullptr) {
59           _Item* __next = __list->*_Next;
60           __list->*_Next = __new_head;
61           __new_head = __list;
62           __list = __next;
63         }
64 
65         __intrusive_queue __result;
66         __result.__head_ = __new_head;
67         __result.__tail_ = __new_tail;
68         return __result;
69       }
70 
make(_Item * __list)71       static auto make(_Item* __list) noexcept -> __intrusive_queue {
72         __intrusive_queue __result{};
73         __result.__head_ = __list;
74         __result.__tail_ = __list;
75         if (__list == nullptr) {
76           return __result;
77         }
78         while (__result.__tail_->*_Next != nullptr) {
79           __result.__tail_ = __result.__tail_->*_Next;
80         }
81         return __result;
82       }
83 
84       [[nodiscard]]
empty() const85       auto empty() const noexcept -> bool {
86         return __head_ == nullptr;
87       }
88 
clear()89       void clear() noexcept {
90         __head_ = nullptr;
91         __tail_ = nullptr;
92       }
93 
94       [[nodiscard]]
pop_front()95       auto pop_front() noexcept -> _Item* {
96         STDEXEC_ASSERT(!empty());
97         _Item* __item = std::exchange(__head_, __head_->*_Next);
98         // This should test if __head_ == nullptr, but due to a bug in
99         // nvc++'s optimization, `__head_` isn't assigned until later.
100         // Filed as NVBug#3952534.
101         if (__item->*_Next == nullptr) {
102           __tail_ = nullptr;
103         }
104         return __item;
105       }
106 
push_front(_Item * __item)107       void push_front(_Item* __item) noexcept {
108         STDEXEC_ASSERT(__item != nullptr);
109         __item->*_Next = __head_;
110         __head_ = __item;
111         if (__tail_ == nullptr) {
112           __tail_ = __item;
113         }
114       }
115 
push_back(_Item * __item)116       void push_back(_Item* __item) noexcept {
117         STDEXEC_ASSERT(__item != nullptr);
118         __item->*_Next = nullptr;
119         if (__tail_ == nullptr) {
120           __head_ = __item;
121         } else {
122           __tail_->*_Next = __item;
123         }
124         __tail_ = __item;
125       }
126 
append(__intrusive_queue __other)127       void append(__intrusive_queue __other) noexcept {
128         if (__other.empty())
129           return;
130         auto* __other_head = std::exchange(__other.__head_, nullptr);
131         if (empty()) {
132           __head_ = __other_head;
133         } else {
134           __tail_->*_Next = __other_head;
135         }
136         __tail_ = std::exchange(__other.__tail_, nullptr);
137       }
138 
prepend(__intrusive_queue __other)139       void prepend(__intrusive_queue __other) noexcept {
140         if (__other.empty())
141           return;
142 
143         __other.__tail_->*_Next = __head_;
144         __head_ = __other.__head_;
145         if (__tail_ == nullptr) {
146           __tail_ = __other.__tail_;
147         }
148 
149         __other.__tail_ = nullptr;
150         __other.__head_ = nullptr;
151       }
152 
153       struct iterator {
154         using difference_type = std::ptrdiff_t;
155         using value_type = _Item*;
156 
157         _Item* __predecessor_ = nullptr;
158         _Item* __item_ = nullptr;
159 
160         iterator() noexcept = default;
161 
iteratorstdexec::__queue::__intrusive_queue::iterator162         explicit iterator(_Item* __pred, _Item* __item) noexcept
163           : __predecessor_(__pred)
164           , __item_(__item) {
165         }
166 
167         [[nodiscard]]
operator *stdexec::__queue::__intrusive_queue::iterator168         auto operator*() const noexcept -> _Item* {
169           STDEXEC_ASSERT(__item_ != nullptr);
170           return __item_;
171         }
172 
173         [[nodiscard]]
operator ->stdexec::__queue::__intrusive_queue::iterator174         auto operator->() const noexcept -> _Item** {
175           STDEXEC_ASSERT(__item_ != nullptr);
176           return &__item_;
177         }
178 
operator ++stdexec::__queue::__intrusive_queue::iterator179         auto operator++() noexcept -> iterator& {
180           __predecessor_ = __item_;
181           if (__item_) {
182             __item_ = __item_->*_Next;
183           }
184           return *this;
185         }
186 
operator ++stdexec::__queue::__intrusive_queue::iterator187         auto operator++(int) noexcept -> iterator {
188           iterator __result = *this;
189           ++*this;
190           return __result;
191         }
192 
193         friend auto operator==(const iterator&, const iterator&) noexcept -> bool = default;
194       };
195 
196       [[nodiscard]]
begin() const197       auto begin() const noexcept -> iterator {
198         return iterator(nullptr, __head_);
199       }
200 
201       [[nodiscard]]
end() const202       auto end() const noexcept -> iterator {
203         return iterator(__tail_, nullptr);
204       }
205 
splice(iterator pos,__intrusive_queue & other,iterator first,iterator last)206       void splice(iterator pos, __intrusive_queue& other, iterator first, iterator last) noexcept {
207         if (first == last) {
208           return;
209         }
210         STDEXEC_ASSERT(first.__item_ != nullptr);
211         STDEXEC_ASSERT(last.__predecessor_ != nullptr);
212         if (other.__head_ == first.__item_) {
213           other.__head_ = last.__item_;
214           if (other.__head_ == nullptr) {
215             other.__tail_ = nullptr;
216           }
217         } else {
218           STDEXEC_ASSERT(first.__predecessor_ != nullptr);
219           first.__predecessor_->*_Next = last.__item_;
220           last.__predecessor_->*_Next = pos.__item_;
221         }
222         if (empty()) {
223           __head_ = first.__item_;
224           __tail_ = last.__predecessor_;
225         } else {
226           pos.__predecessor_->*_Next = first.__item_;
227           if (pos.__item_ == nullptr) {
228             __tail_ = last.__predecessor_;
229           }
230         }
231       }
232 
front() const233       auto front() const noexcept -> _Item* {
234         return __head_;
235       }
236 
back() const237       auto back() const noexcept -> _Item* {
238         return __tail_;
239       }
240 
241      private:
242       _Item* __head_ = nullptr;
243       _Item* __tail_ = nullptr;
244     };
245   } // namespace __queue
246 
247   using __queue::__intrusive_queue;
248 
249 } // namespace stdexec
250