tdlight/tdutils/td/utils/TsList.h
levlam 23534d218f Use static mutext in TsList.
GitOrigin-RevId: 00bd863b3d5499d463e838771fe5e4bc9b16a31e
2020-07-21 02:05:15 +03:00

206 lines
4.0 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
#include "td/utils/List.h"
#include <mutex>
namespace td {
template <class DataT>
class TsList;
template <class DataT>
class TsListNode : protected ListNode {
public:
TsListNode() {
clear();
}
explicit TsListNode(DataT &&data) : data_(std::move(data)) {
clear();
}
~TsListNode() {
remove();
}
std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT;
TsListNode(const TsListNode &) = delete;
TsListNode &operator=(const TsListNode &) = delete;
TsListNode(TsListNode &&other) {
other.validate();
if (other.empty()) {
data_ = std::move(other.data_);
clear();
} else {
auto guard = other.lock();
init_from(std::move(other));
}
validate();
other.validate();
}
TsListNode &operator=(TsListNode &&other) {
validate();
if (this == &other) {
return *this;
}
other.validate();
remove();
if (other.empty()) {
data_ = std::move(other.data_);
} else {
auto guard = other.lock();
init_from(std::move(other));
}
validate();
other.validate();
return *this;
}
void validate() {
CHECK(empty() || !ListNode::empty() || is_root);
CHECK(!empty() || ListNode::empty());
}
void remove() {
validate();
if (is_root) {
CHECK(ListNode::empty());
return;
}
if (empty()) {
CHECK(ListNode::empty());
return;
}
{
auto guard = lock();
ListNode::remove();
if (!is_root) {
parent = nullptr;
}
}
validate();
}
void put(TsListNode *other) {
validate();
other->validate();
DCHECK(other->empty());
DCHECK(!empty());
DCHECK(!other->is_root);
{
auto guard = lock();
ListNode::put(other);
other->parent = parent;
}
validate();
other->validate();
}
void put_back(TsListNode *other) {
DCHECK(other->empty());
DCHECK(!empty());
DCHECK(!other->is_root);
auto guard = lock();
ListNode::put_back(other);
other->parent = parent;
}
bool empty() const {
return parent == nullptr;
}
TsListNode *get_next() {
return static_cast<TsListNode *>(next);
}
TsListNode *get_prev() {
return static_cast<TsListNode *>(prev);
}
DataT &get_data_unsafe() {
return data_;
}
private:
TsList<DataT> *parent;
bool is_root{false};
DataT data_;
friend class TsList<DataT>;
void clear() {
ListNode::clear();
if (!is_root) {
parent = nullptr;
}
}
void init_from(TsListNode &&other) {
ListNode::init_from(std::move(other));
parent = other.parent;
other.parent = nullptr;
data_ = std::move(other.data_);
}
};
template <class DataT>
class TsList : public TsListNode<DataT> {
public:
TsList() {
this->parent = this;
this->is_root = true;
}
TsList(const TsList &) = delete;
TsList &operator=(const TsList &) = delete;
TsList(TsList &&) = delete;
TsList &operator=(TsList &&) = delete;
~TsList() {
auto guard = lock();
while (true) {
auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
if (!res) {
break;
}
res->parent = nullptr;
}
this->parent = nullptr;
}
static std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT {
static std::mutex mutex;
return std::unique_lock<std::mutex>(mutex);
}
TsListNode<DataT> *begin() {
return this->get_next();
}
TsListNode<DataT> *end() {
return this;
}
TsListNode<DataT> *get() {
auto guard = lock();
auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
if (res) {
res->parent = nullptr;
}
return res;
}
};
template <class DataT>
std::unique_lock<std::mutex> TsListNode<DataT>::lock() {
CHECK(parent != nullptr);
return parent->lock();
}
} // namespace td