mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			295 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
	
		
			6.9 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 "td/utils/logging.h"
 | |
| #include "td/utils/MpscLinkQueue.h"
 | |
| 
 | |
| #include <atomic>
 | |
| #include <memory>
 | |
| #include <new>
 | |
| #include <utility>
 | |
| 
 | |
| namespace td {
 | |
| 
 | |
| namespace detail {
 | |
| class AtomicRefCnt {
 | |
|  public:
 | |
|   explicit AtomicRefCnt(uint64 cnt) : cnt_(cnt) {
 | |
|   }
 | |
|   void inc() {
 | |
|     cnt_.fetch_add(1, std::memory_order_relaxed);
 | |
|   }
 | |
|   bool dec() {
 | |
|     return cnt_.fetch_sub(1, std::memory_order_acq_rel) == 1;
 | |
|   }
 | |
|   uint64 value() const {
 | |
|     return cnt_.load(std::memory_order_relaxed);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   std::atomic<uint64> cnt_{0};
 | |
| };
 | |
| 
 | |
| template <class DataT, class DeleterT>
 | |
| class SharedPtrRaw : public DeleterT, private MpscLinkQueueImpl::Node {
 | |
|  public:
 | |
|   explicit SharedPtrRaw(DeleterT deleter) : DeleterT(std::move(deleter)), ref_cnt_{0}, option_magic_(Magic) {
 | |
|   }
 | |
| 
 | |
|   ~SharedPtrRaw() {
 | |
|     CHECK(use_cnt() == 0);
 | |
|     CHECK(option_magic_ == Magic);
 | |
|   }
 | |
|   template <class... ArgsT>
 | |
|   void init_data(ArgsT &&... args) {
 | |
|     new (&option_data_) DataT(std::forward<ArgsT>(args)...);
 | |
|   }
 | |
|   void destroy_data() {
 | |
|     option_data_.~DataT();
 | |
|     option_magic_ = Magic;
 | |
|   }
 | |
|   uint64 use_cnt() const {
 | |
|     return ref_cnt_.value();
 | |
|   }
 | |
|   void inc() {
 | |
|     ref_cnt_.inc();
 | |
|   }
 | |
|   bool dec() {
 | |
|     return ref_cnt_.dec();
 | |
|   }
 | |
|   DataT &data() {
 | |
|     return option_data_;
 | |
|   }
 | |
|   static SharedPtrRaw *from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
 | |
|     return static_cast<SharedPtrRaw<DataT, DeleterT> *>(node);
 | |
|   }
 | |
|   MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
 | |
|     return static_cast<MpscLinkQueueImpl::Node *>(this);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   AtomicRefCnt ref_cnt_;
 | |
|   enum { Magic = 0x732817a2 };
 | |
|   union {
 | |
|     DataT option_data_;
 | |
|     uint32 option_magic_;
 | |
|   };
 | |
| };
 | |
| 
 | |
| template <class T, class DeleterT = std::default_delete<T>>
 | |
| class SharedPtr {
 | |
|  public:
 | |
|   using Raw = detail::SharedPtrRaw<T, DeleterT>;
 | |
|   struct acquire_t {};
 | |
|   SharedPtr() = default;
 | |
|   ~SharedPtr() {
 | |
|     if (!raw_) {
 | |
|       return;
 | |
|     }
 | |
|     reset();
 | |
|   }
 | |
|   explicit SharedPtr(Raw *raw) : raw_(raw) {
 | |
|     if (raw_) {
 | |
|       raw_->inc();
 | |
|     }
 | |
|   }
 | |
|   SharedPtr(acquire_t, Raw *raw) : raw_(raw) {
 | |
|   }
 | |
|   SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
 | |
|   }
 | |
|   SharedPtr &operator=(const SharedPtr &other) {
 | |
|     if (other.raw_) {
 | |
|       other.raw_->inc();
 | |
|     }
 | |
|     reset(other.raw_);
 | |
|     return *this;
 | |
|   }
 | |
|   SharedPtr(SharedPtr &&other) : raw_(other.raw_) {
 | |
|     other.raw_ = nullptr;
 | |
|   }
 | |
|   SharedPtr &operator=(SharedPtr &&other) {
 | |
|     reset(other.raw_);
 | |
|     other.raw_ = nullptr;
 | |
|     return *this;
 | |
|   }
 | |
|   bool empty() const {
 | |
|     return raw_ == nullptr;
 | |
|   }
 | |
|   explicit operator bool() const {
 | |
|     return !empty();
 | |
|   }
 | |
|   uint64 use_cnt() const {
 | |
|     if (!raw_) {
 | |
|       return 0;
 | |
|     }
 | |
|     return raw_->use_cnt();
 | |
|   }
 | |
|   T &operator*() const {
 | |
|     return raw_->data();
 | |
|   }
 | |
|   T *operator->() const {
 | |
|     return &raw_->data();
 | |
|   }
 | |
| 
 | |
|   Raw *release() {
 | |
|     auto res = raw_;
 | |
|     raw_ = nullptr;
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void reset(Raw *new_raw = nullptr) {
 | |
|     if (raw_ && raw_->dec()) {
 | |
|       raw_->destroy_data();
 | |
|       auto deleter = std::move(static_cast<DeleterT &>(*raw_));
 | |
|       deleter(raw_);
 | |
|     }
 | |
|     raw_ = new_raw;
 | |
|   }
 | |
| 
 | |
|   template <class... ArgsT>
 | |
|   static SharedPtr<T, DeleterT> create(ArgsT &&... args) {
 | |
|     auto raw = make_unique<Raw>(DeleterT());
 | |
|     raw->init_data(std::forward<ArgsT>(args)...);
 | |
|     return SharedPtr<T, DeleterT>(raw.release());
 | |
|   }
 | |
|   template <class D, class... ArgsT>
 | |
|   static SharedPtr<T, DeleterT> create_with_deleter(D &&d, ArgsT &&... args) {
 | |
|     auto raw = make_unique<Raw>(std::forward<D>(d));
 | |
|     raw->init_data(std::forward<ArgsT>(args)...);
 | |
|     return SharedPtr<T, DeleterT>(raw.release());
 | |
|   }
 | |
|   bool operator==(const SharedPtr<T, DeleterT> &other) const {
 | |
|     return raw_ == other.raw_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   Raw *raw_{nullptr};
 | |
| };
 | |
| 
 | |
| }  // namespace detail
 | |
| 
 | |
| template <class DataT>
 | |
| class SharedObjectPool {
 | |
|   class Deleter;
 | |
| 
 | |
|  public:
 | |
|   using Ptr = detail::SharedPtr<DataT, Deleter>;
 | |
| 
 | |
|   SharedObjectPool() = default;
 | |
|   SharedObjectPool(const SharedObjectPool &other) = delete;
 | |
|   SharedObjectPool &operator=(const SharedObjectPool &other) = delete;
 | |
|   SharedObjectPool(SharedObjectPool &&other) = delete;
 | |
|   SharedObjectPool &operator=(SharedObjectPool &&other) = delete;
 | |
|   ~SharedObjectPool() {
 | |
|     free_queue_.pop_all(free_queue_reader_);
 | |
|     size_t free_cnt = 0;
 | |
|     while (free_queue_reader_.read()) {
 | |
|       free_cnt++;
 | |
|     }
 | |
|     LOG_CHECK(free_cnt == allocated_.size()) << free_cnt << " " << allocated_.size();
 | |
|   }
 | |
| 
 | |
|   template <class... ArgsT>
 | |
|   Ptr alloc(ArgsT &&... args) {
 | |
|     auto *raw = alloc_raw();
 | |
|     raw->init_data(std::forward<ArgsT>(args)...);
 | |
|     return Ptr(raw);
 | |
|   }
 | |
|   size_t total_size() const {
 | |
|     return allocated_.size();
 | |
|   }
 | |
|   uint64 calc_free_size() {
 | |
|     free_queue_.pop_all(free_queue_reader_);
 | |
|     return free_queue_reader_.calc_size();
 | |
|   }
 | |
| 
 | |
|   //non thread safe
 | |
|   template <class F>
 | |
|   void for_each(F &&f) {
 | |
|     for (auto &raw : allocated_) {
 | |
|       if (raw->use_cnt() > 0) {
 | |
|         f(raw->data());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   using Raw = typename Ptr::Raw;
 | |
|   Raw *alloc_raw() {
 | |
|     free_queue_.pop_all(free_queue_reader_);
 | |
|     auto *raw = free_queue_reader_.read().get();
 | |
|     if (raw) {
 | |
|       return raw;
 | |
|     }
 | |
|     allocated_.push_back(make_unique<Raw>(deleter()));
 | |
|     return allocated_.back().get();
 | |
|   }
 | |
| 
 | |
|   void free_raw(Raw *raw) {
 | |
|     free_queue_.push(Node{raw});
 | |
|   }
 | |
| 
 | |
|   class Node {
 | |
|    public:
 | |
|     Node() = default;
 | |
|     explicit Node(Raw *raw) : raw_(raw) {
 | |
|     }
 | |
| 
 | |
|     MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
 | |
|       return raw_->to_mpsc_link_queue_node();
 | |
|     }
 | |
|     static Node from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
 | |
|       return Node{Raw::from_mpsc_link_queue_node(node)};
 | |
|     }
 | |
|     Raw *get() const {
 | |
|       return raw_;
 | |
|     }
 | |
|     explicit operator bool() const {
 | |
|       return raw_ != nullptr;
 | |
|     }
 | |
| 
 | |
|    private:
 | |
|     Raw *raw_{nullptr};
 | |
|   };
 | |
| 
 | |
|   class Deleter {
 | |
|    public:
 | |
|     explicit Deleter(SharedObjectPool<DataT> *pool) : pool_(pool) {
 | |
|     }
 | |
|     void operator()(Raw *raw) {
 | |
|       pool_->free_raw(raw);
 | |
|     };
 | |
| 
 | |
|    private:
 | |
|     SharedObjectPool<DataT> *pool_;
 | |
|   };
 | |
|   friend class Deleter;
 | |
| 
 | |
|   Deleter deleter() {
 | |
|     return Deleter(this);
 | |
|   }
 | |
| 
 | |
|   std::vector<unique_ptr<Raw>> allocated_;
 | |
|   MpscLinkQueue<Node> free_queue_;
 | |
|   typename MpscLinkQueue<Node>::Reader free_queue_reader_;
 | |
| };
 | |
| 
 | |
| }  // namespace td
 |