2018-12-31 20:04:05 +01:00
|
|
|
//
|
2018-12-31 23:02:34 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
2018-12-31 20:04:05 +01:00
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
//
|
|
|
|
#include "td/telegram/Client.h"
|
|
|
|
|
|
|
|
#include "td/telegram/Td.h"
|
|
|
|
|
2018-07-03 21:29:04 +02:00
|
|
|
#include "td/actor/actor.h"
|
|
|
|
|
2019-02-12 22:26:36 +01:00
|
|
|
#include "td/utils/common.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/utils/crypto.h"
|
|
|
|
#include "td/utils/logging.h"
|
2019-04-23 12:02:10 +02:00
|
|
|
#include "td/utils/misc.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/utils/MpscPollableQueue.h"
|
|
|
|
#include "td/utils/port/thread.h"
|
|
|
|
|
2019-04-23 12:02:10 +02:00
|
|
|
#include <algorithm>
|
2018-07-23 13:45:31 +02:00
|
|
|
#include <atomic>
|
2018-12-31 20:04:05 +01:00
|
|
|
#include <deque>
|
2019-03-21 23:42:41 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <unordered_map>
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
|
|
|
#if TD_THREAD_UNSUPPORTED || TD_EVENTFD_UNSUPPORTED
|
|
|
|
|
|
|
|
class Client::Impl final {
|
|
|
|
public:
|
|
|
|
Impl() {
|
2018-09-27 03:19:03 +02:00
|
|
|
concurrent_scheduler_ = make_unique<ConcurrentScheduler>();
|
2018-09-15 18:33:27 +02:00
|
|
|
concurrent_scheduler_->init(0);
|
|
|
|
class Callback : public TdCallback {
|
|
|
|
public:
|
|
|
|
explicit Callback(Impl *client) : client_(client) {
|
|
|
|
}
|
|
|
|
void on_result(std::uint64_t id, td_api::object_ptr<td_api::Object> result) override {
|
|
|
|
client_->responses_.push_back({id, std::move(result)});
|
|
|
|
}
|
|
|
|
void on_error(std::uint64_t id, td_api::object_ptr<td_api::error> error) override {
|
|
|
|
client_->responses_.push_back({id, std::move(error)});
|
|
|
|
}
|
|
|
|
|
|
|
|
Callback(const Callback &) = delete;
|
|
|
|
Callback &operator=(const Callback &) = delete;
|
|
|
|
Callback(Callback &&) = delete;
|
|
|
|
Callback &operator=(Callback &&) = delete;
|
|
|
|
~Callback() override {
|
|
|
|
client_->closed_ = true;
|
|
|
|
Scheduler::instance()->yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Impl *client_;
|
|
|
|
};
|
|
|
|
td_ = concurrent_scheduler_->create_actor_unsafe<Td>(0, "Td", make_unique<Callback>(this));
|
|
|
|
concurrent_scheduler_->start();
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void send(Request request) {
|
|
|
|
requests_.push_back(std::move(request));
|
|
|
|
}
|
|
|
|
|
|
|
|
Response receive(double timeout) {
|
|
|
|
if (!requests_.empty()) {
|
2018-09-18 15:43:16 +02:00
|
|
|
auto guard = concurrent_scheduler_->get_main_guard();
|
2018-12-31 20:04:05 +01:00
|
|
|
for (auto &request : requests_) {
|
|
|
|
send_closure_later(td_, &Td::request, request.id, std::move(request.function));
|
|
|
|
}
|
|
|
|
requests_.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (responses_.empty()) {
|
2018-09-14 20:40:33 +02:00
|
|
|
concurrent_scheduler_->run_main(0);
|
2018-09-18 15:43:16 +02:00
|
|
|
} else {
|
|
|
|
ConcurrentScheduler::emscripten_clear_main_timeout();
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
if (!responses_.empty()) {
|
|
|
|
auto result = std::move(responses_.front());
|
|
|
|
responses_.pop_front();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return {0, nullptr};
|
|
|
|
}
|
|
|
|
|
2018-07-09 01:36:44 +02:00
|
|
|
Impl(const Impl &) = delete;
|
|
|
|
Impl &operator=(const Impl &) = delete;
|
|
|
|
Impl(Impl &&) = delete;
|
|
|
|
Impl &operator=(Impl &&) = delete;
|
2018-12-31 20:04:05 +01:00
|
|
|
~Impl() {
|
|
|
|
{
|
2018-09-18 15:43:16 +02:00
|
|
|
auto guard = concurrent_scheduler_->get_main_guard();
|
2018-12-31 20:04:05 +01:00
|
|
|
td_.reset();
|
|
|
|
}
|
|
|
|
while (!closed_) {
|
2018-09-14 20:40:33 +02:00
|
|
|
concurrent_scheduler_->run_main(0);
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
2018-09-14 20:40:33 +02:00
|
|
|
concurrent_scheduler_.reset();
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::deque<Response> responses_;
|
|
|
|
std::vector<Request> requests_;
|
2018-09-27 03:19:03 +02:00
|
|
|
unique_ptr<ConcurrentScheduler> concurrent_scheduler_;
|
2018-12-31 20:04:05 +01:00
|
|
|
ActorOwn<Td> td_;
|
|
|
|
bool closed_ = false;
|
2018-09-15 18:33:27 +02:00
|
|
|
};
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-09-15 18:33:27 +02:00
|
|
|
#else
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
class MultiTd : public Actor {
|
|
|
|
public:
|
2019-03-21 23:42:41 +01:00
|
|
|
void create(int32 td_id, unique_ptr<TdCallback> callback) {
|
2019-03-21 10:59:20 +01:00
|
|
|
auto &td = tds_[td_id];
|
|
|
|
CHECK(td.empty());
|
|
|
|
|
|
|
|
string name = "Td";
|
2019-04-23 10:56:32 +02:00
|
|
|
class TdActorContext : public ActorContext {
|
|
|
|
public:
|
2019-06-12 15:37:11 +02:00
|
|
|
explicit TdActorContext(std::string tag) : tag_(std::move(tag)) {
|
2019-04-23 10:56:32 +02:00
|
|
|
}
|
2019-08-01 21:12:04 +02:00
|
|
|
int32 get_id() const override {
|
|
|
|
return 0x172ae58d;
|
|
|
|
}
|
2019-04-23 10:56:32 +02:00
|
|
|
std::string tag_;
|
|
|
|
};
|
|
|
|
auto context = std::make_shared<TdActorContext>(to_string(td_id));
|
|
|
|
auto old_context = set_context(context);
|
|
|
|
auto old_tag = set_tag(context->tag_);
|
|
|
|
td = create_actor<Td>("Td", std::move(callback));
|
|
|
|
set_context(old_context);
|
|
|
|
set_tag(old_tag);
|
2019-03-21 10:59:20 +01:00
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
void send(int32 td_id, Client::Request request) {
|
2019-03-21 10:59:20 +01:00
|
|
|
auto &td = tds_[td_id];
|
|
|
|
CHECK(!td.empty());
|
|
|
|
send_closure(td, &Td::request, request.id, std::move(request.function));
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
void destroy(int32 td_id) {
|
2019-03-21 10:59:20 +01:00
|
|
|
auto size = tds_.erase(td_id);
|
|
|
|
CHECK(size == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-03-21 23:42:41 +01:00
|
|
|
std::unordered_map<int32, ActorOwn<Td> > tds_;
|
2019-03-21 10:59:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class MultiImpl {
|
|
|
|
public:
|
|
|
|
static std::shared_ptr<MultiImpl> get() {
|
|
|
|
static std::mutex mutex;
|
2019-04-23 09:42:46 +02:00
|
|
|
static std::vector<std::weak_ptr<MultiImpl> > impls;
|
2019-03-21 10:59:20 +01:00
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
2019-04-23 09:42:46 +02:00
|
|
|
if (impls.size() == 0) {
|
|
|
|
impls.resize(clamp(thread::hardware_concurrency(), 8u, 1000u) * 5 / 4);
|
|
|
|
}
|
|
|
|
auto &impl = *std::min_element(impls.begin(), impls.end(),
|
|
|
|
[](auto &a, auto &b) { return a.lock().use_count() < b.lock().use_count(); });
|
2019-03-21 10:59:20 +01:00
|
|
|
auto res = impl.lock();
|
|
|
|
if (!res) {
|
|
|
|
res = std::make_shared<MultiImpl>();
|
|
|
|
impl = res;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
MultiImpl() {
|
|
|
|
concurrent_scheduler_ = std::make_shared<ConcurrentScheduler>();
|
|
|
|
concurrent_scheduler_->init(3);
|
|
|
|
concurrent_scheduler_->start();
|
|
|
|
|
|
|
|
{
|
|
|
|
auto guard = concurrent_scheduler_->get_main_guard();
|
|
|
|
multi_td_ = create_actor<MultiTd>("MultiTd");
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduler_thread_ = thread([concurrent_scheduler = concurrent_scheduler_] {
|
|
|
|
while (concurrent_scheduler->run_main(10)) {
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
MultiImpl(const MultiImpl &) = delete;
|
|
|
|
MultiImpl &operator=(const MultiImpl &) = delete;
|
|
|
|
MultiImpl(MultiImpl &&) = delete;
|
|
|
|
MultiImpl &operator=(MultiImpl &&) = delete;
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
int32 create_id() {
|
2019-04-23 10:56:32 +02:00
|
|
|
static std::atomic<int32> id_{0};
|
2019-03-21 10:59:20 +01:00
|
|
|
return id_.fetch_add(1) + 1;
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
|
|
|
|
void create(int32 td_id, unique_ptr<TdCallback> callback) {
|
2019-03-21 10:59:20 +01:00
|
|
|
auto guard = concurrent_scheduler_->get_send_guard();
|
|
|
|
send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback));
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
void send(int32 td_id, Client::Request request) {
|
|
|
|
auto guard = concurrent_scheduler_->get_send_guard();
|
|
|
|
send_closure(multi_td_, &MultiTd::send, td_id, std::move(request));
|
|
|
|
}
|
2019-03-21 23:42:41 +01:00
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
void destroy(int32 td_id) {
|
|
|
|
auto guard = concurrent_scheduler_->get_send_guard();
|
|
|
|
send_closure(multi_td_, &MultiTd::destroy, td_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
~MultiImpl() {
|
|
|
|
{
|
|
|
|
auto guard = concurrent_scheduler_->get_send_guard();
|
|
|
|
multi_td_.reset();
|
|
|
|
Scheduler::instance()->finish();
|
|
|
|
}
|
|
|
|
scheduler_thread_.join();
|
2019-05-20 18:57:28 +02:00
|
|
|
concurrent_scheduler_->finish();
|
2019-03-21 10:59:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::shared_ptr<ConcurrentScheduler> concurrent_scheduler_;
|
2019-03-21 23:42:41 +01:00
|
|
|
thread scheduler_thread_;
|
|
|
|
ActorOwn<MultiTd> multi_td_;
|
2019-03-21 10:59:20 +01:00
|
|
|
};
|
2018-09-15 18:33:27 +02:00
|
|
|
|
|
|
|
class Client::Impl final {
|
|
|
|
public:
|
2019-03-21 10:59:20 +01:00
|
|
|
using OutputQueue = MpscPollableQueue<Client::Response>;
|
2018-09-15 18:33:27 +02:00
|
|
|
Impl() {
|
2019-03-21 10:59:20 +01:00
|
|
|
multi_impl_ = MultiImpl::get();
|
|
|
|
td_id_ = multi_impl_->create_id();
|
2018-09-15 18:33:27 +02:00
|
|
|
output_queue_ = std::make_shared<OutputQueue>();
|
|
|
|
output_queue_->init();
|
2019-03-21 10:59:20 +01:00
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
class Callback : public TdCallback {
|
|
|
|
public:
|
2018-09-15 18:33:27 +02:00
|
|
|
explicit Callback(std::shared_ptr<OutputQueue> output_queue) : output_queue_(std::move(output_queue)) {
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
void on_result(std::uint64_t id, td_api::object_ptr<td_api::Object> result) override {
|
2018-09-15 18:33:27 +02:00
|
|
|
output_queue_->writer_put({id, std::move(result)});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
void on_error(std::uint64_t id, td_api::object_ptr<td_api::error> error) override {
|
2018-09-15 18:33:27 +02:00
|
|
|
output_queue_->writer_put({id, std::move(error)});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
2018-09-15 18:33:27 +02:00
|
|
|
Callback(const Callback &) = delete;
|
|
|
|
Callback &operator=(const Callback &) = delete;
|
|
|
|
Callback(Callback &&) = delete;
|
|
|
|
Callback &operator=(Callback &&) = delete;
|
|
|
|
~Callback() override {
|
2019-03-21 10:59:20 +01:00
|
|
|
output_queue_->writer_put({0, nullptr});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-09-15 18:33:27 +02:00
|
|
|
std::shared_ptr<OutputQueue> output_queue_;
|
2018-12-31 20:04:05 +01:00
|
|
|
};
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
multi_impl_->create(td_id_, td::make_unique<Callback>(output_queue_));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
void send(Client::Request request) {
|
2018-12-31 20:04:05 +01:00
|
|
|
if (request.id == 0 || request.function == nullptr) {
|
|
|
|
LOG(ERROR) << "Drop wrong request " << request.id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
multi_impl_->send(td_id_, std::move(request));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
Client::Response receive(double timeout) {
|
2018-10-31 13:09:25 +01:00
|
|
|
VLOG(td_requests) << "Begin to wait for updates with timeout " << timeout;
|
2018-07-22 22:59:18 +02:00
|
|
|
auto is_locked = receive_lock_.exchange(true);
|
|
|
|
CHECK(!is_locked);
|
2018-07-24 17:20:59 +02:00
|
|
|
auto response = receive_unlocked(timeout);
|
|
|
|
is_locked = receive_lock_.exchange(false);
|
|
|
|
CHECK(is_locked);
|
2018-10-31 13:15:16 +01:00
|
|
|
VLOG(td_requests) << "End to wait for updates, returning object " << response.id << ' ' << response.object.get();
|
2018-07-24 17:20:59 +02:00
|
|
|
return response;
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2018-07-09 01:36:44 +02:00
|
|
|
Impl(const Impl &) = delete;
|
|
|
|
Impl &operator=(const Impl &) = delete;
|
|
|
|
Impl(Impl &&) = delete;
|
|
|
|
Impl &operator=(Impl &&) = delete;
|
2018-12-31 20:04:05 +01:00
|
|
|
~Impl() {
|
2019-03-21 10:59:20 +01:00
|
|
|
multi_impl_->destroy(td_id_);
|
|
|
|
while (!is_closed_) {
|
|
|
|
receive(10);
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-03-21 10:59:20 +01:00
|
|
|
std::shared_ptr<MultiImpl> multi_impl_;
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
std::shared_ptr<OutputQueue> output_queue_;
|
|
|
|
int output_queue_ready_cnt_{0};
|
2018-07-22 22:59:18 +02:00
|
|
|
std::atomic<bool> receive_lock_{false};
|
2019-03-21 10:59:20 +01:00
|
|
|
bool is_closed_{false};
|
|
|
|
int32 td_id_;
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2019-03-21 10:59:20 +01:00
|
|
|
Client::Response receive_unlocked(double timeout) {
|
2018-07-22 23:24:42 +02:00
|
|
|
if (output_queue_ready_cnt_ == 0) {
|
|
|
|
output_queue_ready_cnt_ = output_queue_->reader_wait_nonblock();
|
|
|
|
}
|
|
|
|
if (output_queue_ready_cnt_ > 0) {
|
|
|
|
output_queue_ready_cnt_--;
|
2019-03-21 10:59:20 +01:00
|
|
|
auto res = output_queue_->reader_get_unsafe();
|
2019-03-21 23:42:41 +01:00
|
|
|
if (res.object == nullptr && res.id == 0) {
|
2019-03-21 10:59:20 +01:00
|
|
|
is_closed_ = true;
|
|
|
|
}
|
|
|
|
return res;
|
2018-07-22 23:24:42 +02:00
|
|
|
}
|
|
|
|
if (timeout != 0) {
|
2018-09-13 13:55:55 +02:00
|
|
|
output_queue_->reader_get_event_fd().wait(static_cast<int>(timeout * 1000));
|
2018-07-22 23:24:42 +02:00
|
|
|
return receive_unlocked(0);
|
|
|
|
}
|
|
|
|
return {0, nullptr};
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2018-09-27 03:19:03 +02:00
|
|
|
Client::Client() : impl_(std::make_unique<Impl>()) {
|
2018-12-31 20:04:05 +01:00
|
|
|
// At least it should be enough for everybody who uses TDLib
|
|
|
|
init_openssl_threads();
|
|
|
|
}
|
|
|
|
|
2018-03-11 21:49:38 +01:00
|
|
|
void Client::send(Request &&request) {
|
2018-12-31 20:04:05 +01:00
|
|
|
impl_->send(std::move(request));
|
|
|
|
}
|
|
|
|
|
|
|
|
Client::Response Client::receive(double timeout) {
|
|
|
|
return impl_->receive(timeout);
|
|
|
|
}
|
|
|
|
|
2018-03-11 21:49:38 +01:00
|
|
|
Client::Response Client::execute(Request &&request) {
|
2018-12-31 20:04:05 +01:00
|
|
|
Response response;
|
|
|
|
response.id = request.id;
|
|
|
|
response.object = Td::static_request(std::move(request.function));
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
Client::~Client() = default;
|
|
|
|
Client::Client(Client &&other) = default;
|
|
|
|
Client &Client::operator=(Client &&other) = default;
|
|
|
|
|
|
|
|
} // namespace td
|