tdlight/td/telegram/RequestActor.h
2024-01-01 03:07:21 +03:00

161 lines
3.9 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// 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/telegram/Global.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Promise.h"
#include "td/utils/Status.h"
#include <type_traits>
namespace td {
template <class T = Unit>
class RequestActor : public Actor {
public:
RequestActor(ActorShared<Td> td_id, uint64 request_id)
: td_id_(std::move(td_id)), td_(td_id_.get().get_actor_unsafe()), request_id_(request_id) {
}
void loop() override {
if (G()->close_flag()) {
return do_send_error(Global::request_aborted_error());
}
PromiseActor<T> promise_actor;
FutureActor<T> future;
init_promise_future(&promise_actor, &future);
auto promise = create_promise_from_promise_actor(std::move(promise_actor));
do_run(std::move(promise));
if (future.is_ready()) {
CHECK(!promise);
if (future.is_error()) {
do_send_error(future.move_as_error());
} else {
do_set_result(future.move_as_ok());
do_send_result();
}
stop();
} else {
CHECK(!future.empty());
CHECK(future.get_state() == FutureActor<T>::State::Waiting);
if (--tries_left_ == 0) {
future.close();
do_send_error(Status::Error(500, "Requested data is inaccessible"));
return stop();
}
future.set_event(EventCreator::raw(actor_id(), nullptr));
future_ = std::move(future);
}
}
void raw_event(const Event::Raw &event) final {
if (future_.is_error()) {
auto error = future_.move_as_error();
if (error.is_static() && error.code() == FutureActor<T>::HANGUP_ERROR_CODE) {
// dropping query due to closing or lost promise
if (G()->close_flag()) {
do_send_error(Global::request_aborted_error());
} else {
LOG(ERROR) << "Promise was lost";
do_send_error(Status::Error(500, "Query can't be answered due to a bug in TDLib"));
}
return stop();
}
do_send_error(std::move(error));
stop();
} else {
do_set_result(future_.move_as_ok());
loop();
}
}
void on_start_migrate(int32 /*sched_id*/) final {
UNREACHABLE();
}
void on_finish_migrate() final {
UNREACHABLE();
}
int get_tries() const {
return tries_left_;
}
void set_tries(int32 tries) {
tries_left_ = tries;
}
protected:
ActorShared<Td> td_id_;
Td *td_;
void send_result(tl_object_ptr<td_api::Object> &&result) {
send_closure(td_id_, &Td::send_result, request_id_, std::move(result));
}
void send_error(Status &&status) {
LOG(INFO) << "Receive error for query: " << status;
send_closure(td_id_, &Td::send_error, request_id_, std::move(status));
}
private:
virtual void do_run(Promise<T> &&promise) = 0;
virtual void do_send_result() {
send_result(make_tl_object<td_api::ok>());
}
virtual void do_send_error(Status &&status) {
send_error(std::move(status));
}
virtual void do_set_result(T &&result) {
CHECK((std::is_same<T, Unit>::value)); // all other results should be implicitly handled by overriding this method
}
void hangup() final {
do_send_error(Global::request_aborted_error());
stop();
}
friend class RequestOnceActor;
uint64 request_id_;
int tries_left_ = 2;
FutureActor<T> future_;
};
class RequestOnceActor : public RequestActor<> {
public:
RequestOnceActor(ActorShared<Td> td_id, uint64 request_id) : RequestActor(std::move(td_id), request_id) {
}
void loop() final {
if (get_tries() < 2) {
do_send_result();
stop();
return;
}
RequestActor::loop();
}
};
} // namespace td