2018-12-31 20:04:05 +01:00
|
|
|
//
|
2024-01-01 01:07:21 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
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)
|
|
|
|
//
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "td/actor/impl/ActorInfo-decl.h"
|
|
|
|
#include "td/actor/impl/Scheduler-decl.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/Heap.h"
|
|
|
|
#include "td/utils/logging.h"
|
|
|
|
#include "td/utils/ObjectPool.h"
|
2018-09-07 02:41:21 +02:00
|
|
|
#include "td/utils/port/detail/PollableFd.h"
|
2018-09-10 03:08:15 +02:00
|
|
|
#include "td/utils/port/PollFlags.h"
|
2022-08-14 14:04:08 +02:00
|
|
|
#include "td/utils/Promise.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/utils/Slice.h"
|
2018-09-27 15:37:15 +02:00
|
|
|
#include "td/utils/Time.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
#include <tuple>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
|
|
|
/*** EventGuard ***/
|
|
|
|
class EventGuard {
|
|
|
|
public:
|
|
|
|
EventGuard(Scheduler *scheduler, ActorInfo *actor_info);
|
|
|
|
|
|
|
|
bool can_run() const {
|
|
|
|
return event_context_.flags == 0;
|
|
|
|
}
|
|
|
|
|
2023-05-05 12:51:19 +02:00
|
|
|
EventGuard(const EventGuard &) = delete;
|
|
|
|
EventGuard &operator=(const EventGuard &) = delete;
|
|
|
|
EventGuard(EventGuard &&) = delete;
|
|
|
|
EventGuard &operator=(EventGuard &&) = delete;
|
2018-12-31 20:04:05 +01:00
|
|
|
~EventGuard();
|
|
|
|
|
|
|
|
private:
|
|
|
|
Scheduler::EventContext event_context_;
|
|
|
|
Scheduler::EventContext *event_context_ptr_;
|
|
|
|
Scheduler *scheduler_;
|
|
|
|
ActorContext *save_context_;
|
|
|
|
const char *save_log_tag2_;
|
|
|
|
|
|
|
|
void swap_context(ActorInfo *info);
|
|
|
|
};
|
|
|
|
|
|
|
|
/*** Scheduler ***/
|
|
|
|
inline SchedulerGuard Scheduler::get_guard() {
|
|
|
|
return SchedulerGuard(this);
|
|
|
|
}
|
2024-02-01 14:00:38 +01:00
|
|
|
|
2018-08-16 15:56:16 +02:00
|
|
|
inline SchedulerGuard Scheduler::get_const_guard() {
|
|
|
|
return SchedulerGuard(this, false);
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
inline int32 Scheduler::sched_id() const {
|
|
|
|
return sched_id_;
|
|
|
|
}
|
2024-02-01 14:00:38 +01:00
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
inline int32 Scheduler::sched_count() const {
|
|
|
|
return sched_n_;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT, class... Args>
|
2021-12-09 22:27:13 +01:00
|
|
|
ActorOwn<ActorT> Scheduler::create_actor(Slice name, Args &&...args) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return register_actor_impl(name, new ActorT(std::forward<Args>(args)...), Actor::Deleter::Destroy, sched_id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT, class... Args>
|
2021-12-09 22:27:13 +01:00
|
|
|
ActorOwn<ActorT> Scheduler::create_actor_on_scheduler(Slice name, int32 sched_id, Args &&...args) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return register_actor_impl(name, new ActorT(std::forward<Args>(args)...), Actor::Deleter::Destroy, sched_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> Scheduler::register_actor(Slice name, ActorT *actor_ptr, int32 sched_id) {
|
|
|
|
return register_actor_impl(name, actor_ptr, Actor::Deleter::None, sched_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> Scheduler::register_actor(Slice name, unique_ptr<ActorT> actor_ptr, int32 sched_id) {
|
|
|
|
return register_actor_impl(name, actor_ptr.release(), Actor::Deleter::Destroy, sched_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> Scheduler::register_actor_impl(Slice name, ActorT *actor_ptr, Actor::Deleter deleter, int32 sched_id) {
|
2018-08-16 15:56:16 +02:00
|
|
|
CHECK(has_guard_);
|
2018-12-31 20:04:05 +01:00
|
|
|
if (sched_id == -1) {
|
|
|
|
sched_id = sched_id_;
|
|
|
|
}
|
|
|
|
#if TD_THREAD_UNSUPPORTED || TD_EVENTFD_UNSUPPORTED
|
|
|
|
sched_id = 0;
|
|
|
|
#endif
|
2019-02-12 22:28:47 +01:00
|
|
|
LOG_CHECK(sched_id == sched_id_ || (0 <= sched_id && sched_id < static_cast<int32>(outbound_queues_.size())))
|
|
|
|
<< sched_id;
|
2018-12-31 20:04:05 +01:00
|
|
|
auto info = actor_info_pool_->create_empty();
|
|
|
|
actor_count_++;
|
|
|
|
auto weak_info = info.get_weak();
|
|
|
|
auto actor_info = info.get();
|
2019-01-25 16:44:23 +01:00
|
|
|
actor_info->init(sched_id_, name, std::move(info), static_cast<Actor *>(actor_ptr), deleter,
|
2021-11-03 13:10:43 +01:00
|
|
|
ActorTraits<ActorT>::need_context, ActorTraits<ActorT>::need_start_up);
|
2021-11-03 14:53:14 +01:00
|
|
|
VLOG(actor) << "Create actor " << *actor_info << " (actor_count = " << actor_count_ << ')';
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
ActorId<ActorT> actor_id = weak_info->actor_id(actor_ptr);
|
|
|
|
if (sched_id != sched_id_) {
|
2024-05-06 18:38:24 +02:00
|
|
|
send_later(actor_id, Event::start());
|
2018-12-31 20:04:05 +01:00
|
|
|
do_migrate_actor(actor_info, sched_id);
|
|
|
|
} else {
|
|
|
|
pending_actors_list_.put(weak_info->get_list_node());
|
2021-11-03 13:10:43 +01:00
|
|
|
if (ActorTraits<ActorT>::need_start_up) {
|
2024-05-06 18:38:24 +02:00
|
|
|
send_later(actor_id, Event::start());
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ActorOwn<ActorT>(actor_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
2018-09-27 03:19:03 +02:00
|
|
|
ActorOwn<ActorT> Scheduler::register_existing_actor(unique_ptr<ActorT> actor_ptr) {
|
2018-12-31 20:04:05 +01:00
|
|
|
CHECK(!actor_ptr->empty());
|
|
|
|
auto actor_info = actor_ptr->get_info();
|
|
|
|
CHECK(actor_info->migrate_dest_flag_atomic().first == sched_id_);
|
|
|
|
return actor_info->transfer_ownership_to_scheduler(std::move(actor_ptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::destroy_actor(ActorInfo *actor_info) {
|
2021-11-03 14:53:14 +01:00
|
|
|
VLOG(actor) << "Destroy actor " << *actor_info << " (actor_count = " << actor_count_ << ')';
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2019-02-12 17:17:20 +01:00
|
|
|
LOG_CHECK(actor_info->migrate_dest() == sched_id_) << actor_info->migrate_dest() << " " << sched_id_;
|
2018-12-31 20:04:05 +01:00
|
|
|
cancel_actor_timeout(actor_info);
|
|
|
|
actor_info->get_list_node()->remove();
|
|
|
|
// called by ObjectPool
|
|
|
|
// actor_info->clear();
|
|
|
|
actor_count_--;
|
|
|
|
CHECK(actor_count_ >= 0);
|
|
|
|
}
|
|
|
|
|
2022-07-20 12:40:14 +02:00
|
|
|
inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<Actor> &actor_id, Event &&event) {
|
2018-12-31 20:04:05 +01:00
|
|
|
if (sched_id == sched_id_) {
|
|
|
|
ActorInfo *actor_info = actor_id.get_actor_info();
|
|
|
|
pending_events_[actor_info].push_back(std::move(event));
|
|
|
|
} else {
|
|
|
|
send_to_other_scheduler(sched_id, actor_id, std::move(event));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-20 12:40:14 +02:00
|
|
|
template <class T>
|
2022-07-27 10:41:16 +02:00
|
|
|
void Scheduler::destroy_on_scheduler(int32 sched_id, T &value) {
|
2022-07-20 12:57:05 +02:00
|
|
|
if (!value.empty()) {
|
|
|
|
destroy_on_scheduler_impl(sched_id, PromiseCreator::lambda([value = std::move(value)](Unit) {
|
|
|
|
// destroy value
|
|
|
|
}));
|
2024-05-02 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void Scheduler::destroy_on_scheduler_unique_ptr(int32 sched_id, T &value) {
|
|
|
|
if (value != nullptr) {
|
|
|
|
destroy_on_scheduler_impl(sched_id, PromiseCreator::lambda([value = std::move(value)](Unit) {
|
|
|
|
// destroy value
|
|
|
|
}));
|
2022-07-20 12:40:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 20:21:30 +02:00
|
|
|
template <class... ArgsT>
|
2022-07-27 10:41:16 +02:00
|
|
|
void Scheduler::destroy_on_scheduler(int32 sched_id, ArgsT &...values) {
|
2022-07-22 20:21:30 +02:00
|
|
|
destroy_on_scheduler_impl(sched_id, PromiseCreator::lambda([values = std::make_tuple(std::move(values)...)](Unit) {
|
|
|
|
// destroy values
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
inline void Scheduler::before_tail_send(const ActorId<> &actor_id) {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
template <class RunFuncT, class EventFuncT>
|
|
|
|
void Scheduler::send_immediately_impl(const ActorId<> &actor_id, const RunFuncT &run_func,
|
|
|
|
const EventFuncT &event_func) {
|
2018-12-31 20:04:05 +01:00
|
|
|
ActorInfo *actor_info = actor_id.get_actor_info();
|
|
|
|
if (unlikely(actor_info == nullptr || close_flag_)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 actor_sched_id;
|
2024-05-06 13:20:35 +02:00
|
|
|
bool on_current_sched;
|
|
|
|
bool can_send_immediately;
|
2024-05-06 18:38:24 +02:00
|
|
|
get_actor_sched_id_to_send_immediately(actor_info, actor_sched_id, on_current_sched, can_send_immediately);
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
if (likely(can_send_immediately)) { // run immediately
|
2023-01-11 11:27:53 +01:00
|
|
|
EventGuard guard(this, actor_info);
|
|
|
|
run_func(actor_info);
|
2018-12-31 20:04:05 +01:00
|
|
|
} else {
|
|
|
|
if (on_current_sched) {
|
|
|
|
add_to_mailbox(actor_info, event_func());
|
|
|
|
} else {
|
|
|
|
send_to_scheduler(actor_sched_id, actor_id, event_func());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
template <class EventT>
|
|
|
|
void Scheduler::send_lambda_immediately(ActorRef actor_ref, EventT &&func) {
|
|
|
|
return send_immediately_impl(
|
2019-09-28 04:14:21 +02:00
|
|
|
actor_ref.get(),
|
|
|
|
[&](ActorInfo *actor_info) {
|
|
|
|
event_context_ptr_->link_token = actor_ref.token();
|
2024-01-20 10:54:17 +01:00
|
|
|
func();
|
2019-09-28 04:14:21 +02:00
|
|
|
},
|
2020-02-26 16:15:19 +01:00
|
|
|
[&] {
|
2024-01-20 10:54:17 +01:00
|
|
|
auto event = Event::from_lambda(std::forward<EventT>(func));
|
2019-09-28 04:14:21 +02:00
|
|
|
event.set_link_token(actor_ref.token());
|
|
|
|
return event;
|
|
|
|
});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
template <class EventT>
|
|
|
|
void Scheduler::send_lambda_later(ActorRef actor_ref, EventT &&func) {
|
2024-05-06 19:31:30 +02:00
|
|
|
auto event = Event::from_lambda(std::forward<EventT>(func));
|
|
|
|
event.set_link_token(actor_ref.token());
|
|
|
|
return send_later_impl(actor_ref.get(), std::move(event));
|
2024-05-06 18:38:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class EventT>
|
|
|
|
void Scheduler::send_closure_immediately(ActorRef actor_ref, EventT &&closure) {
|
|
|
|
return send_immediately_impl(
|
2019-09-28 04:14:21 +02:00
|
|
|
actor_ref.get(),
|
|
|
|
[&](ActorInfo *actor_info) {
|
|
|
|
event_context_ptr_->link_token = actor_ref.token();
|
|
|
|
closure.run(static_cast<typename EventT::ActorType *>(actor_info->get_actor_unsafe()));
|
|
|
|
},
|
2020-02-26 16:15:19 +01:00
|
|
|
[&] {
|
2019-09-28 04:14:21 +02:00
|
|
|
auto event = Event::immediate_closure(std::forward<EventT>(closure));
|
|
|
|
event.set_link_token(actor_ref.token());
|
|
|
|
return event;
|
|
|
|
});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
template <class EventT>
|
|
|
|
void Scheduler::send_closure_later(ActorRef actor_ref, EventT &&closure) {
|
2024-05-06 19:31:30 +02:00
|
|
|
auto event = Event::immediate_closure(std::forward<EventT>(closure));
|
|
|
|
event.set_link_token(actor_ref.token());
|
|
|
|
return send_later_impl(actor_ref.get(), std::move(event));
|
2024-05-06 18:38:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::send_immediately(ActorRef actor_ref, Event &&event) {
|
2018-12-31 20:04:05 +01:00
|
|
|
event.set_link_token(actor_ref.token());
|
2024-05-06 18:38:24 +02:00
|
|
|
return send_immediately_impl(
|
2019-09-28 04:14:21 +02:00
|
|
|
actor_ref.get(), [&](ActorInfo *actor_info) { do_event(actor_info, std::move(event)); },
|
2020-02-26 16:15:19 +01:00
|
|
|
[&] { return std::move(event); });
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:24 +02:00
|
|
|
inline void Scheduler::send_later(ActorRef actor_ref, Event &&event) {
|
|
|
|
event.set_link_token(actor_ref.token());
|
2024-05-06 19:31:30 +02:00
|
|
|
return send_later_impl(actor_ref.get(), std::move(event));
|
2024-05-06 18:38:24 +02:00
|
|
|
}
|
|
|
|
|
2018-08-14 09:42:40 +02:00
|
|
|
inline void Scheduler::subscribe(PollableFd fd, PollFlags flags) {
|
2018-09-11 00:20:41 +02:00
|
|
|
instance()->poll_.subscribe(std::move(fd), flags);
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2018-08-14 09:42:40 +02:00
|
|
|
inline void Scheduler::unsubscribe(PollableFdRef fd) {
|
2018-09-11 00:20:41 +02:00
|
|
|
instance()->poll_.unsubscribe(std::move(fd));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
2018-08-14 09:42:40 +02:00
|
|
|
inline void Scheduler::unsubscribe_before_close(PollableFdRef fd) {
|
2018-09-11 00:20:41 +02:00
|
|
|
instance()->poll_.unsubscribe_before_close(std::move(fd));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::yield_actor(Actor *actor) {
|
|
|
|
yield_actor(actor->get_info());
|
|
|
|
}
|
|
|
|
inline void Scheduler::yield_actor(ActorInfo *actor_info) {
|
2024-05-06 18:38:24 +02:00
|
|
|
send_later(actor_info->actor_id(), Event::yield());
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::stop_actor(Actor *actor) {
|
|
|
|
stop_actor(actor->get_info());
|
|
|
|
}
|
|
|
|
inline void Scheduler::stop_actor(ActorInfo *actor_info) {
|
|
|
|
CHECK(event_context_ptr_->actor_info == actor_info);
|
|
|
|
event_context_ptr_->flags |= EventContext::Stop;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline uint64 Scheduler::get_link_token(Actor *actor) {
|
|
|
|
return get_link_token(actor->get_info());
|
|
|
|
}
|
|
|
|
inline uint64 Scheduler::get_link_token(ActorInfo *actor_info) {
|
2019-04-29 20:00:36 +02:00
|
|
|
LOG_CHECK(event_context_ptr_->actor_info == actor_info) << actor_info->get_name();
|
2018-12-31 20:04:05 +01:00
|
|
|
return event_context_ptr_->link_token;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::finish_migrate_actor(Actor *actor) {
|
|
|
|
register_migrated_actor(actor->get_info());
|
|
|
|
}
|
|
|
|
|
2021-08-15 10:15:14 +02:00
|
|
|
inline double Scheduler::get_actor_timeout(const Actor *actor) const {
|
|
|
|
return get_actor_timeout(actor->get_info());
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
inline void Scheduler::set_actor_timeout_in(Actor *actor, double timeout) {
|
|
|
|
set_actor_timeout_in(actor->get_info(), timeout);
|
|
|
|
}
|
|
|
|
inline void Scheduler::set_actor_timeout_at(Actor *actor, double timeout_at) {
|
|
|
|
set_actor_timeout_at(actor->get_info(), timeout_at);
|
|
|
|
}
|
|
|
|
inline void Scheduler::cancel_actor_timeout(Actor *actor) {
|
|
|
|
cancel_actor_timeout(actor->get_info());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::cancel_actor_timeout(ActorInfo *actor_info) {
|
|
|
|
HeapNode *heap_node = actor_info->get_heap_node();
|
|
|
|
if (heap_node->in_heap()) {
|
|
|
|
timeout_queue_.erase(heap_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::finish() {
|
|
|
|
if (callback_) {
|
|
|
|
callback_->on_finish();
|
|
|
|
}
|
|
|
|
yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::yield() {
|
|
|
|
yield_flag_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Scheduler::wakeup() {
|
|
|
|
std::atomic_thread_fence(std::memory_order_release);
|
|
|
|
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
2018-08-16 15:56:16 +02:00
|
|
|
inbound_queue_->writer_put({});
|
2018-12-31 20:04:05 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-09-18 15:43:16 +02:00
|
|
|
inline void Scheduler::run(Timestamp timeout) {
|
2018-12-31 20:04:05 +01:00
|
|
|
auto guard = get_guard();
|
|
|
|
run_no_guard(timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** Interface to current scheduler ***/
|
|
|
|
template <class ActorT, class... Args>
|
2021-12-09 22:27:13 +01:00
|
|
|
ActorOwn<ActorT> create_actor(Slice name, Args &&...args) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return Scheduler::instance()->create_actor<ActorT>(name, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT, class... Args>
|
2021-12-09 22:27:13 +01:00
|
|
|
ActorOwn<ActorT> create_actor_on_scheduler(Slice name, int32 sched_id, Args &&...args) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return Scheduler::instance()->create_actor_on_scheduler<ActorT>(name, sched_id, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> register_actor(Slice name, ActorT *actor_ptr, int32 sched_id) {
|
|
|
|
return Scheduler::instance()->register_actor<ActorT>(name, actor_ptr, sched_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> register_actor(Slice name, unique_ptr<ActorT> actor_ptr, int32 sched_id) {
|
|
|
|
return Scheduler::instance()->register_actor<ActorT>(name, std::move(actor_ptr), sched_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ActorT>
|
|
|
|
ActorOwn<ActorT> register_existing_actor(unique_ptr<ActorT> actor_ptr) {
|
|
|
|
return Scheduler::instance()->register_existing_actor(std::move(actor_ptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace td
|