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