mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			185 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #pragma once
 | |
| 
 | |
| #include "td/utils/common.h"
 | |
| 
 | |
| #include <atomic>
 | |
| 
 | |
| namespace td {
 | |
| //NB: holder of the queue holds all responsibility of freeing its nodes
 | |
| class MpscLinkQueueImpl {
 | |
|  public:
 | |
|   class Node;
 | |
|   class Reader;
 | |
| 
 | |
|   void push(Node *node) {
 | |
|     node->next_ = head_.load(std::memory_order_relaxed);
 | |
|     while (!head_.compare_exchange_strong(node->next_, node, std::memory_order_release, std::memory_order_relaxed)) {
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void push_unsafe(Node *node) {
 | |
|     node->next_ = head_.load(std::memory_order_relaxed);
 | |
|     head_.store(node, std::memory_order_relaxed);
 | |
|   }
 | |
| 
 | |
|   void pop_all(Reader &reader) {
 | |
|     return reader.add(head_.exchange(nullptr, std::memory_order_acquire));
 | |
|   }
 | |
| 
 | |
|   void pop_all_unsafe(Reader &reader) {
 | |
|     return reader.add(head_.exchange(nullptr, std::memory_order_relaxed));
 | |
|   }
 | |
| 
 | |
|   class Node {
 | |
|     friend class MpscLinkQueueImpl;
 | |
|     Node *next_{nullptr};
 | |
|   };
 | |
| 
 | |
|   class Reader {
 | |
|    public:
 | |
|     Node *read() {
 | |
|       auto old_head = head_;
 | |
|       if (head_) {
 | |
|         head_ = head_->next_;
 | |
|       }
 | |
|       return old_head;
 | |
|     }
 | |
|     void delay(Node *node) {
 | |
|       node->next_ = head_;
 | |
|       if (!head_) {
 | |
|         tail_ = node;
 | |
|       }
 | |
|       head_ = node;
 | |
|     }
 | |
|     size_t calc_size() const {
 | |
|       size_t res = 0;
 | |
|       for (auto it = head_; it != nullptr; it = it->next_, res++) {
 | |
|       }
 | |
|       return res;
 | |
|     }
 | |
| 
 | |
|    private:
 | |
|     friend class MpscLinkQueueImpl;
 | |
|     void add(Node *node) {
 | |
|       if (node == nullptr) {
 | |
|         return;
 | |
|       }
 | |
|       // Reverse list
 | |
|       Node *tail = node;
 | |
|       Node *head = nullptr;
 | |
|       while (node) {
 | |
|         auto next = node->next_;
 | |
|         node->next_ = head;
 | |
|         head = node;
 | |
|         node = next;
 | |
|       }
 | |
|       if (head_ == nullptr) {
 | |
|         head_ = head;
 | |
|       } else {
 | |
|         tail_->next_ = head;
 | |
|       }
 | |
|       tail_ = tail;
 | |
|     }
 | |
|     Node *head_{nullptr};
 | |
|     Node *tail_{nullptr};
 | |
|   };
 | |
| 
 | |
|  private:
 | |
|   std::atomic<Node *> head_{nullptr};
 | |
| };
 | |
| 
 | |
| // Uses MpscLinkQueueImpl.
 | |
| // Node should have to_mpsc_link_queue_node and from_mpsc_link_queue_node functions
 | |
| template <class Node>
 | |
| class MpscLinkQueue {
 | |
|  public:
 | |
|   void push(Node node) {
 | |
|     impl_.push(node.to_mpsc_link_queue_node());
 | |
|   }
 | |
|   void push_unsafe(Node node) {
 | |
|     impl_.push_unsafe(node.to_mpsc_link_queue_node());
 | |
|   }
 | |
|   class Reader {
 | |
|    public:
 | |
|     ~Reader() {
 | |
|       CHECK(!read());
 | |
|     }
 | |
|     Node read() {
 | |
|       auto node = impl_.read();
 | |
|       if (!node) {
 | |
|         return {};
 | |
|       }
 | |
|       return Node::from_mpsc_link_queue_node(node);
 | |
|     }
 | |
|     void delay(Node node) {
 | |
|       impl_.delay(node.to_mpsc_link_queue_node());
 | |
|     }
 | |
|     size_t calc_size() const {
 | |
|       return impl_.calc_size();
 | |
|     }
 | |
| 
 | |
|    private:
 | |
|     friend class MpscLinkQueue;
 | |
| 
 | |
|     MpscLinkQueueImpl::Reader impl_;
 | |
|     MpscLinkQueueImpl::Reader &impl() {
 | |
|       return impl_;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   void pop_all(Reader &reader) {
 | |
|     return impl_.pop_all(reader.impl());
 | |
|   }
 | |
|   void pop_all_unsafe(Reader &reader) {
 | |
|     return impl_.pop_all_unsafe(reader.impl());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   MpscLinkQueueImpl impl_;
 | |
| };
 | |
| 
 | |
| template <class Value>
 | |
| class MpscLinkQueueUniquePtrNode {
 | |
|  public:
 | |
|   MpscLinkQueueUniquePtrNode() = default;
 | |
|   explicit MpscLinkQueueUniquePtrNode(unique_ptr<Value> ptr) : ptr_(std::move(ptr)) {
 | |
|   }
 | |
| 
 | |
|   MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
 | |
|     return ptr_.release()->to_mpsc_link_queue_node();
 | |
|   }
 | |
|   static MpscLinkQueueUniquePtrNode<Value> from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
 | |
|     return MpscLinkQueueUniquePtrNode<Value>(unique_ptr<Value>(Value::from_mpsc_link_queue_node(node)));
 | |
|   }
 | |
| 
 | |
|   explicit operator bool() {
 | |
|     return ptr_ != nullptr;
 | |
|   }
 | |
| 
 | |
|   Value &value() {
 | |
|     return *ptr_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   unique_ptr<Value> ptr_;
 | |
| };
 | |
| 
 | |
| }  // namespace td
 |