mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
261
tdutils/td/utils/ObjectPool.h
Normal file
261
tdutils/td/utils/ObjectPool.h
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
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-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace td {
|
||||
// It is draft object pool implementaion
|
||||
//
|
||||
// Compared with std::shared_ptr:
|
||||
// + WeakPtr are much faster. Just pointer copy. No barriers, no atomics.
|
||||
// - We can't destroy object, because we don't know if it is pointed to by some weak pointer
|
||||
//
|
||||
template <class DataT>
|
||||
class ObjectPool {
|
||||
struct Storage;
|
||||
|
||||
public:
|
||||
class WeakPtr {
|
||||
public:
|
||||
WeakPtr() : generation_(-1), storage_(nullptr) {
|
||||
}
|
||||
WeakPtr(int32 generation, Storage *storage) : generation_(generation), storage_(storage) {
|
||||
}
|
||||
|
||||
DataT &operator*() const {
|
||||
return storage_->data;
|
||||
}
|
||||
|
||||
DataT *operator->() const {
|
||||
return &**this;
|
||||
}
|
||||
|
||||
// Pattern of usage: 1. Read an object 2. Check if read was valid via is_alive
|
||||
//
|
||||
// It is not very usual case of acquire/release use.
|
||||
// Instead of publishing an object via some flag we do the opposite.
|
||||
// We publish new generation via destruction of the data.
|
||||
// In usual case if we see a flag then we are able to use an object.
|
||||
// In our case if we have used an object and it is already invalid, then generation will mismatch
|
||||
bool is_alive() const {
|
||||
if (!storage_) {
|
||||
return false;
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
return generation_ == storage_->generation.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Used for ActorId
|
||||
bool is_alive_unsafe() const {
|
||||
if (!storage_) {
|
||||
return false;
|
||||
}
|
||||
return generation_ == storage_->generation.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return storage_ == nullptr;
|
||||
}
|
||||
void clear() {
|
||||
generation_ = -1;
|
||||
storage_ = nullptr;
|
||||
}
|
||||
int32 generation() {
|
||||
return generation_;
|
||||
}
|
||||
|
||||
private:
|
||||
int32 generation_;
|
||||
Storage *storage_;
|
||||
};
|
||||
|
||||
class OwnerPtr {
|
||||
public:
|
||||
OwnerPtr() = default;
|
||||
OwnerPtr(const OwnerPtr &) = delete;
|
||||
OwnerPtr &operator=(const OwnerPtr &) = delete;
|
||||
OwnerPtr(OwnerPtr &&other) : storage_(other.storage_), parent_(other.parent_) {
|
||||
other.storage_ = nullptr;
|
||||
other.parent_ = nullptr;
|
||||
}
|
||||
OwnerPtr &operator=(OwnerPtr &&other) {
|
||||
if (this != &other) {
|
||||
storage_ = other.storage_;
|
||||
parent_ = other.parent_;
|
||||
other.storage_ = nullptr;
|
||||
other.parent_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~OwnerPtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
DataT *get() {
|
||||
return &storage_->data;
|
||||
}
|
||||
DataT &operator*() {
|
||||
return *get();
|
||||
}
|
||||
DataT *operator->() {
|
||||
return get();
|
||||
}
|
||||
|
||||
const DataT *get() const {
|
||||
return &storage_->data;
|
||||
}
|
||||
const DataT &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
const DataT *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
WeakPtr get_weak() {
|
||||
return WeakPtr(storage_->generation.load(std::memory_order_relaxed), storage_);
|
||||
}
|
||||
int32 generation() {
|
||||
return storage_->generation.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Storage *release() {
|
||||
auto result = storage_;
|
||||
storage_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return storage_ == nullptr;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (storage_ != nullptr) {
|
||||
// for crazy cases when data owns owner pointer to itself.
|
||||
auto tmp = storage_;
|
||||
storage_ = nullptr;
|
||||
parent_->release(OwnerPtr(tmp, parent_));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ObjectPool;
|
||||
OwnerPtr(Storage *storage, ObjectPool<DataT> *parent) : storage_(storage), parent_(parent) {
|
||||
}
|
||||
Storage *storage_ = nullptr;
|
||||
ObjectPool<DataT> *parent_ = nullptr;
|
||||
};
|
||||
|
||||
template <class... ArgsT>
|
||||
OwnerPtr create(ArgsT &&... args) {
|
||||
Storage *storage = get_storage();
|
||||
storage->init_data(std::forward<ArgsT>(args)...);
|
||||
return OwnerPtr(storage, this);
|
||||
}
|
||||
|
||||
OwnerPtr create_empty() {
|
||||
Storage *storage = get_storage();
|
||||
return OwnerPtr(storage, this);
|
||||
}
|
||||
|
||||
void set_check_empty(bool flag) {
|
||||
check_empty_flag_ = flag;
|
||||
}
|
||||
|
||||
void release(OwnerPtr &&owner_ptr) {
|
||||
Storage *storage = owner_ptr.release();
|
||||
storage->destroy_data();
|
||||
release_storage(storage);
|
||||
}
|
||||
|
||||
ObjectPool() = default;
|
||||
ObjectPool(const ObjectPool &) = delete;
|
||||
ObjectPool &operator=(const ObjectPool &) = delete;
|
||||
ObjectPool(ObjectPool &&other) = delete;
|
||||
ObjectPool &operator=(ObjectPool &&other) = delete;
|
||||
~ObjectPool() {
|
||||
while (head_.load()) {
|
||||
auto to_delete = head_.load();
|
||||
head_ = to_delete->next;
|
||||
delete to_delete;
|
||||
storage_count_--;
|
||||
}
|
||||
LOG_CHECK(storage_count_.load() == 0) << storage_count_.load();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Storage {
|
||||
// union {
|
||||
DataT data;
|
||||
//};
|
||||
Storage *next = nullptr;
|
||||
std::atomic<int32> generation{1};
|
||||
|
||||
template <class... ArgsT>
|
||||
void init_data(ArgsT &&... args) {
|
||||
// new (&data) DataT(std::forward<ArgsT>(args)...);
|
||||
data = DataT(std::forward<ArgsT>(args)...);
|
||||
}
|
||||
void destroy_data() {
|
||||
generation.fetch_add(1, std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
data.clear();
|
||||
}
|
||||
};
|
||||
|
||||
std::atomic<int32> storage_count_{0};
|
||||
std::atomic<Storage *> head_{static_cast<Storage *>(nullptr)};
|
||||
bool check_empty_flag_ = false;
|
||||
|
||||
// TODO(perf): allocation Storages in chunks? Anyway we won't be able to release them.
|
||||
// TODO(perf): memory order
|
||||
// TODO(perf): use another non lockfree list for release on the same thread
|
||||
// only one thread, so no aba problem
|
||||
Storage *get_storage() {
|
||||
if (head_.load() == nullptr) {
|
||||
storage_count_++;
|
||||
return new Storage();
|
||||
}
|
||||
Storage *res;
|
||||
while (true) {
|
||||
res = head_.load();
|
||||
auto *next = res->next;
|
||||
if (head_.compare_exchange_weak(res, next)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// release can be called from other thread
|
||||
void release_storage(Storage *storage) {
|
||||
while (true) {
|
||||
auto *save_head = head_.load();
|
||||
storage->next = save_head;
|
||||
if (head_.compare_exchange_weak(save_head, storage)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace td
|
Loading…
Add table
Add a link
Reference in a new issue