2018-12-31 20:04:05 +01:00
|
|
|
//
|
2018-01-02 14:42:31 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
|
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/actor/impl/ConcurrentScheduler.h"
|
|
|
|
|
2018-01-06 13:12:45 +01:00
|
|
|
#include "td/actor/impl/Actor.h"
|
|
|
|
#include "td/actor/impl/ActorId.h"
|
|
|
|
#include "td/actor/impl/ActorInfo.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/actor/impl/Scheduler.h"
|
|
|
|
|
|
|
|
#include "td/utils/MpscPollableQueue.h"
|
|
|
|
#include "td/utils/port/thread_local.h"
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
|
|
|
void ConcurrentScheduler::init(int32 threads_n) {
|
|
|
|
#if TD_THREAD_UNSUPPORTED || TD_EVENTFD_UNSUPPORTED
|
|
|
|
threads_n = 0;
|
|
|
|
#endif
|
|
|
|
threads_n++;
|
|
|
|
std::vector<std::shared_ptr<MpscPollableQueue<EventFull>>> outbound(threads_n);
|
2018-09-07 02:41:21 +02:00
|
|
|
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
2018-12-31 20:04:05 +01:00
|
|
|
for (int32 i = 0; i < threads_n; i++) {
|
|
|
|
auto queue = std::make_shared<MpscPollableQueue<EventFull>>();
|
|
|
|
queue->init();
|
|
|
|
outbound[i] = queue;
|
|
|
|
}
|
2018-09-07 02:41:21 +02:00
|
|
|
#endif
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-08-16 15:56:16 +02:00
|
|
|
// +1 for extra scheduler for IOCP and send_closure from unrelated threads
|
|
|
|
// It will know about other schedulers
|
2018-09-07 02:41:21 +02:00
|
|
|
// Other schedulers will have no idea about its existence
|
|
|
|
int32 extra_scheduler = 1;
|
2018-08-16 15:56:16 +02:00
|
|
|
#if TD_THREAD_UNSUPPORTED || TD_EVENTFD_UNSUPPORTED
|
|
|
|
extra_scheduler = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
schedulers_.resize(threads_n + extra_scheduler);
|
|
|
|
for (int32 i = 0; i < threads_n + extra_scheduler; i++) {
|
2018-12-31 20:04:05 +01:00
|
|
|
auto &sched = schedulers_[i];
|
|
|
|
sched = make_unique<Scheduler>();
|
2018-08-16 15:56:16 +02:00
|
|
|
|
2018-09-07 02:41:21 +02:00
|
|
|
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
2018-08-16 15:56:16 +02:00
|
|
|
if (i >= threads_n) {
|
|
|
|
auto queue = std::make_shared<MpscPollableQueue<EventFull>>();
|
|
|
|
queue->init();
|
|
|
|
outbound.push_back(std::move(queue));
|
|
|
|
}
|
2018-09-07 02:41:21 +02:00
|
|
|
#endif
|
2018-08-16 15:56:16 +02:00
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
sched->init(i, outbound, static_cast<Scheduler::Callback *>(this));
|
|
|
|
}
|
|
|
|
|
2018-08-16 16:29:13 +02:00
|
|
|
#if TD_PORT_WINDOWS
|
2018-09-11 16:43:43 +02:00
|
|
|
iocp_ = std::make_unique<detail::Iocp>();
|
2018-08-16 16:29:13 +02:00
|
|
|
iocp_->init();
|
|
|
|
#endif
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
state_ = State::Start;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConcurrentScheduler::test_one_thread_run() {
|
|
|
|
do {
|
|
|
|
for (auto &sched : schedulers_) {
|
2018-09-18 15:43:16 +02:00
|
|
|
sched->run(Timestamp::now_cached());
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
} while (!is_finished_.load(std::memory_order_relaxed));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConcurrentScheduler::start() {
|
|
|
|
CHECK(state_ == State::Start);
|
|
|
|
is_finished_.store(false, std::memory_order_relaxed);
|
|
|
|
set_thread_id(0);
|
|
|
|
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
|
|
|
for (size_t i = 1; i < schedulers_.size(); i++) {
|
|
|
|
auto &sched = schedulers_[i];
|
|
|
|
threads_.push_back(td::thread([&, tid = i]() {
|
|
|
|
set_thread_id(static_cast<int32>(tid));
|
2018-08-16 16:29:13 +02:00
|
|
|
#if TD_PORT_WINDOWS
|
2018-09-11 16:43:43 +02:00
|
|
|
td::detail::Iocp::Guard iocp_guard(iocp_.get());
|
2018-08-16 16:29:13 +02:00
|
|
|
#endif
|
2018-12-31 20:04:05 +01:00
|
|
|
while (!is_finished()) {
|
2018-09-18 15:43:16 +02:00
|
|
|
sched->run(Timestamp::in(10));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
2018-08-16 16:29:13 +02:00
|
|
|
#if TD_PORT_WINDOWS
|
2018-09-07 02:41:21 +02:00
|
|
|
iocp_thread_ = td::thread([this] {
|
2018-08-17 11:19:21 +02:00
|
|
|
auto guard = this->get_send_guard();
|
2018-09-07 02:41:21 +02:00
|
|
|
this->iocp_->loop();
|
2018-08-17 11:19:21 +02:00
|
|
|
});
|
2018-09-07 02:41:21 +02:00
|
|
|
#endif
|
2018-08-16 16:29:13 +02:00
|
|
|
#endif
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
state_ = State::Run;
|
|
|
|
}
|
2018-09-18 15:43:16 +02:00
|
|
|
static TD_THREAD_LOCAL double emscripten_timeout;
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-09-18 15:43:16 +02:00
|
|
|
bool ConcurrentScheduler::run_main(Timestamp timeout) {
|
2018-12-31 20:04:05 +01:00
|
|
|
CHECK(state_ == State::Run);
|
|
|
|
// run main scheduler in same thread
|
|
|
|
auto &main_sched = schedulers_[0];
|
|
|
|
if (!is_finished()) {
|
2018-08-16 16:29:13 +02:00
|
|
|
#if TD_PORT_WINDOWS
|
2018-09-11 16:43:43 +02:00
|
|
|
td::detail::Iocp::Guard iocp_guard(iocp_.get());
|
2018-08-16 16:29:13 +02:00
|
|
|
#endif
|
2018-12-31 20:04:05 +01:00
|
|
|
main_sched->run(timeout);
|
|
|
|
}
|
2018-09-18 15:43:16 +02:00
|
|
|
|
|
|
|
// hack for emscripten
|
|
|
|
emscripten_timeout = get_main_timeout().at();
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
return !is_finished();
|
|
|
|
}
|
|
|
|
|
2018-09-18 15:43:16 +02:00
|
|
|
Timestamp ConcurrentScheduler::get_main_timeout() {
|
|
|
|
CHECK(state_ == State::Run);
|
|
|
|
return schedulers_[0]->get_timeout();
|
|
|
|
}
|
|
|
|
|
|
|
|
double ConcurrentScheduler::emscripten_get_main_timeout() {
|
|
|
|
return Timestamp::at(emscripten_timeout).in();
|
|
|
|
}
|
|
|
|
void ConcurrentScheduler::emscripten_clear_main_timeout() {
|
|
|
|
emscripten_timeout = 0;
|
|
|
|
}
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
void ConcurrentScheduler::finish() {
|
|
|
|
CHECK(state_ == State::Run);
|
|
|
|
if (!is_finished()) {
|
|
|
|
on_finish();
|
|
|
|
}
|
2018-08-16 16:29:13 +02:00
|
|
|
#if TD_PORT_WINDOWS
|
|
|
|
SCOPE_EXIT {
|
|
|
|
iocp_->clear();
|
|
|
|
};
|
2018-09-11 16:43:43 +02:00
|
|
|
td::detail::Iocp::Guard iocp_guard(iocp_.get());
|
2018-08-16 16:29:13 +02:00
|
|
|
#endif
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
|
|
|
for (auto &thread : threads_) {
|
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
threads_.clear();
|
|
|
|
#endif
|
2018-08-16 16:29:13 +02:00
|
|
|
|
|
|
|
#if TD_PORT_WINDOWS
|
|
|
|
iocp_->interrupt_loop();
|
|
|
|
iocp_thread_.join();
|
|
|
|
#endif
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
schedulers_.clear();
|
|
|
|
for (auto &f : at_finish_) {
|
|
|
|
f();
|
|
|
|
}
|
|
|
|
at_finish_.clear();
|
|
|
|
|
|
|
|
state_ = State::Start;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace td
|