397 lines
10 KiB
C++
397 lines
10 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
It 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 General Public License for more details.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#pragma once
|
|
|
|
#include <mutex>
|
|
#include <gsl/gsl_assert>
|
|
#include <rpl/lifetime.h>
|
|
|
|
namespace rpl {
|
|
namespace details {
|
|
|
|
template <typename Arg>
|
|
const Arg &const_ref_val();
|
|
|
|
template <
|
|
typename Func,
|
|
typename Arg,
|
|
typename = decltype(std::declval<Func>()(const_ref_val<Arg>()))>
|
|
void const_ref_call_helper(Func &handler, const Arg &value, int) {
|
|
handler(value);
|
|
}
|
|
|
|
template <
|
|
typename Func,
|
|
typename Arg>
|
|
void const_ref_call_helper(Func &handler, const Arg &value, double) {
|
|
auto copy = value;
|
|
handler(std::move(copy));
|
|
}
|
|
|
|
} // namespace details
|
|
|
|
struct no_value {
|
|
no_value() = delete;
|
|
};
|
|
|
|
struct no_error {
|
|
no_error() = delete;
|
|
};
|
|
|
|
struct empty_value {
|
|
};
|
|
|
|
struct empty_error {
|
|
};
|
|
|
|
template <typename Value = empty_value, typename Error = no_error>
|
|
class consumer {
|
|
public:
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename = decltype(std::declval<OnNext>()(std::declval<Value>())),
|
|
typename = decltype(std::declval<OnError>()(std::declval<Error>())),
|
|
typename = decltype(std::declval<OnDone>()())>
|
|
consumer(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done);
|
|
|
|
bool put_next(Value &&value) const;
|
|
bool put_next_copy(const Value &value) const;
|
|
bool put_next_forward(Value &&value) const {
|
|
return put_next(std::move(value));
|
|
}
|
|
bool put_next_forward(const Value &value) const {
|
|
return put_next_copy(value);
|
|
}
|
|
void put_error(Error &&error) const;
|
|
void put_error_copy(const Error &error) const;
|
|
void put_error_forward(Error &&error) const {
|
|
return put_error(std::move(error));
|
|
}
|
|
void put_error_forward(const Error &error) const {
|
|
return put_error_copy(error);
|
|
}
|
|
void put_done() const;
|
|
|
|
void add_lifetime(lifetime &&lifetime) const;
|
|
|
|
template <typename Type, typename... Args>
|
|
Type *make_state(Args&& ...args) const;
|
|
|
|
void terminate() const;
|
|
|
|
bool operator==(const consumer &other) const {
|
|
return _instance == other._instance;
|
|
}
|
|
bool operator!=(const consumer &other) const {
|
|
return !(*this == other);
|
|
}
|
|
bool operator<(const consumer &other) const {
|
|
return _instance < other._instance;
|
|
}
|
|
bool operator>(const consumer &other) const {
|
|
return other < *this;
|
|
}
|
|
bool operator<=(const consumer &other) const {
|
|
return !(other < *this);
|
|
}
|
|
bool operator>=(const consumer &other) const {
|
|
return !(*this < other);
|
|
}
|
|
|
|
private:
|
|
class abstract_consumer_instance;
|
|
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
class consumer_instance;
|
|
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
std::shared_ptr<abstract_consumer_instance> ConstructInstance(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done);
|
|
|
|
mutable std::shared_ptr<abstract_consumer_instance> _instance;
|
|
|
|
};
|
|
|
|
|
|
template <typename Value, typename Error>
|
|
class consumer<Value, Error>::abstract_consumer_instance {
|
|
public:
|
|
virtual bool put_next(Value &&value) = 0;
|
|
virtual bool put_next_copy(const Value &value) = 0;
|
|
virtual void put_error(Error &&error) = 0;
|
|
virtual void put_error_copy(const Error &error) = 0;
|
|
virtual void put_done() = 0;
|
|
|
|
void add_lifetime(lifetime &&lifetime);
|
|
|
|
template <typename Type, typename... Args>
|
|
Type *make_state(Args&& ...args);
|
|
|
|
void terminate();
|
|
|
|
protected:
|
|
lifetime _lifetime;
|
|
bool _terminated = false;
|
|
std::mutex _mutex;
|
|
|
|
};
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
class consumer<Value, Error>::consumer_instance
|
|
: public consumer<Value, Error>::abstract_consumer_instance {
|
|
public:
|
|
template <typename OnNextImpl, typename OnErrorImpl, typename OnDoneImpl>
|
|
consumer_instance(
|
|
OnNextImpl &&next,
|
|
OnErrorImpl &&error,
|
|
OnDoneImpl &&done)
|
|
: _next(std::forward<OnNextImpl>(next))
|
|
, _error(std::forward<OnErrorImpl>(error))
|
|
, _done(std::forward<OnDoneImpl>(done)) {
|
|
}
|
|
|
|
bool put_next(Value &&value) override;
|
|
bool put_next_copy(const Value &value) override;
|
|
void put_error(Error &&error) override;
|
|
void put_error_copy(const Error &error) override;
|
|
void put_done() override;
|
|
|
|
private:
|
|
OnNext _next;
|
|
OnError _error;
|
|
OnDone _done;
|
|
|
|
};
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
std::shared_ptr<typename consumer<Value, Error>::abstract_consumer_instance>
|
|
consumer<Value, Error>::ConstructInstance(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) {
|
|
return std::make_shared<consumer_instance<
|
|
std::decay_t<OnNext>,
|
|
std::decay_t<OnError>,
|
|
std::decay_t<OnDone>>>(
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done));
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename,
|
|
typename,
|
|
typename>
|
|
consumer<Value, Error>::consumer(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) : _instance(ConstructInstance(
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done))) {
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
bool consumer<Value, Error>::put_next(Value &&value) const {
|
|
if (_instance) {
|
|
if (_instance->put_next(std::move(value))) {
|
|
return true;
|
|
}
|
|
_instance = nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
bool consumer<Value, Error>::put_next_copy(const Value &value) const {
|
|
if (_instance) {
|
|
if (_instance->put_next_copy(value)) {
|
|
return true;
|
|
}
|
|
_instance = nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::put_error(Error &&error) const {
|
|
if (_instance) {
|
|
std::exchange(_instance, nullptr)->put_error(std::move(error));
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::put_error_copy(const Error &error) const {
|
|
if (_instance) {
|
|
std::exchange(_instance, nullptr)->put_error_copy(error);
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::put_done() const {
|
|
if (_instance) {
|
|
std::exchange(_instance, nullptr)->put_done();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::add_lifetime(lifetime &&lifetime) const {
|
|
if (_instance) {
|
|
_instance->add_lifetime(std::move(lifetime));
|
|
} else {
|
|
lifetime.destroy();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename Type, typename... Args>
|
|
Type *consumer<Value, Error>::make_state(Args&& ...args) const {
|
|
Expects(_instance != nullptr);
|
|
return _instance->template make_state<Type>(std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::terminate() const {
|
|
if (_instance) {
|
|
std::exchange(_instance, nullptr)->terminate();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::abstract_consumer_instance::add_lifetime(
|
|
lifetime &&lifetime) {
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
if (_terminated) {
|
|
lock.unlock();
|
|
|
|
lifetime.destroy();
|
|
} else {
|
|
_lifetime.add(std::move(lifetime));
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename Type, typename... Args>
|
|
Type *consumer<Value, Error>::abstract_consumer_instance::make_state(
|
|
Args&& ...args) {
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
Expects(!_terminated);
|
|
return _lifetime.template make_state<Type>(std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
void consumer<Value, Error>::abstract_consumer_instance::terminate() {
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
if (!_terminated) {
|
|
_terminated = true;
|
|
auto handler = std::exchange(_lifetime, lifetime());
|
|
lock.unlock();
|
|
|
|
handler.destroy();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_next(
|
|
Value &&value) {
|
|
std::unique_lock<std::mutex> lock(this->_mutex);
|
|
if (this->_terminated) {
|
|
return false;
|
|
}
|
|
auto handler = this->_next;
|
|
lock.unlock();
|
|
|
|
handler(std::move(value));
|
|
return true;
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_next_copy(
|
|
const Value &value) {
|
|
std::unique_lock<std::mutex> lock(this->_mutex);
|
|
if (this->_terminated) {
|
|
return false;
|
|
}
|
|
auto handler = this->_next;
|
|
lock.unlock();
|
|
|
|
details::const_ref_call_helper(handler, value, 0);
|
|
return true;
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_error(
|
|
Error &&error) {
|
|
std::unique_lock<std::mutex> lock(this->_mutex);
|
|
if (!this->_terminated) {
|
|
auto handler = std::move(this->_error);
|
|
lock.unlock();
|
|
|
|
handler(std::move(error));
|
|
this->terminate();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_error_copy(
|
|
const Error &error) {
|
|
std::unique_lock<std::mutex> lock(this->_mutex);
|
|
if (!this->_terminated) {
|
|
auto handler = std::move(this->_error);
|
|
lock.unlock();
|
|
|
|
details::const_ref_call_helper(handler, error, 0);
|
|
this->terminate();
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_done() {
|
|
std::unique_lock<std::mutex> lock(this->_mutex);
|
|
if (!this->_terminated) {
|
|
auto handler = std::move(this->_done);
|
|
lock.unlock();
|
|
|
|
handler();
|
|
this->terminate();
|
|
}
|
|
}
|
|
|
|
} // namespace rpl
|