480 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			480 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include <rpl/details/callable.h>
 | 
						|
#include "base/bytes.h"
 | 
						|
#include "base/weak_ptr.h"
 | 
						|
#include "base/flat_map.h"
 | 
						|
#include "mtproto/core_types.h"
 | 
						|
 | 
						|
#ifndef _DEBUG
 | 
						|
#define MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
#endif // !_DEBUG
 | 
						|
 | 
						|
class RPCError;
 | 
						|
 | 
						|
namespace MTP {
 | 
						|
 | 
						|
class Instance;
 | 
						|
 | 
						|
class ConcurrentSender : public base::has_weak_ptr {
 | 
						|
	template <typename ...Args>
 | 
						|
	static constexpr bool is_callable_v
 | 
						|
		= rpl::details::is_callable_v<Args...>;
 | 
						|
 | 
						|
	template <typename Method>
 | 
						|
	auto with_instance(Method &&method)
 | 
						|
	-> std::enable_if_t<is_callable_v<Method, not_null<Instance*>>>;
 | 
						|
 | 
						|
	using DoneHandler = FnMut<bool(
 | 
						|
		mtpRequestId requestId,
 | 
						|
		bytes::const_span result)>;
 | 
						|
	using FailHandler = FnMut<void(
 | 
						|
		mtpRequestId requestId,
 | 
						|
		RPCError &&error)>;
 | 
						|
	struct Handlers {
 | 
						|
		DoneHandler done;
 | 
						|
		FailHandler fail;
 | 
						|
	};
 | 
						|
 | 
						|
	enum class FailSkipPolicy {
 | 
						|
		Simple,
 | 
						|
		HandleFlood,
 | 
						|
		HandleAll,
 | 
						|
	};
 | 
						|
 | 
						|
	class RequestBuilder {
 | 
						|
	public:
 | 
						|
		RequestBuilder(const RequestBuilder &other) = delete;
 | 
						|
		RequestBuilder(RequestBuilder &&other) = default;
 | 
						|
		RequestBuilder &operator=(const RequestBuilder &other) = delete;
 | 
						|
		RequestBuilder &operator=(RequestBuilder &&other) = delete;
 | 
						|
 | 
						|
		mtpRequestId send();
 | 
						|
 | 
						|
	protected:
 | 
						|
		RequestBuilder(
 | 
						|
			not_null<ConcurrentSender*> sender,
 | 
						|
			SecureRequest &&serialized) noexcept;
 | 
						|
 | 
						|
		void setToDC(ShiftedDcId dcId) noexcept;
 | 
						|
		void setCanWait(crl::time ms) noexcept;
 | 
						|
		template <typename Response, typename InvokeFullDone>
 | 
						|
		void setDoneHandler(InvokeFullDone &&invoke) noexcept;
 | 
						|
		template <typename InvokeFullFail>
 | 
						|
		void setFailHandler(InvokeFullFail &&invoke) noexcept;
 | 
						|
		void setFailSkipPolicy(FailSkipPolicy policy) noexcept;
 | 
						|
		void setAfter(mtpRequestId requestId) noexcept;
 | 
						|
 | 
						|
	private:
 | 
						|
		not_null<ConcurrentSender*> _sender;
 | 
						|
		SecureRequest _serialized;
 | 
						|
		ShiftedDcId _dcId = 0;
 | 
						|
		crl::time _canWait = 0;
 | 
						|
 | 
						|
		Handlers _handlers;
 | 
						|
		FailSkipPolicy _failSkipPolicy = FailSkipPolicy::Simple;
 | 
						|
		mtpRequestId _afterRequestId = 0;
 | 
						|
 | 
						|
	};
 | 
						|
 | 
						|
public:
 | 
						|
	ConcurrentSender(Fn<void(FnMut<void()>)> runner);
 | 
						|
 | 
						|
	template <typename Request>
 | 
						|
	class SpecificRequestBuilder : public RequestBuilder {
 | 
						|
	public:
 | 
						|
		using Response = typename Request::ResponseType;
 | 
						|
 | 
						|
		SpecificRequestBuilder(
 | 
						|
			const SpecificRequestBuilder &other) = delete;
 | 
						|
		SpecificRequestBuilder(
 | 
						|
			SpecificRequestBuilder &&other) = default;
 | 
						|
		SpecificRequestBuilder &operator=(
 | 
						|
			const SpecificRequestBuilder &other) = delete;
 | 
						|
		SpecificRequestBuilder &operator=(
 | 
						|
			SpecificRequestBuilder &&other) = delete;
 | 
						|
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &toDC(
 | 
						|
			ShiftedDcId dcId) noexcept;
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &afterDelay(
 | 
						|
			crl::time ms) noexcept;
 | 
						|
 | 
						|
#ifndef MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
		// Allow code completion to show response type.
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &done(FnMut<void()> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &done(
 | 
						|
			FnMut<void(mtpRequestId)> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &done(
 | 
						|
			FnMut<void(mtpRequestId, Response &&)> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &done(FnMut<void(
 | 
						|
			Response &&)> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &fail(FnMut<void()> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &fail(
 | 
						|
			FnMut<void(mtpRequestId)> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &fail(
 | 
						|
			FnMut<void(mtpRequestId, RPCError &&)> &&handler);
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &fail(
 | 
						|
			FnMut<void(RPCError &&)> &&handler);
 | 
						|
#else // !MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
		template <typename Handler>
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &done(Handler &&handler);
 | 
						|
		template <typename Handler>
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &fail(Handler &&handler);
 | 
						|
#endif // MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &handleFloodErrors() noexcept;
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &handleAllErrors() noexcept;
 | 
						|
		[[nodiscard]] SpecificRequestBuilder &afterRequest(
 | 
						|
			mtpRequestId requestId) noexcept;
 | 
						|
 | 
						|
	private:
 | 
						|
		SpecificRequestBuilder(
 | 
						|
			not_null<ConcurrentSender*> sender,
 | 
						|
			Request &&request) noexcept;
 | 
						|
 | 
						|
		friend class ConcurrentSender;
 | 
						|
 | 
						|
	};
 | 
						|
 | 
						|
	class SentRequestWrap {
 | 
						|
	public:
 | 
						|
		void cancel();
 | 
						|
		void detach();
 | 
						|
 | 
						|
	private:
 | 
						|
		friend class ConcurrentSender;
 | 
						|
		SentRequestWrap(
 | 
						|
			not_null<ConcurrentSender*> sender,
 | 
						|
			mtpRequestId requestId);
 | 
						|
 | 
						|
		not_null<ConcurrentSender*> _sender;
 | 
						|
		mtpRequestId _requestId = 0;
 | 
						|
 | 
						|
	};
 | 
						|
 | 
						|
	template <
 | 
						|
		typename Request,
 | 
						|
		typename = std::enable_if_t<!std::is_reference_v<Request>>,
 | 
						|
		typename = typename Request::Unboxed>
 | 
						|
	[[nodiscard]] SpecificRequestBuilder<Request> request(
 | 
						|
		Request &&request) noexcept;
 | 
						|
 | 
						|
	[[nodiscard]] SentRequestWrap request(mtpRequestId requestId) noexcept;
 | 
						|
 | 
						|
	[[nodiscard]] auto requestCanceller() noexcept;
 | 
						|
 | 
						|
	~ConcurrentSender();
 | 
						|
 | 
						|
private:
 | 
						|
	class RPCDoneHandler;
 | 
						|
	friend class RPCDoneHandler;
 | 
						|
	class RPCFailHandler;
 | 
						|
	friend class RPCFailHandler;
 | 
						|
	friend class RequestBuilder;
 | 
						|
	friend class SentRequestWrap;
 | 
						|
 | 
						|
	void senderRequestRegister(mtpRequestId requestId, Handlers &&handlers);
 | 
						|
	void senderRequestDone(
 | 
						|
		mtpRequestId requestId,
 | 
						|
		bytes::const_span result);
 | 
						|
	void senderRequestFail(
 | 
						|
		mtpRequestId requestId,
 | 
						|
		RPCError &&error);
 | 
						|
	void senderRequestCancel(mtpRequestId requestId);
 | 
						|
	void senderRequestCancelAll();
 | 
						|
	void senderRequestDetach(mtpRequestId requestId);
 | 
						|
 | 
						|
	const Fn<void(FnMut<void()>)> _runner;
 | 
						|
	base::flat_map<mtpRequestId, Handlers> _requests;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
template <typename Response, typename InvokeFullDone>
 | 
						|
void ConcurrentSender::RequestBuilder::setDoneHandler(
 | 
						|
	InvokeFullDone &&invoke
 | 
						|
) noexcept {
 | 
						|
	_handlers.done = [handler = std::move(invoke)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			bytes::const_span result) mutable {
 | 
						|
		auto from = reinterpret_cast<const mtpPrime*>(result.data());
 | 
						|
		const auto end = from + result.size() / sizeof(mtpPrime);
 | 
						|
		Response data;
 | 
						|
		if (!data.read(from, end)) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
		std::move(handler)(requestId, std::move(data));
 | 
						|
		return true;
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
template <typename InvokeFullFail>
 | 
						|
void ConcurrentSender::RequestBuilder::setFailHandler(
 | 
						|
	InvokeFullFail &&invoke
 | 
						|
) noexcept {
 | 
						|
	_handlers.fail = std::move(invoke);
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
ConcurrentSender::SpecificRequestBuilder<Request>::SpecificRequestBuilder(
 | 
						|
	not_null<ConcurrentSender*> sender,
 | 
						|
	Request &&request
 | 
						|
) noexcept : RequestBuilder(sender, SecureRequest::Serialize(request)) {
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::toDC(
 | 
						|
	ShiftedDcId dcId
 | 
						|
) noexcept -> SpecificRequestBuilder & {
 | 
						|
	setToDC(dcId);
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::afterDelay(
 | 
						|
	crl::time ms
 | 
						|
) noexcept -> SpecificRequestBuilder & {
 | 
						|
	setCanWait(ms);
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
#ifndef MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
// Allow code completion to show response type.
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
 | 
						|
	FnMut<void(Response &&)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setDoneHandler<Response>([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			Response &&result) mutable {
 | 
						|
		std::move(handler)(std::move(result));
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
 | 
						|
	FnMut<void(mtpRequestId, Response &&)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setDoneHandler<Response>(std::move(handler));
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
 | 
						|
	FnMut<void(mtpRequestId)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setDoneHandler<Response>([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			Response &&result) mutable {
 | 
						|
		std::move(handler)(requestId);
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
 | 
						|
	FnMut<void()> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setDoneHandler<Response>([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			Response &&result) mutable {
 | 
						|
		std::move(handler)();
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
 | 
						|
	FnMut<void(RPCError &&)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setFailHandler([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			RPCError &&error) mutable {
 | 
						|
		std::move(handler)(std::move(error));
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
 | 
						|
	FnMut<void(mtpRequestId, RPCError &&)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setFailHandler(std::move(handler));
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
 | 
						|
	FnMut<void(mtpRequestId)> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setFailHandler([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			RPCError &&error) mutable {
 | 
						|
		std::move(handler)(requestId);
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
 | 
						|
	FnMut<void()> &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	setFailHandler([handler = std::move(handler)](
 | 
						|
			mtpRequestId requestId,
 | 
						|
			RPCError &&error) mutable {
 | 
						|
		std::move(handler)();
 | 
						|
	});
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
#else // !MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
template <typename Request>
 | 
						|
template <typename Handler>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
 | 
						|
	Handler &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	using Response = typename Request::ResponseType;
 | 
						|
	constexpr auto takesFull = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		mtpRequestId,
 | 
						|
		Response>;
 | 
						|
	constexpr auto takesResponse = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		Response>;
 | 
						|
	constexpr auto takesRequestId = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		mtpRequestId>;
 | 
						|
	constexpr auto takesNone = rpl::details::is_callable_plain_v<Handler>;
 | 
						|
 | 
						|
	if constexpr (takesFull) {
 | 
						|
		setDoneHandler<Response>(std::forward<Handler>(handler));
 | 
						|
	} else if constexpr (takesResponse) {
 | 
						|
		setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				Response &&result) mutable {
 | 
						|
			std::move(handler)(std::move(result));
 | 
						|
		});
 | 
						|
	} else if constexpr (takesRequestId) {
 | 
						|
		setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				Response &&result) mutable {
 | 
						|
			std::move(handler)(requestId);
 | 
						|
		});
 | 
						|
	} else if constexpr (takesNone) {
 | 
						|
		setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				Response &&result) mutable {
 | 
						|
			std::move(handler)();
 | 
						|
		});
 | 
						|
	} else {
 | 
						|
		static_assert(false_t(Handler{}), "Bad done handler.");
 | 
						|
	}
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
template <typename Handler>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
 | 
						|
	Handler &&handler
 | 
						|
) -> SpecificRequestBuilder & {
 | 
						|
	constexpr auto takesFull = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		mtpRequestId,
 | 
						|
		RPCError>;
 | 
						|
	constexpr auto takesError = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		RPCError>;
 | 
						|
	constexpr auto takesRequestId = rpl::details::is_callable_plain_v<
 | 
						|
		Handler,
 | 
						|
		mtpRequestId>;
 | 
						|
	constexpr auto takesNone = rpl::details::is_callable_plain_v<Handler>;
 | 
						|
 | 
						|
	if constexpr (takesFull) {
 | 
						|
		setFailHandler(std::forward<Handler>(handler));
 | 
						|
	} else if constexpr (takesError) {
 | 
						|
		setFailHandler([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				RPCError &&error) mutable {
 | 
						|
			std::move(handler)(std::move(error));
 | 
						|
		});
 | 
						|
	} else if constexpr (takesRequestId) {
 | 
						|
		setFailHandler([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				RPCError &&error) mutable {
 | 
						|
			std::move(handler)(requestId);
 | 
						|
		});
 | 
						|
	} else if constexpr (takesNone) {
 | 
						|
		setFailHandler([handler = std::forward<Handler>(handler)](
 | 
						|
				mtpRequestId requestId,
 | 
						|
				RPCError &&error) mutable {
 | 
						|
			std::move(handler)();
 | 
						|
		});
 | 
						|
	} else {
 | 
						|
		static_assert(false_t(Handler{}), "Bad fail handler.");
 | 
						|
	}
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
#endif // MTP_SENDER_USE_GENERIC_HANDLERS
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::handleFloodErrors(
 | 
						|
) noexcept -> SpecificRequestBuilder & {
 | 
						|
	setFailSkipPolicy(FailSkipPolicy::HandleFlood);
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::handleAllErrors(
 | 
						|
) noexcept -> SpecificRequestBuilder & {
 | 
						|
	setFailSkipPolicy(FailSkipPolicy::HandleAll);
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request>
 | 
						|
auto ConcurrentSender::SpecificRequestBuilder<Request>::afterRequest(
 | 
						|
	mtpRequestId requestId
 | 
						|
) noexcept -> SpecificRequestBuilder & {
 | 
						|
	setAfter(requestId);
 | 
						|
	return *this;
 | 
						|
}
 | 
						|
 | 
						|
inline void ConcurrentSender::SentRequestWrap::cancel() {
 | 
						|
	_sender->senderRequestCancel(_requestId);
 | 
						|
}
 | 
						|
 | 
						|
inline void ConcurrentSender::SentRequestWrap::detach() {
 | 
						|
	_sender->senderRequestDetach(_requestId);
 | 
						|
}
 | 
						|
 | 
						|
inline ConcurrentSender::SentRequestWrap::SentRequestWrap(
 | 
						|
	not_null<ConcurrentSender*> sender,
 | 
						|
	mtpRequestId requestId
 | 
						|
) : _sender(sender)
 | 
						|
, _requestId(requestId) {
 | 
						|
}
 | 
						|
 | 
						|
template <typename Request, typename, typename>
 | 
						|
inline auto ConcurrentSender::request(Request &&request) noexcept
 | 
						|
-> SpecificRequestBuilder<Request> {
 | 
						|
	return SpecificRequestBuilder<Request>(this, std::move(request));
 | 
						|
}
 | 
						|
 | 
						|
inline auto ConcurrentSender::requestCanceller() noexcept {
 | 
						|
	return [=](mtpRequestId requestId) {
 | 
						|
		request(requestId).cancel();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
inline auto ConcurrentSender::request(mtpRequestId requestId) noexcept
 | 
						|
-> SentRequestWrap {
 | 
						|
	return SentRequestWrap(this, requestId);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace MTP
 |