1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

129
tdactor/td/actor/ActorId.h Normal file
View file

@ -0,0 +1,129 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/common.h"
namespace td {
namespace actor {
template <class ActorType = core::Actor>
class ActorId;
template <class ActorType = core::Actor>
class ActorOwn;
template <class ActorType = core::Actor>
class ActorShared;
namespace core {
template <class SelfT>
ActorId<SelfT> actor_id(SelfT *self);
}
// Essentially ActorInfoWeakPtr with Type
template <class ActorType>
class ActorId {
public:
using ActorT = ActorType;
ActorId() = default;
ActorId(const ActorId &) = default;
ActorId &operator=(const ActorId &) = default;
ActorId(ActorId &&other) = default;
ActorId &operator=(ActorId &&other) = default;
// allow only conversion from child to parent
template <class ToActorType, class = std::enable_if_t<std::is_base_of<ToActorType, ActorType>::value>>
operator ActorId<ToActorType>() const {
return ActorId<ToActorType>(ptr_);
}
template <class ToActorType, class FromActorType>
friend ActorId<ToActorType> actor_dynamic_cast(ActorId<FromActorType> from);
ActorType &get_actor_unsafe() const {
return static_cast<ActorType &>(actor_info().actor());
}
bool empty() const {
return !ptr_;
}
bool is_alive() const {
return !empty() && actor_info().is_alive();
}
template <class... ArgsT>
static ActorId<ActorType> create(ActorOptions &options, ArgsT &&... args) {
return ActorId<ActorType>(detail::create_actor<ActorType>(options, std::forward<ArgsT>(args)...));
}
template <class OtherT>
bool operator==(const ActorId<OtherT> &other) const {
return ptr_ == other.ptr_;
}
detail::ActorRef as_actor_ref() const {
CHECK(!empty());
return detail::ActorRef(*actor_info_ptr());
}
const core::ActorInfoPtr &actor_info_ptr() const {
return ptr_;
}
core::ActorInfo &actor_info() const {
CHECK(ptr_);
return *ptr_;
}
private:
core::ActorInfoPtr ptr_;
template <class OtherActorType>
friend class ActorId;
template <class OtherActorType>
friend class ActorOwn;
template <class OtherActorType>
friend class ActorShared;
explicit ActorId(core::ActorInfoPtr ptr) : ptr_(std::move(ptr)) {
}
template <class SelfT>
friend ActorId<SelfT> core::actor_id(SelfT *self);
};
template <class ToActorType, class FromActorType>
ActorId<ToActorType> actor_dynamic_cast(ActorId<FromActorType> from) {
static_assert(
std::is_base_of<FromActorType, ToActorType>::value || std::is_base_of<ToActorType, FromActorType>::value,
"Invalid actor dynamic conversion");
auto res = ActorId<ToActorType>(std::move(from.ptr_));
CHECK(dynamic_cast<ToActorType *>(&res.actor_info().actor()) == &res.get_actor_unsafe());
return res;
}
namespace core { // for ADL
template <class SelfT>
ActorId<SelfT> actor_id(SelfT *self) {
CHECK(self);
CHECK(static_cast<core::Actor *>(self) == &core::ActorExecuteContext::get()->actor());
return ActorId<SelfT>(core::ActorExecuteContext::get()->actor().get_actor_info_ptr());
}
inline ActorId<> actor_id() {
return actor_id(&core::ActorExecuteContext::get()->actor());
}
} // namespace core
using core::actor_id;
} // namespace actor
} // namespace td

100
tdactor/td/actor/ActorOwn.h Normal file
View file

@ -0,0 +1,100 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/common.h"
#include "td/actor/ActorId.h"
namespace td {
namespace actor {
template <class ActorType>
class ActorOwn {
public:
using ActorT = ActorType;
ActorOwn() = default;
explicit ActorOwn(ActorId<ActorType> id) : id_(std::move(id)) {
}
template <class OtherActorType>
explicit ActorOwn(ActorId<OtherActorType> id) : id_(std::move(id)) {
}
template <class OtherActorType>
ActorOwn(ActorOwn<OtherActorType> &&other) : id_(other.release()) {
}
template <class OtherActorType>
ActorOwn &operator=(ActorOwn<OtherActorType> &&other) {
reset(other.release());
return *this;
}
ActorOwn(ActorOwn &&other) : id_(other.release()) {
}
ActorOwn &operator=(ActorOwn &&other) {
reset(other.release());
return *this;
}
ActorOwn(const ActorOwn &) = delete;
ActorOwn &operator=(const ActorOwn &) = delete;
~ActorOwn() {
reset();
}
bool empty() const {
return id_.empty();
}
bool is_alive() const {
return id_.is_alive();
}
ActorId<ActorType> get() const {
return id_;
}
ActorType &get_actor_unsafe() const {
return (*this)->get_actor_unsafe();
}
ActorId<ActorType> release() {
return std::move(id_);
}
void reset(ActorId<ActorType> other = ActorId<ActorType>()) {
static_assert(sizeof(ActorType) > 0, "Can't use ActorOwn with incomplete type");
hangup();
id_ = std::move(other);
}
const ActorId<ActorType> *operator->() const {
return &id_;
}
detail::ActorRef as_actor_ref() const {
CHECK(!empty());
return detail::ActorRef(*id_.actor_info_ptr(), 0);
}
private:
ActorId<ActorType> id_;
void hangup() const {
if (empty()) {
return;
}
detail::send_message(as_actor_ref(), detail::ActorMessageCreator::hangup());
}
};
template <class ToActorType, class FromActorType>
ActorOwn<ToActorType> actor_dynamic_cast(ActorOwn<FromActorType> from) {
return ActorOwn<ToActorType>(td::actor::actor_dynamic_cast<ToActorType>(from.release()));
}
} // namespace actor
} // namespace td

View file

@ -0,0 +1,120 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/common.h"
#include "td/actor/ActorId.h"
namespace td {
namespace actor {
template <class ActorType>
class ActorShared {
public:
using ActorT = ActorType;
ActorShared() = default;
template <class OtherActorType>
ActorShared(ActorId<OtherActorType> id, uint64 token) : id_(std::move(id)), token_(token) {
CHECK(token_ != 0);
}
template <class OtherActorType>
ActorShared(ActorShared<OtherActorType> &&other) : id_(other.release()), token_(other.token()) {
}
template <class OtherActorType>
ActorShared(ActorOwn<OtherActorType> &&other) : id_(other.release()), token_(other.token()) {
}
template <class OtherActorType>
ActorShared &operator=(ActorShared<OtherActorType> &&other) {
reset(other.release(), other.token());
}
ActorShared(ActorShared &&other) : id_(other.release()), token_(other.token()) {
}
ActorShared &operator=(ActorShared &&other) {
reset(other.release(), other.token());
return *this;
}
ActorShared(const ActorShared &) = delete;
ActorShared &operator=(const ActorShared &) = delete;
~ActorShared() {
reset();
}
uint64 token() const {
return token_;
}
bool empty() const {
return id_.empty();
}
bool is_alive() const {
return id_.is_alive();
}
ActorId<ActorType> get() const {
return id_;
}
ActorId<ActorType> release() {
return std::move(id_);
}
ActorType &get_actor_unsafe() const {
return (*this)->get_actor_unsafe();
}
void reset(ActorId<ActorType> other = ActorId<ActorType>(), uint64 link_token = core::EmptyLinkToken) {
static_assert(sizeof(ActorType) > 0, "Can't use ActorShared with incomplete type");
hangup();
id_ = other;
token_ = link_token;
}
const ActorId<ActorType> *operator->() const {
return &id_;
}
detail::ActorRef as_actor_ref() const {
CHECK(!empty());
return detail::ActorRef(*id_.actor_info_ptr(), token_);
}
private:
ActorId<ActorType> id_;
uint64 token_;
void hangup() const {
if (empty()) {
return;
}
detail::send_message(as_actor_ref(), detail::ActorMessageCreator::hangup_shared());
}
};
template <class ToActorType, class FromActorType>
ActorShared<ToActorType> actor_dynamic_cast(ActorShared<FromActorType> from) {
return ActorShared<ToActorType>(td::actor::actor_dynamic_cast<ToActorType>(from.release()), from.token());
}
// common interface
namespace core { // for ADL
template <class SelfT>
ActorShared<SelfT> actor_shared(SelfT *self, uint64 id = static_cast<uint64>(-1)) {
return ActorShared<SelfT>(actor_id(self), id);
}
inline ActorShared<> actor_shared() {
return actor_shared(&core::ActorExecuteContext::get()->actor());
}
} // namespace core
using core::actor_shared;
} // namespace actor
} // namespace td

View file

@ -0,0 +1,112 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/actor/MultiPromise.h"
#include <mutex>
namespace td {
namespace detail {
class MultiPromiseImpl {
public:
explicit MultiPromiseImpl(MultiPromise::Options options) : options_(options) {
}
~MultiPromiseImpl() {
for (auto &promise : pending_) {
promise.set_value(Unit());
}
}
void on_status(Status status) {
if (status.is_ok() || options_.ignore_errors) {
return;
}
std::vector<Promise<>> promises;
{
std::unique_lock<std::mutex> lock(mutex_);
if (pending_error_.is_ok()) {
pending_error_ = status.clone();
std::swap(promises, pending_);
} else {
CHECK(pending_.empty());
}
}
for (auto &promise : promises) {
promise.set_error(status.clone());
}
}
void add_promise(Promise<> promise) {
if (options_.ignore_errors) {
pending_.push_back(std::move(promise));
}
Status status;
{
std::unique_lock<std::mutex> lock(mutex_);
if (pending_error_.is_error()) {
status = pending_error_.clone();
} else {
pending_.push_back(std::move(promise));
}
}
if (status.is_error()) {
promise.set_error(std::move(status));
}
}
private:
std::mutex mutex_;
std::vector<Promise<>> pending_;
MultiPromise::Options options_;
Status pending_error_;
};
} // namespace detail
void MultiPromise::InitGuard::add_promise(Promise<> promise) {
impl_->add_promise(std::move(promise));
}
Promise<> MultiPromise::InitGuard::get_promise() {
return [impl = impl_](Result<Unit> result) {
if (result.is_ok()) {
impl->on_status(Status::OK());
} else {
impl->on_status(result.move_as_error());
}
};
}
bool MultiPromise::InitGuard::empty() const {
return !impl_;
}
MultiPromise::InitGuard::operator bool() const {
return !empty();
}
MultiPromise::InitGuard MultiPromise::init_guard() {
CHECK(!impl_.lock());
auto impl = std::make_shared<Impl>(options_);
impl_ = impl;
return InitGuard(std::move(impl));
}
MultiPromise::InitGuard MultiPromise::add_promise_or_init(Promise<> promise) {
auto impl = impl_.lock();
if (!impl) {
auto guard = init_guard();
guard.add_promise(std::move(promise));
return guard;
}
impl->add_promise(std::move(promise));
return {};
}
} // namespace td

View file

@ -0,0 +1,65 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/PromiseFuture.h"
namespace td {
namespace detail {
class MultiPromiseImpl;
}
class MultiPromise {
using Impl = detail::MultiPromiseImpl;
public:
struct Options {
Options() {
}
bool ignore_errors{false};
};
explicit MultiPromise(Options options = Options{}) : options_(options) {
}
struct InitGuard {
public:
InitGuard() = default;
InitGuard(std::shared_ptr<Impl> impl) : impl_(std::move(impl)) {
}
InitGuard(InitGuard &&other) = default;
InitGuard &operator=(InitGuard &&other) = default;
InitGuard(const InitGuard &other) = delete;
InitGuard &operator=(const InitGuard &other) = delete;
void add_promise(Promise<> promise);
Promise<> get_promise();
bool empty() const;
explicit operator bool() const;
private:
std::shared_ptr<Impl> impl_;
};
TD_WARN_UNUSED_RESULT InitGuard init_guard();
TD_WARN_UNUSED_RESULT InitGuard add_promise_or_init(Promise<> promise);
private:
Options options_;
std::weak_ptr<Impl> impl_;
};
} // namespace td

View file

@ -0,0 +1,336 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Closure.h"
#include "td/utils/common.h"
#include "td/utils/invoke.h" // for tuple_for_each
#include "td/utils/logging.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Status.h"
#include <tuple>
#include <type_traits>
#include <utility>
namespace td {
namespace detail {
template <typename T>
struct GetArg : public GetArg<decltype(&T::operator())> {};
template <class C, class R, class Arg>
class GetArg<R (C::*)(Arg)> {
public:
using type = Arg;
};
template <class C, class R, class Arg>
class GetArg<R (C::*)(Arg) const> {
public:
using type = Arg;
};
template <class T>
using get_arg_t = std::decay_t<typename GetArg<T>::type>;
template <class T>
struct DropResult {
using type = T;
};
template <class T>
struct DropResult<Result<T>> {
using type = T;
};
template <class T>
using drop_result_t = typename DropResult<T>::type;
} // namespace detail
template <class T = Unit>
class PromiseInterface {
public:
using ValueType = T;
PromiseInterface() = default;
PromiseInterface(const PromiseInterface &) = delete;
PromiseInterface &operator=(const PromiseInterface &) = delete;
PromiseInterface(PromiseInterface &&) = default;
PromiseInterface &operator=(PromiseInterface &&) = default;
virtual ~PromiseInterface() = default;
virtual void set_value(T &&value) {
set_result(std::move(value));
}
virtual void set_error(Status &&error) {
set_result(std::move(error));
}
virtual void set_result(Result<T> &&result) {
if (result.is_ok()) {
set_value(result.move_as_ok());
} else {
set_error(result.move_as_error());
}
}
void operator()(T &&value) {
set_value(std::move(value));
}
void operator()(Status &&error) {
set_error(std::move(error));
}
void operator()(Result<T> &&result) {
set_result(std::move(result));
}
};
template <class T = Unit>
class Promise;
constexpr std::false_type is_promise_interface(...) {
return {};
}
template <class T>
constexpr std::true_type is_promise_interface(const PromiseInterface<T> &promise) {
return {};
}
template <class T>
constexpr std::true_type is_promise_interface(const Promise<T> &promise) {
return {};
}
template <class F>
constexpr bool is_promise_interface() {
return decltype(is_promise_interface(std::declval<F>()))::value;
}
constexpr std::false_type is_promise_interface_ptr(...) {
return {};
}
template <class T>
constexpr std::true_type is_promise_interface_ptr(const unique_ptr<T> &promise) {
return {};
}
template <class F>
constexpr bool is_promise_interface_ptr() {
return decltype(is_promise_interface_ptr(std::declval<F>()))::value;
}
template <class ValueT, class FunctionT>
class LambdaPromise : public PromiseInterface<ValueT> {
public:
void set_value(ValueT &&value) override {
CHECK(has_lambda_.get());
do_ok(std::move(value));
has_lambda_ = false;
}
void set_error(Status &&error) override {
CHECK(has_lambda_.get());
do_error(std::move(error));
has_lambda_ = false;
}
LambdaPromise(const LambdaPromise &other) = delete;
LambdaPromise &operator=(const LambdaPromise &other) = delete;
LambdaPromise(LambdaPromise &&other) = default;
LambdaPromise &operator=(LambdaPromise &&other) = default;
~LambdaPromise() override {
if (has_lambda_.get()) {
do_error(Status::Error("Lost promise"));
}
}
template <class FromOkT>
explicit LambdaPromise(FromOkT &&ok) : ok_(std::forward<FromOkT>(ok)), has_lambda_(true) {
}
private:
FunctionT ok_;
MovableValue<bool> has_lambda_{false};
template <class F = FunctionT>
std::enable_if_t<is_callable<F, Result<ValueT>>::value, void> do_error(Status &&status) {
ok_(Result<ValueT>(std::move(status)));
}
template <class Y, class F = FunctionT>
std::enable_if_t<!is_callable<F, Result<ValueT>>::value, void> do_error(Y &&status) {
ok_(Auto());
}
template <class F = FunctionT>
std::enable_if_t<is_callable<F, Result<ValueT>>::value, void> do_ok(ValueT &&result) {
ok_(Result<ValueT>(std::move(result)));
}
template <class F = FunctionT>
std::enable_if_t<!is_callable<F, Result<ValueT>>::value, void> do_ok(ValueT &&result) {
ok_(std::move(result));
}
};
template <class T = void, class F = void, std::enable_if_t<std::is_same<T, void>::value, bool> has_t = false>
auto lambda_promise(F &&f) {
return LambdaPromise<detail::drop_result_t<detail::get_arg_t<std::decay_t<F>>>, std::decay_t<F>>(std::forward<F>(f));
}
template <class T = void, class F = void, std::enable_if_t<!std::is_same<T, void>::value, bool> has_t = true>
auto lambda_promise(F &&f) {
return LambdaPromise<T, std::decay_t<F>>(std::forward<F>(f));
}
template <class T, class F, std::enable_if_t<is_promise_interface<F>(), bool> from_promise_inerface = true>
auto &&promise_interface(F &&f) {
return std::forward<F>(f);
}
template <class T, class F, std::enable_if_t<!is_promise_interface<F>(), bool> from_promise_inerface = false>
auto promise_interface(F &&f) {
return lambda_promise<T>(std::forward<F>(f));
}
template <class T, class F, std::enable_if_t<is_promise_interface_ptr<F>(), bool> from_promise_inerface = true>
auto promise_interface_ptr(F &&f) {
return std::forward<F>(f);
}
template <class T, class F, std::enable_if_t<!is_promise_interface_ptr<F>(), bool> from_promise_inerface = false>
auto promise_interface_ptr(F &&f) {
return std::make_unique<std::decay_t<decltype(promise_interface<T>(std::forward<F>(f)))>>(
promise_interface<T>(std::forward<F>(f)));
}
template <class T>
class Promise {
public:
using ArgT = T;
void set_value(T &&value) {
if (!promise_) {
return;
}
promise_->set_value(std::move(value));
promise_.reset();
}
void set_error(Status &&error) {
if (!promise_) {
return;
}
promise_->set_error(std::move(error));
promise_.reset();
}
void set_result(Result<T> &&result) {
if (!promise_) {
return;
}
promise_->set_result(std::move(result));
promise_.reset();
}
template <class S>
void operator()(S &&result) {
if (!promise_) {
return;
}
promise_->operator()(std::forward<S>(result));
promise_.reset();
}
void reset() {
promise_.reset();
}
std::unique_ptr<PromiseInterface<T>> release() {
return std::move(promise_);
}
Promise() = default;
explicit Promise(std::unique_ptr<PromiseInterface<T>> promise) : promise_(std::move(promise)) {
}
Promise &operator=(Promise &&) = default;
Promise(Promise &&) = default;
template <class F>
Promise(F &&f) : promise_(promise_interface_ptr<T>(std::forward<F>(f))) {
}
explicit operator bool() {
return static_cast<bool>(promise_);
}
private:
std::unique_ptr<PromiseInterface<T>> promise_;
};
template <class F>
auto make_promise(F &&f) {
using ValueT = detail::drop_result_t<detail::get_arg_t<F>>;
return Promise<ValueT>(promise_interface_ptr(std::forward<F>(f)));
}
namespace detail {
template <class... ArgsT>
class JoinPromise : public PromiseInterface<Unit> {
public:
explicit JoinPromise(ArgsT &&... arg) : promises_(std::forward<ArgsT>(arg)...) {
}
void set_value(Unit &&) override {
tuple_for_each(promises_, [](auto &promise) { promise.set_value(Unit()); });
}
void set_error(Status &&error) override {
tuple_for_each(promises_, [&error](auto &promise) { promise.set_error(error.clone()); });
}
private:
std::tuple<std::decay_t<ArgsT>...> promises_;
};
} // namespace detail
class PromiseCreator {
public:
struct Ignore {
void operator()(Status &&error) {
error.ignore();
}
};
template <class OkT>
static auto lambda(OkT &&ok) {
return lambda_promise(std::forward<OkT>(ok));
}
template <class... ArgsT>
static Promise<> join(ArgsT &&... args) {
return Promise<>(std::make_unique<detail::JoinPromise<ArgsT...>>(std::forward<ArgsT>(args)...));
}
};
template <class T = Unit>
class SafePromise {
public:
SafePromise(Promise<T> promise, Result<T> result) : promise_(std::move(promise)), result_(std::move(result)) {
}
SafePromise(const SafePromise &other) = delete;
SafePromise &operator=(const SafePromise &other) = delete;
SafePromise(SafePromise &&other) = default;
SafePromise &operator=(SafePromise &&other) = default;
~SafePromise() {
if (promise_) {
promise_.set_result(std::move(result_));
}
}
Promise<T> release() {
return std::move(promise_);
}
operator Promise<T>() && {
return release();
}
private:
Promise<T> promise_;
Result<T> result_;
};
} // namespace td

165
tdactor/td/actor/actor.h Normal file
View file

@ -0,0 +1,165 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/common.h"
#include "td/actor/ActorId.h"
#include "td/actor/ActorOwn.h"
#include "td/actor/ActorShared.h"
namespace td {
namespace actor {
template <class T, class... ArgsT>
TD_WARN_UNUSED_RESULT ActorOwn<T> create_actor(ActorOptions options, ArgsT &&... args) {
return ActorOwn<T>(ActorId<T>::create(options, std::forward<ArgsT>(args)...));
}
template <class T, class... ArgsT>
TD_WARN_UNUSED_RESULT ActorOwn<T> create_actor(Slice name, ArgsT &&... args) {
return ActorOwn<T>(ActorId<T>::create(ActorOptions().with_name(name), std::forward<ArgsT>(args)...));
}
#define SEND_CLOSURE_LATER 1
#ifndef SEND_CLOSURE_LATER
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count == sizeof...(ArgsT), bool> with_promise = false>
void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure(id.as_actor_ref(), function, std::forward<ArgsT>(args)...);
}
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count != sizeof...(ArgsT), bool> with_promise = true>
void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure_with_promise(id.as_actor_ref(),
call_n_arguments<argument_count>(
[&function](auto &&... nargs) {
return create_immediate_closure(function,
std::forward<decltype(nargs)>(nargs)...);
},
std::forward<ArgsT>(args)...),
get_last_argument(std::forward<ArgsT>(args)...));
}
#else
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count == sizeof...(ArgsT), bool> with_promise = false>
void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure_later(id.as_actor_ref(), function, std::forward<ArgsT>(args)...);
}
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count != sizeof...(ArgsT), bool> with_promise = true>
void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure_with_promise_later(id.as_actor_ref(),
call_n_arguments<argument_count>(
[&function](auto &&... nargs) {
return create_delayed_closure(function,
std::forward<decltype(nargs)>(nargs)...);
},
std::forward<ArgsT>(args)...),
get_last_argument(std::forward<ArgsT>(args)...));
}
#endif
template <typename ActorIdT, typename FunctionT, typename... ArgsT>
bool send_closure_bool(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
send_closure(std::forward<ActorIdT>(actor_id), function, std::forward<ArgsT>(args)...);
return true;
}
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count == sizeof...(ArgsT), bool> with_promise = false>
void send_closure_later(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure_later(id.as_actor_ref(), function, std::forward<ArgsT>(args)...);
}
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count != sizeof...(ArgsT), bool> with_promise = true>
void send_closure_later(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using ActorT = typename std::decay_t<ActorIdT>::ActorT;
static_assert(std::is_base_of<FunctionClassT, ActorT>::value, "unsafe send_closure");
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_closure_with_promise_later(id.as_actor_ref(),
call_n_arguments<argument_count>(
[&function](auto &&... nargs) {
return create_delayed_closure(function,
std::forward<decltype(nargs)>(nargs)...);
},
std::forward<ArgsT>(args)...),
get_last_argument(std::forward<ArgsT>(args)...));
}
template <typename ActorIdT, typename FunctionT, typename... ArgsT>
bool send_closure_later_bool(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
send_closure_later(std::forward<ActorIdT>(actor_id), function, std::forward<ArgsT>(args)...);
return true;
}
template <class ActorIdT, class... ArgsT>
void send_lambda(ActorIdT &&actor_id, ArgsT &&... args) {
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_lambda(id.as_actor_ref(), std::forward<ArgsT>(args)...);
}
template <class ActorIdT, class... ArgsT>
void send_lambda_later(ActorIdT &&actor_id, ArgsT &&... args) {
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_lambda_later(id.as_actor_ref(), std::forward<ArgsT>(args)...);
}
template <class ActorIdT>
void send_signals(ActorIdT &&actor_id, ActorSignals signals) {
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_signals(id.as_actor_ref(), signals);
}
template <class ActorIdT>
void send_signals_later(ActorIdT &&actor_id, ActorSignals signals) {
ActorIdT id = std::forward<ActorIdT>(actor_id);
detail::send_signals_later(id.as_actor_ref(), signals);
}
} // namespace actor
} // namespace td

379
tdactor/td/actor/common.h Normal file
View file

@ -0,0 +1,379 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/Actor.h"
#include "td/actor/core/ActorSignals.h"
#include "td/actor/core/SchedulerId.h"
#include "td/actor/core/SchedulerContext.h"
#include "td/actor/core/Scheduler.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/Timer.h"
namespace td {
namespace actor {
using core::ActorOptions;
// Replacement for core::ActorSignals. Easier to use and do not allow internal signals
class ActorSignals {
public:
static ActorSignals pause() {
return ActorSignals(core::ActorSignals::one(core::ActorSignals::Pause));
}
static ActorSignals kill() {
return ActorSignals(core::ActorSignals::one(core::ActorSignals::Kill));
}
static ActorSignals wakeup() {
return ActorSignals(core::ActorSignals::one(core::ActorSignals::Wakeup));
}
friend ActorSignals operator|(ActorSignals a, ActorSignals b) {
a.raw_.add_signals(b.raw_);
return a;
}
ActorSignals() = default;
core::ActorSignals raw() const {
return raw_;
}
private:
ActorSignals(core::ActorSignals raw) : raw_(raw) {
}
core::ActorSignals raw_;
};
// TODO: proper interface
using core::Actor;
using core::SchedulerContext;
using core::SchedulerId;
class Scheduler {
public:
struct NodeInfo {
NodeInfo(size_t cpu_threads) : cpu_threads_(cpu_threads) {
}
NodeInfo(size_t cpu_threads, size_t io_threads) : cpu_threads_(cpu_threads), io_threads_(io_threads) {
}
size_t cpu_threads_;
size_t io_threads_{1};
};
enum Mode { Running, Paused };
Scheduler(std::vector<NodeInfo> infos, Mode mode = Paused) : infos_(std::move(infos)) {
init();
if (mode == Running) {
start();
}
}
~Scheduler() {
stop();
}
Scheduler(const Scheduler &) = delete;
Scheduler(Scheduler &&) = delete;
Scheduler &operator=(const Scheduler &) = delete;
Scheduler &operator=(Scheduler &&) = delete;
void start() {
if (is_started_) {
return;
}
is_started_ = true;
for (size_t it = 0; it < schedulers_.size(); it++) {
auto &scheduler = schedulers_[it];
scheduler->start();
if (it != 0) {
auto thread = td::thread([&] {
while (scheduler->run(10)) {
}
});
thread.set_name(PSLICE() << "#" << it << ":io");
thread.detach();
}
}
}
bool run() {
start();
while (schedulers_[0]->run(10)) {
}
return false;
}
bool run(double timeout) {
start();
return schedulers_[0]->run(timeout);
}
template <class F>
void run_in_context(F &&f) {
schedulers_[0]->run_in_context(std::forward<F>(f));
}
template <class F>
void run_in_context_external(F &&f) {
schedulers_[0]->run_in_context_external(std::forward<F>(f));
}
void stop() {
if (!group_info_) {
return;
}
if (!is_started_) {
start();
}
schedulers_[0]->stop();
run();
core::Scheduler::close_scheduler_group(*group_info_);
group_info_.reset();
}
private:
std::vector<NodeInfo> infos_{td::thread::hardware_concurrency()};
std::shared_ptr<core::SchedulerGroupInfo> group_info_;
std::vector<td::unique_ptr<core::Scheduler>> schedulers_;
bool is_started_{false};
void init() {
CHECK(infos_.size() < 256);
CHECK(!group_info_);
group_info_ = std::make_shared<core::SchedulerGroupInfo>(infos_.size());
td::uint8 id = 0;
for (const auto &info : infos_) {
schedulers_.emplace_back(td::make_unique<core::Scheduler>(group_info_, core::SchedulerId{id}, info.cpu_threads_));
id++;
}
}
};
// Some helper functions. Not part of public interface and not part
// of namespace core
namespace detail {
template <class LambdaT>
class ActorMessageLambda : public core::ActorMessageImpl {
public:
template <class FromLambdaT>
explicit ActorMessageLambda(FromLambdaT &&lambda) : lambda_(std::forward<FromLambdaT>(lambda)) {
}
void run() override {
//delivery_warning_.reset();
lambda_();
}
private:
LambdaT lambda_;
//PerfWarningTimer delivery_warning_{"Long message delivery", 2};
};
class ActorMessageCreator {
public:
template <class F>
static auto lambda(F &&f) {
return core::ActorMessage(std::make_unique<ActorMessageLambda<std::decay_t<F>>>(std::forward<F>(f)));
}
static auto hangup() {
return core::ActorMessage(std::make_unique<core::ActorMessageHangup>());
}
static auto hangup_shared() {
return core::ActorMessage(std::make_unique<core::ActorMessageHangupShared>());
}
// Use faster allocation?
};
struct ActorRef {
ActorRef(core::ActorInfo &actor_info, uint64 link_token = core::EmptyLinkToken)
: actor_info(actor_info), link_token(link_token) {
}
core::ActorInfo &actor_info;
uint64 link_token;
};
template <class T>
T &current_actor() {
return static_cast<T &>(core::ActorExecuteContext::get()->actor());
}
inline void send_message(core::ActorInfo &actor_info, core::ActorMessage message) {
auto scheduler_context_ptr = core::SchedulerContext::get();
if (scheduler_context_ptr == nullptr) {
LOG(ERROR) << "send to actor is silently ignored";
return;
}
auto &scheduler_context = *scheduler_context_ptr;
core::ActorExecutor executor(actor_info, scheduler_context,
core::ActorExecutor::Options().with_has_poll(scheduler_context.has_poll()));
executor.send(std::move(message));
}
inline void send_message(ActorRef actor_ref, core::ActorMessage message) {
message.set_link_token(actor_ref.link_token);
send_message(actor_ref.actor_info, std::move(message));
}
inline void send_message_later(core::ActorInfo &actor_info, core::ActorMessage message) {
auto scheduler_context_ptr = core::SchedulerContext::get();
if (scheduler_context_ptr == nullptr) {
LOG(ERROR) << "send to actor is silently ignored";
return;
}
auto &scheduler_context = *scheduler_context_ptr;
core::ActorExecutor executor(actor_info, scheduler_context,
core::ActorExecutor::Options().with_has_poll(scheduler_context.has_poll()));
message.set_big();
executor.send(std::move(message));
}
inline void send_message_later(ActorRef actor_ref, core::ActorMessage message) {
message.set_link_token(actor_ref.link_token);
send_message_later(actor_ref.actor_info, std::move(message));
}
template <class ExecuteF, class ToMessageF>
void send_immediate(ActorRef actor_ref, ExecuteF &&execute, ToMessageF &&to_message) {
auto scheduler_context_ptr = core::SchedulerContext::get();
if (scheduler_context_ptr == nullptr) {
LOG(ERROR) << "send to actor is silently ignored";
return;
}
auto &scheduler_context = *scheduler_context_ptr;
core::ActorExecutor executor(actor_ref.actor_info, scheduler_context,
core::ActorExecutor::Options().with_has_poll(scheduler_context.has_poll()));
if (executor.can_send_immediate()) {
return executor.send_immediate(execute, actor_ref.link_token);
}
auto message = to_message();
message.set_link_token(actor_ref.link_token);
executor.send(std::move(message));
}
template <class F>
void send_lambda(ActorRef actor_ref, F &&lambda) {
send_immediate(actor_ref, lambda, [&lambda]() mutable { return ActorMessageCreator::lambda(std::move(lambda)); });
}
template <class F>
void send_lambda_later(ActorRef actor_ref, F &&lambda) {
send_message_later(actor_ref, ActorMessageCreator::lambda(std::move(lambda)));
}
template <class ClosureT>
void send_closure_impl(ActorRef actor_ref, ClosureT &&closure) {
using ActorType = typename ClosureT::ActorType;
send_immediate(
actor_ref, [&closure]() mutable { closure.run(&current_actor<ActorType>()); },
[&closure]() mutable {
return ActorMessageCreator::lambda(
[closure = to_delayed_closure(std::move(closure))]() mutable { closure.run(&current_actor<ActorType>()); });
});
}
template <class... ArgsT>
void send_closure(ActorRef actor_ref, ArgsT &&... args) {
send_closure_impl(actor_ref, create_immediate_closure(std::forward<ArgsT>(args)...));
}
template <class ClosureT>
void send_closure_later_impl(ActorRef actor_ref, ClosureT &&closure) {
using ActorType = typename ClosureT::ActorType;
send_message_later(actor_ref,
ActorMessageCreator::lambda([closure = to_delayed_closure(std::move(closure))]() mutable {
closure.run(&current_actor<ActorType>());
}));
}
template <class ClosureT, class PromiseT>
void send_closure_with_promise(ActorRef actor_ref, ClosureT &&closure, PromiseT &&promise) {
using ActorType = typename ClosureT::ActorType;
using ResultType = decltype(closure.run(&current_actor<ActorType>()));
auto &&promise_i = promise_interface<ResultType>(std::forward<PromiseT>(promise));
send_immediate(
actor_ref, [&closure, &promise = promise_i]() mutable { promise(closure.run(&current_actor<ActorType>())); },
[&closure, &promise = promise_i]() mutable {
return ActorMessageCreator::lambda(
[closure = to_delayed_closure(std::move(closure)), promise = std::move(promise)]() mutable {
promise(closure.run(&current_actor<ActorType>()));
});
});
}
template <class ClosureT, class PromiseT>
void send_closure_with_promise_later(ActorRef actor_ref, ClosureT &&closure, PromiseT &&promise) {
using ActorType = typename ClosureT::ActorType;
using ResultType = decltype(closure.run(&current_actor<ActorType>()));
send_message_later(
actor_ref,
ActorMessageCreator::lambda([closure = to_delayed_closure(std::move(closure)),
promise = promise_interface<ResultType>(std::forward<PromiseT>(promise))]() mutable {
promise(closure.run(&current_actor<ActorType>()));
}));
}
template <class... ArgsT>
void send_closure_later(ActorRef actor_ref, ArgsT &&... args) {
send_closure_later_impl(actor_ref, create_delayed_closure(std::forward<ArgsT>(args)...));
}
inline void send_signals(ActorRef actor_ref, ActorSignals signals) {
auto scheduler_context_ptr = core::SchedulerContext::get();
if (scheduler_context_ptr == nullptr) {
LOG(ERROR) << "send to actor is silently ignored";
return;
}
auto &scheduler_context = *scheduler_context_ptr;
core::ActorExecutor executor(actor_ref.actor_info, scheduler_context,
core::ActorExecutor::Options().with_has_poll(scheduler_context.has_poll()));
if (executor.can_send_immediate()) {
return executor.send_immediate(signals.raw());
}
executor.send(signals.raw());
}
inline void send_signals_later(ActorRef actor_ref, ActorSignals signals) {
auto scheduler_context_ptr = core::SchedulerContext::get();
if (scheduler_context_ptr == nullptr) {
LOG(ERROR) << "send to actor is silently ignored";
return;
}
auto &scheduler_context = *scheduler_context_ptr;
core::ActorExecutor executor(actor_ref.actor_info, scheduler_context,
core::ActorExecutor::Options().with_has_poll(scheduler_context.has_poll()));
executor.send((signals | ActorSignals::pause()).raw());
}
inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) {
auto state = actor_info_ptr->state().get_flags_unsafe();
core::SchedulerContext::get()->add_to_queue(std::move(actor_info_ptr), state.get_scheduler_id(), !state.is_shared());
}
template <class T, class... ArgsT>
core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) {
auto *scheduler_context = core::SchedulerContext::get();
if (!options.has_scheduler()) {
options.on_scheduler(scheduler_context->get_scheduler_id());
}
auto res =
scheduler_context->get_actor_info_creator().create(std::make_unique<T>(std::forward<ArgsT>(args)...), options);
register_actor_info_ptr(res);
return res;
}
} // namespace detail
} // namespace actor
} // namespace td

View file

@ -0,0 +1,108 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorInfo.h"
#include "td/utils/SharedObjectPool.h"
namespace td {
namespace actor {
namespace core {
class Actor {
public:
Actor() = default;
Actor(const Actor &) = delete;
Actor &operator=(const Actor &) = delete;
Actor(Actor &&other) = delete;
Actor &operator=(Actor &&other) = delete;
virtual ~Actor() = default;
void set_actor_info_ptr(ActorInfoPtr actor_info_ptr) {
actor_info_ptr_ = std::move(actor_info_ptr);
}
const ActorInfoPtr &get_actor_info_ptr() const {
return actor_info_ptr_;
}
td::Unit dummy() {
return td::Unit();
}
protected:
// Signal handlers
virtual void start_up() { // StartUp signal handler
yield();
}
virtual void tear_down() { // TearDown signal handler (or Kill)
//noop
}
virtual void hangup() { // HangUp signal handler
stop();
}
virtual void hangup_shared() { // HangUp signal handler
loop();
}
virtual void wake_up() { // WakeUp signal handler
loop();
}
virtual void alarm() { // Alarm signal handler
loop();
}
virtual void loop() { // default handler
//noop
}
// Useful functions
void yield() { // send wakeup signal to itself
ActorExecuteContext::get()->set_yield();
}
void stop() { // send Kill signal to itself
ActorExecuteContext::get()->set_stop();
}
Timestamp &alarm_timestamp() {
return ActorExecuteContext::get()->alarm_timestamp();
}
Timestamp get_alarm_timestamp() {
return ActorExecuteContext::get()->get_alarm_timestamp();
}
CSlice get_name() {
return actor_info_ptr_->get_name();
}
uint64 get_link_token() {
return ActorExecuteContext::get()->get_link_token();
}
//set context that will be inherited by all childrens
//void set_context(std::shared_ptr<ActorContext> context);
//void do_stop(); // process Kill signal immediately
private:
friend class ActorExecutor;
friend class ActorMessageHangup;
friend class ActorMessageHangupShared;
ActorInfoPtr actor_info_ptr_;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,101 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/Context.h"
#include "td/utils/logging.h"
#include "td/utils/Time.h"
#include <limits>
namespace td {
namespace actor {
namespace core {
class Actor;
enum : uint64 { EmptyLinkToken = std::numeric_limits<uint64>::max() };
class ActorExecuteContext : public Context<ActorExecuteContext> {
public:
explicit ActorExecuteContext(Actor *actor, Timestamp alarm_timestamp = Timestamp::never())
: actor_(actor), alarm_timestamp_(alarm_timestamp) {
}
void set_actor(Actor *actor) {
actor_ = actor;
}
Actor &actor() const {
CHECK(actor_);
return *actor_;
}
bool has_flags() const {
return flags_ != 0;
}
bool has_immediate_flags() const {
return (flags_ & ~(1 << Alarm)) != 0;
}
void set_stop() {
flags_ |= 1 << Stop;
}
bool get_stop() const {
return (flags_ & (1 << Stop)) != 0;
}
void set_pause() {
flags_ |= 1 << Pause;
}
bool get_pause() const {
return (flags_ & (1 << Pause)) != 0;
}
void clear_actor() {
actor_ = nullptr;
}
void set_link_token(uint64 link_token) {
link_token_ = link_token;
}
uint64 get_link_token() const {
return link_token_;
}
Timestamp &alarm_timestamp() {
flags_ |= 1 << Alarm;
return alarm_timestamp_;
}
bool get_alarm_flag() const {
return (flags_ & (1 << Alarm)) != 0;
}
Timestamp get_alarm_timestamp() const {
return alarm_timestamp_;
}
void set_yield() {
flags_ |= 1 << Yield;
}
bool get_yield() {
return (flags_ & (1 << Yield)) != 0;
}
private:
Actor *actor_;
uint32 flags_{0};
uint64 link_token_{EmptyLinkToken};
Timestamp alarm_timestamp_;
enum { Stop, Pause, Alarm, Yield };
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,281 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/actor/core/ActorExecutor.h"
#include "td/utils/ScopeGuard.h"
namespace td {
namespace actor {
namespace core {
void ActorExecutor::send_immediate(ActorMessage message) {
CHECK(can_send_immediate());
if (is_closed()) {
return;
}
if (message.is_big()) {
actor_info_.mailbox().reader().delay(std::move(message));
pending_signals_.add_signal(ActorSignals::Message);
actor_execute_context_.set_pause();
return;
}
actor_execute_context_.set_link_token(message.get_link_token());
message.run();
}
void ActorExecutor::send_immediate(ActorSignals signals) {
CHECK(can_send_immediate());
if (is_closed()) {
return;
}
SCOPE_EXIT {
pending_signals_.add_signals(signals);
};
while (flush_one_signal(signals) && !actor_execute_context_.has_immediate_flags()) {
}
}
void ActorExecutor::send(ActorMessage message) {
if (is_closed()) {
return;
}
if (can_send_immediate()) {
//LOG(ERROR) << "AE::send immediate";
return send_immediate(std::move(message));
}
//LOG(ERROR) << "AE::send delayed";
actor_info_.mailbox().push(std::move(message));
pending_signals_.add_signal(ActorSignals::Message);
}
void ActorExecutor::send(ActorSignals signals) {
if (is_closed()) {
return;
}
if (can_send_immediate()) {
return send_immediate(signals);
}
pending_signals_.add_signals(signals);
}
void ActorExecutor::start() {
//LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options.from_queue);
if (is_closed()) {
return;
}
ActorSignals signals;
SCOPE_EXIT {
pending_signals_.add_signals(signals);
};
if (options_.from_queue) {
signals.add_signal(ActorSignals::Pop);
}
actor_locker_.try_lock();
flags_ = actor_locker_.flags();
if (!actor_locker_.own_lock()) {
return;
}
if (!actor_locker_.can_execute()) {
CHECK(!options_.from_queue);
return;
}
signals.add_signals(flags().get_signals());
if (options_.from_queue) {
signals.clear_signal(ActorSignals::Pause);
}
flags().clear_signals();
if (flags_.is_closed()) {
return;
}
actor_execute_context_.set_actor(&actor_info_.actor());
while (flush_one_signal(signals)) {
if (actor_execute_context_.has_immediate_flags()) {
return;
}
}
while (flush_one_message()) {
if (actor_execute_context_.has_immediate_flags()) {
return;
}
}
}
void ActorExecutor::finish() {
//LOG(ERROR) << "FINISH " << actor_info_.get_name() << " " << tag("own_lock", actor_locker_.own_lock());
if (!actor_locker_.own_lock()) {
if (!pending_signals_.empty() && actor_locker_.add_signals(pending_signals_)) {
flags_ = actor_locker_.flags();
//LOG(ERROR) << "Own after finish " << actor_info_.get_name() << " " << format::as_binary(flags().raw());
} else {
//LOG(ERROR) << "DO FINISH " << actor_info_.get_name() << " " << flags();
return;
}
} else {
flags_.add_signals(pending_signals_);
}
CHECK(actor_locker_.own_lock());
if (td::unlikely(actor_execute_context_.has_flags())) {
flush_context_flags();
}
bool add_to_queue = false;
while (true) {
// Drop InQueue flag if has pop signal
// Can't delay or ignore this signal
auto signals = flags().get_signals();
if (signals.has_signal(ActorSignals::Pop)) {
signals.clear_signal(ActorSignals::Pop);
flags().set_signals(signals);
flags().set_in_queue(false);
//LOG(ERROR) << "clear in_queue " << format::as_binary(flags().raw());
}
//LOG(ERROR) << tag("in_queue", flags().is_in_queue()) << tag("has_signals", flags().has_signals());
if (flags_.is_closed()) {
// Writing to mailbox and closing actor may happen concurrently
// We must ensure that all messages in mailbox will be deleted
// Note that an ActorExecute may have to delete messages that was added by itself.
actor_info_.mailbox().clear();
} else {
// No need to add closed actor into queue.
if (flags().has_signals() && !flags().is_in_queue()) {
add_to_queue = true;
flags().set_in_queue(true);
}
}
ActorInfoPtr actor_info_ptr;
if (add_to_queue) {
actor_info_ptr = actor_info_.actor().get_actor_info_ptr();
}
if (actor_locker_.try_unlock(flags())) {
if (add_to_queue) {
dispatcher_.add_to_queue(std::move(actor_info_ptr), flags().get_scheduler_id(), !flags().is_shared());
}
break;
}
flags_ = actor_locker_.flags();
}
//LOG(ERROR) << "DO FINISH " << actor_info_.get_name() << " " << flags();
}
bool ActorExecutor::flush_one_signal(ActorSignals &signals) {
auto signal = signals.first_signal();
if (!signal) {
return false;
}
switch (signal) {
//NB: Signals will be handled in order of their value.
// For clarity it conincides with order in this switch
case ActorSignals::Pause:
actor_execute_context_.set_pause();
break;
case ActorSignals::Kill:
actor_execute_context_.set_stop();
break;
case ActorSignals::StartUp:
actor_info_.actor().start_up();
break;
case ActorSignals::Wakeup:
actor_info_.actor().wake_up();
break;
case ActorSignals::Alarm:
if (actor_execute_context_.get_alarm_timestamp() && actor_execute_context_.get_alarm_timestamp().is_in_past()) {
actor_execute_context_.alarm_timestamp() = Timestamp::never();
actor_info_.actor().alarm();
}
break;
case ActorSignals::Io:
case ActorSignals::Cpu:
LOG(FATAL) << "TODO";
break;
case ActorSignals::Message:
pending_signals_.add_signal(ActorSignals::Message);
actor_info_.mailbox().pop_all();
break;
case ActorSignals::Pop:
flags().set_in_queue(false);
break;
default:
UNREACHABLE();
}
signals.clear_signal(signal);
return true;
}
bool ActorExecutor::flush_one_message() {
auto message = actor_info_.mailbox().reader().read();
//LOG(ERROR) << "flush one message " << !!message << " " << actor_info_.get_name();
if (!message) {
pending_signals_.clear_signal(ActorSignals::Message);
return false;
}
if (message.is_big() && !options_.from_queue) {
actor_info_.mailbox().reader().delay(std::move(message));
actor_execute_context_.set_pause();
return false;
}
actor_execute_context_.set_link_token(message.get_link_token());
message.run();
return true;
}
void ActorExecutor::flush_context_flags() {
if (actor_execute_context_.get_stop()) {
if (actor_info_.get_alarm_timestamp()) {
actor_info_.set_alarm_timestamp(Timestamp::never());
dispatcher_.set_alarm_timestamp(actor_info_.actor().get_actor_info_ptr());
}
flags_.set_closed(true);
if (!flags_.get_signals().has_signal(ActorSignals::Signal::StartUp)) {
actor_info_.actor().tear_down();
}
actor_info_.destroy_actor();
} else {
if (actor_execute_context_.get_pause()) {
flags_.add_signals(ActorSignals::one(ActorSignals::Pause));
}
if (actor_execute_context_.get_yield()) {
flags_.add_signals(ActorSignals::one(ActorSignals::Wakeup));
}
if (actor_execute_context_.get_alarm_flag()) {
auto old_timestamp = actor_info_.get_alarm_timestamp();
auto new_timestamp = actor_execute_context_.get_alarm_timestamp();
if (!(old_timestamp == new_timestamp)) {
actor_info_.set_alarm_timestamp(new_timestamp);
dispatcher_.set_alarm_timestamp(actor_info_.actor().get_actor_info_ptr());
}
}
}
}
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,119 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorExecuteContext.h"
#include "td/actor/core/ActorInfo.h"
#include "td/actor/core/ActorLocker.h"
#include "td/actor/core/ActorMessage.h"
#include "td/actor/core/ActorSignals.h"
#include "td/actor/core/ActorState.h"
#include "td/actor/core/SchedulerContext.h"
#include "td/utils/format.h"
namespace td {
namespace actor {
namespace core {
class ActorExecutor {
public:
struct Options {
Options &with_from_queue() {
from_queue = true;
return *this;
}
Options &with_has_poll(bool new_has_poll) {
this->has_poll = new_has_poll;
return *this;
}
bool from_queue{false};
bool has_poll{false};
};
ActorExecutor(ActorInfo &actor_info, SchedulerDispatcher &dispatcher, Options options)
: actor_info_(actor_info), dispatcher_(dispatcher), options_(options) {
old_log_tag_ = LOG_TAG2;
LOG_TAG2 = actor_info.get_name().c_str();
start();
}
ActorExecutor(const ActorExecutor &) = delete;
ActorExecutor &operator=(const ActorExecutor &) = delete;
ActorExecutor(ActorExecutor &&other) = delete;
ActorExecutor &operator=(ActorExecutor &&other) = delete;
~ActorExecutor() {
finish();
LOG_TAG2 = old_log_tag_;
}
// our best guess if actor is closed or not
bool is_closed() {
return flags().is_closed();
}
bool can_send_immediate() {
return actor_locker_.own_lock() && !actor_execute_context_.has_immediate_flags() && actor_locker_.can_execute();
}
template <class F>
void send_immediate(F &&f, uint64 link_token) {
CHECK(can_send_immediate());
if (is_closed()) {
return;
}
actor_execute_context_.set_link_token(link_token);
f();
}
void send_immediate(ActorMessage message);
void send_immediate(ActorSignals signals);
void send(ActorMessage message);
void send(ActorSignals signals);
private:
ActorInfo &actor_info_;
SchedulerDispatcher &dispatcher_;
Options options_;
ActorLocker actor_locker_{&actor_info_.state(), ActorLocker::Options()
.with_can_execute_paused(options_.from_queue)
.with_is_shared(!options_.has_poll)
.with_scheduler_id(dispatcher_.get_scheduler_id())};
ActorExecuteContext actor_execute_context_{nullptr, actor_info_.get_alarm_timestamp()};
ActorExecuteContext::Guard guard{&actor_execute_context_};
ActorState::Flags flags_;
ActorSignals pending_signals_;
const char *old_log_tag_;
ActorState::Flags &flags() {
return flags_;
}
void start();
void finish();
bool flush_one(ActorSignals &signals);
bool flush_one_signal(ActorSignals &signals);
bool flush_one_message();
void flush_context_flags();
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,109 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorState.h"
#include "td/actor/core/ActorMailbox.h"
#include "td/utils/Heap.h"
#include "td/utils/List.h"
#include "td/utils/Time.h"
#include "td/utils/SharedObjectPool.h"
namespace td {
namespace actor {
namespace core {
class Actor;
class ActorInfo;
using ActorInfoPtr = SharedObjectPool<ActorInfo>::Ptr;
class ActorInfo : private HeapNode, private ListNode {
public:
ActorInfo(std::unique_ptr<Actor> actor, ActorState::Flags state_flags, Slice name)
: actor_(std::move(actor)), name_(name.begin(), name.size()) {
state_.set_flags_unsafe(state_flags);
VLOG(actor) << "Create actor [" << name_ << "]";
}
~ActorInfo() {
VLOG(actor) << "Destroy actor [" << name_ << "]";
}
bool is_alive() const {
return !state_.get_flags_unsafe().is_closed();
}
bool has_actor() const {
return bool(actor_);
}
Actor &actor() {
CHECK(has_actor());
return *actor_;
}
Actor *actor_ptr() const {
return actor_.get();
}
void destroy_actor() {
actor_.reset();
}
ActorState &state() {
return state_;
}
ActorMailbox &mailbox() {
return mailbox_;
}
CSlice get_name() const {
return name_;
}
HeapNode *as_heap_node() {
return this;
}
static ActorInfo *from_heap_node(HeapNode *node) {
return static_cast<ActorInfo *>(node);
}
Timestamp get_alarm_timestamp() const {
return Timestamp::at(alarm_timestamp_at_.load(std::memory_order_relaxed));
}
void set_alarm_timestamp(Timestamp timestamp) {
alarm_timestamp_at_.store(timestamp.at(), std::memory_order_relaxed);
}
void pin(ActorInfoPtr ptr) {
CHECK(pin_.empty());
CHECK(&*ptr == this);
pin_ = std::move(ptr);
}
ActorInfoPtr unpin() {
CHECK(!pin_.empty());
return std::move(pin_);
}
private:
std::unique_ptr<Actor> actor_;
ActorState state_;
ActorMailbox mailbox_;
std::string name_;
std::atomic<double> alarm_timestamp_at_{0};
ActorInfoPtr pin_;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,95 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorInfo.h"
#include "td/actor/core/Actor.h"
namespace td {
namespace actor {
namespace core {
class ActorInfoCreator {
public:
class Options {
public:
Options() = default;
Options &with_name(Slice new_name) {
name = new_name;
return *this;
}
Options &on_scheduler(SchedulerId new_scheduler_id) {
scheduler_id = new_scheduler_id;
return *this;
}
bool has_scheduler() const {
return scheduler_id.is_valid();
}
Options &with_poll(bool has_poll = true) {
is_shared = !has_poll;
return *this;
}
private:
friend class ActorInfoCreator;
Slice name;
SchedulerId scheduler_id;
bool is_shared{true};
bool in_queue{true};
//TODO: rename
};
//Create unlocked actor. One must send StartUp signal immediately.
ActorInfoPtr create(std::unique_ptr<Actor> actor, const Options &args) {
ActorState::Flags flags;
flags.set_scheduler_id(args.scheduler_id);
if (allow_shared_) {
flags.set_shared(args.is_shared);
}
flags.set_in_queue(args.in_queue);
flags.set_signals(ActorSignals::one(ActorSignals::StartUp));
auto actor_info_ptr = pool_.alloc(std::move(actor), flags, args.name);
actor_info_ptr->actor().set_actor_info_ptr(actor_info_ptr);
return actor_info_ptr;
}
ActorInfoCreator() = default;
explicit ActorInfoCreator(bool allow_shared) : allow_shared_(allow_shared) {
}
ActorInfoCreator(const ActorInfoCreator &) = delete;
ActorInfoCreator &operator=(const ActorInfoCreator &) = delete;
ActorInfoCreator(ActorInfoCreator &&other) = delete;
ActorInfoCreator &operator=(ActorInfoCreator &&other) = delete;
void clear() {
pool_.for_each([](auto &actor_info) { actor_info.destroy_actor(); });
}
~ActorInfoCreator() {
clear();
}
private:
SharedObjectPool<ActorInfo> pool_;
bool allow_shared_{true};
};
using ActorOptions = ActorInfoCreator::Options;
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,152 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorSignals.h"
#include "td/actor/core/ActorState.h"
#include "td/utils/logging.h"
#include <atomic>
namespace td {
namespace actor {
namespace core {
class ActorLocker {
public:
struct Options {
Options() {
}
bool can_execute_paused = false;
bool is_shared = true;
SchedulerId scheduler_id;
Options &with_can_execute_paused(bool new_can_execute_paused) {
can_execute_paused = new_can_execute_paused;
return *this;
}
Options &with_is_shared(bool new_is_shared) {
is_shared = new_is_shared;
return *this;
}
Options &with_scheduler_id(SchedulerId id) {
scheduler_id = id;
return *this;
}
};
explicit ActorLocker(ActorState *state, Options options = {})
: state_(state), flags_(state->get_flags_unsafe()), new_flags_{}, options_{options} {
}
bool try_lock() {
CHECK(!own_lock());
while (!can_try_add_signals()) {
new_flags_ = flags_;
new_flags_.set_locked(true);
new_flags_.clear_signals();
if (state_->state_.compare_exchange_strong(flags_.raw_ref(), new_flags_.raw(), std::memory_order_acq_rel)) {
own_lock_ = true;
return true;
}
}
return false;
}
bool try_unlock(ActorState::Flags flags) {
CHECK(!flags.is_locked());
CHECK(own_lock());
// can't unlock with signals set
//CHECK(!flags.has_signals());
flags_ = flags;
// try to unlock
if (state_->state_.compare_exchange_strong(new_flags_.raw_ref(), flags.raw(), std::memory_order_acq_rel)) {
own_lock_ = false;
return true;
}
// read all signals
flags.set_locked(true);
flags.clear_signals();
do {
flags_.add_signals(new_flags_.get_signals());
} while (!state_->state_.compare_exchange_strong(new_flags_.raw_ref(), flags.raw(), std::memory_order_acq_rel));
new_flags_ = flags;
return false;
}
bool try_add_signals(ActorSignals signals) {
CHECK(!own_lock());
CHECK(can_try_add_signals());
new_flags_ = flags_;
new_flags_.add_signals(signals);
// This is not an optimization.
// Sometimes it helps sometimes it makes things worse
// It there are a lot of threads concurrently sending signals to an actor it helps
// Buf it threre is only one thread, CAS without conficts is much cheaper than full
// barrier.
if (false && flags_.raw() == new_flags_.raw()) {
std::atomic_thread_fence(std::memory_order_seq_cst);
auto actual_flags = state_->get_flags_unsafe();
if (actual_flags.raw() == new_flags_.raw()) {
return true;
}
}
return state_->state_.compare_exchange_strong(flags_.raw_ref(), new_flags_.raw(), std::memory_order_acq_rel);
}
bool add_signals(ActorSignals signals) {
CHECK(!own_lock());
while (true) {
if (can_try_add_signals()) {
if (try_add_signals(signals)) {
return false;
}
} else {
if (try_lock()) {
flags_.add_signals(signals);
return true;
}
}
}
}
bool own_lock() const {
return own_lock_;
}
ActorState::Flags flags() const {
return flags_;
}
bool can_execute() const {
return flags_.is_shared() == options_.is_shared && flags_.get_scheduler_id() == options_.scheduler_id &&
(options_.can_execute_paused || !flags_.get_signals().has_signal(ActorSignals::Pause));
}
private:
ActorState *state_{nullptr};
ActorState::Flags flags_;
ActorState::Flags new_flags_;
bool own_lock_{false};
Options options_;
bool can_try_add_signals() const {
return flags_.is_locked() || (flags_.is_in_queue() && !can_execute());
}
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,68 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorMessage.h"
#include "td/utils/MpscLinkQueue.h"
namespace td {
namespace actor {
namespace core {
class ActorMailbox {
public:
ActorMailbox() = default;
ActorMailbox(const ActorMailbox &) = delete;
ActorMailbox &operator=(const ActorMailbox &) = delete;
ActorMailbox(ActorMailbox &&other) = delete;
ActorMailbox &operator=(ActorMailbox &&other) = delete;
~ActorMailbox() {
clear();
}
void push(ActorMessage message) {
queue_.push(std::move(message));
}
void push_unsafe(ActorMessage message) {
queue_.push_unsafe(std::move(message));
}
td::MpscLinkQueue<ActorMessage>::Reader &reader() {
return reader_;
}
void pop_all() {
queue_.pop_all(reader_);
}
void pop_all_unsafe() {
queue_.pop_all_unsafe(reader_);
}
void clear() {
pop_all();
while (reader_.read()) {
// skip
}
}
private:
td::MpscLinkQueue<ActorMessage> queue_;
td::MpscLinkQueue<ActorMessage>::Reader reader_;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,97 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorExecuteContext.h"
#include "td/utils/MpscLinkQueue.h"
namespace td {
namespace actor {
namespace core {
class ActorMessageImpl : private MpscLinkQueueImpl::Node {
public:
ActorMessageImpl() = default;
ActorMessageImpl(const ActorMessageImpl &) = delete;
ActorMessageImpl &operator=(const ActorMessageImpl &) = delete;
ActorMessageImpl(ActorMessageImpl &&other) = delete;
ActorMessageImpl &operator=(ActorMessageImpl &&other) = delete;
virtual ~ActorMessageImpl() = default;
virtual void run() = 0;
private:
friend class ActorMessage;
// ActorMessage <--> MpscLintQueue::Node
// Each actor's mailbox will be a queue
static ActorMessageImpl *from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
return static_cast<ActorMessageImpl *>(node);
}
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
return static_cast<MpscLinkQueueImpl::Node *>(this);
}
uint64 link_token_{EmptyLinkToken};
bool is_big_{false};
};
class ActorMessage {
public:
ActorMessage() = default;
explicit ActorMessage(std::unique_ptr<ActorMessageImpl> impl) : impl_(std::move(impl)) {
}
void run() {
CHECK(impl_);
impl_->run();
}
explicit operator bool() {
return bool(impl_);
}
friend class ActorMailbox;
void set_link_token(uint64 link_token) {
impl_->link_token_ = link_token;
}
uint64 get_link_token() const {
return impl_->link_token_;
}
bool is_big() const {
return impl_->is_big_;
}
void set_big() {
impl_->is_big_ = true;
}
private:
std::unique_ptr<ActorMessageImpl> impl_;
template <class T>
friend class td::MpscLinkQueue;
static ActorMessage from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
return ActorMessage(std::unique_ptr<ActorMessageImpl>(ActorMessageImpl::from_mpsc_link_queue_node(node)));
}
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
return impl_.release()->to_mpsc_link_queue_node();
}
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,118 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/bits.h"
#include "td/utils/format.h"
namespace td {
namespace actor {
namespace core {
class ActorSignals {
public:
ActorSignals() = default;
uint32 raw() const {
return raw_;
}
static ActorSignals create_raw(uint32 raw) {
return ActorSignals(raw);
}
bool empty() const {
return raw_ == 0;
}
bool has_signal(uint32 signal) const {
return (raw_ & (1u << signal)) != 0;
}
void add_signal(uint32 signal) {
raw_ |= (1u << signal);
}
void add_signals(ActorSignals signals) {
raw_ |= signals.raw();
}
void clear_signal(uint32 signal) {
raw_ &= ~(1u << signal);
}
uint32 first_signal() {
if (!raw_) {
return 0;
}
return td::count_trailing_zeroes_non_zero32(raw_);
}
friend StringBuilder &operator<<(StringBuilder &sb, ActorSignals signals) {
sb << "S{";
bool was = false;
auto add_signal = [&](int signal, auto name) {
if (signals.has_signal(signal)) {
if (was) {
sb << ",";
} else {
was = true;
}
sb << name;
}
};
add_signal(Wakeup, "Wakeup");
add_signal(Alarm, "Alarm");
add_signal(Kill, "Kill");
add_signal(Io, "Io");
add_signal(Cpu, "Cpu");
add_signal(StartUp, "StartUp");
add_signal(Pop, "Pop");
add_signal(Message, "Message");
add_signal(Pause, "Pause");
sb << "}";
return sb;
}
enum Signal : uint32 {
// Signals in order of priority
Pause = 1,
Kill = 2, // immediate kill
StartUp = 3,
Wakeup = 4,
Alarm = 5,
Io = 6, // move to io thread
Cpu = 7, // move to cpu thread
// Two signals for mpmc queue logic
//
// PopSignal is set after actor is popped from queue
// When processed it should set InQueue and Pause flags to false.
//
// MessagesSignal is set after new messages was added to actor
// If owner of actor wish to delay message handling, she should set InQueue flag to true and
// add actor into mpmc queue.
Pop = 8, // got popped from queue
Message = 9, // got new message
};
static ActorSignals one(uint32 signal) {
ActorSignals res;
res.add_signal(signal);
return res;
}
private:
uint32 raw_{0};
friend class ActorState;
explicit ActorSignals(uint32 raw) : raw_(raw) {
}
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,181 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorSignals.h"
#include "td/actor/core/SchedulerId.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include <atomic>
namespace td {
namespace actor {
namespace core {
class ActorState {
public:
class Flags {
public:
Flags() = default;
uint32 raw() const {
return raw_;
}
uint32 &raw_ref() {
return raw_;
}
SchedulerId get_scheduler_id() const {
return SchedulerId{static_cast<uint8>(raw_ & SchedulerMask)};
}
void set_scheduler_id(SchedulerId id) {
raw_ = (raw_ & ~SchedulerMask) | id.value();
}
bool is_shared() const {
return check_flag(SharedFlag);
}
void set_shared(bool shared) {
set_flag(SharedFlag, shared);
}
bool is_locked() const {
return check_flag(LockFlag);
}
void set_locked(bool locked) {
set_flag(LockFlag, locked);
}
bool is_migrate() const {
return check_flag(MigrateFlag);
}
void set_migrate(bool migrate) {
set_flag(MigrateFlag, migrate);
}
bool is_closed() const {
return check_flag(ClosedFlag);
}
void set_closed(bool closed) {
set_flag(ClosedFlag, closed);
}
bool is_in_queue() const {
return check_flag(InQueueFlag);
}
void set_in_queue(bool in_queue) {
set_flag(InQueueFlag, in_queue);
}
bool has_signals() const {
return check_flag(SignalMask);
}
void clear_signals() {
set_flag(SignalMask, false);
}
void set_signals(ActorSignals signals) {
raw_ = (raw_ & ~SignalMask) | (signals.raw() << SignalOffset);
}
void add_signals(ActorSignals signals) {
raw_ = raw_ | (signals.raw() << SignalOffset);
}
ActorSignals get_signals() const {
return ActorSignals{(raw_ & SignalMask) >> SignalOffset};
}
friend StringBuilder &operator<<(StringBuilder &sb, Flags flags) {
sb << "ActorFlags{" << flags.get_scheduler_id().value() << ", " << (flags.is_shared() ? "cpu " : "io ")
<< (flags.is_migrate() ? "migrate " : "") << (flags.is_closed() ? "closed " : "")
<< (flags.is_in_queue() ? "in_queue " : "") << flags.get_signals() << "}";
return sb;
}
private:
uint32 raw_{0};
friend class ActorState;
Flags(uint32 raw) : raw_(raw) {
}
bool check_flag(uint32 mask) const {
return (raw_ & mask) != 0;
}
void set_flag(uint32 mask, bool flag) {
raw_ = (raw_ & ~mask) | (flag * mask);
}
};
Flags get_flags_unsafe() const {
return Flags(state_.load(std::memory_order_relaxed));
}
void set_flags_unsafe(Flags flags) {
state_.store(flags.raw(), std::memory_order_relaxed);
}
private:
friend class ActorLocker;
std::atomic<uint32> state_{0};
enum : uint32 {
SchedulerMask = 255,
// Actors can be shared or not.
// If actor is shared, than any thread may try to lock it
// If actor is not shared, than it is owned by its scheduler, and only
// its scheduler is allowed to access it
// This flag may NOT change during the lifetime of an actor
SharedFlag = 1 << 9,
// Only shared actors need lock
// Lock if somebody is going to unlock it eventually.
// For example actor is locked, when some scheduler is executing its mailbox
// Or it is locked when it is in Mpmc queue, so someone will pop it eventually.
LockFlag = 1 << 10,
// While actor is migrating from one scheduler to another no one is allowed to change it
// Could not be set for shared actors.
MigrateFlag = 1 << 11,
// While set all messages are delayed
// Dropped from flush_maibox
// PauseFlag => InQueueFlag
PauseFlag = 1 << 12,
ClosedFlag = 1 << 13,
InQueueFlag = 1 << 14,
// Signals
SignalOffset = 15,
Signal = 1 << SignalOffset,
WakeupSignalFlag = Signal << ActorSignals::Wakeup,
AlarmSignalFlag = Signal << ActorSignals::Alarm,
KillSignalFlag = Signal << ActorSignals::Kill, // immediate kill
IoSignalFlag = Signal << ActorSignals::Io, // move to io thread
CpuSignalFlag = Signal << ActorSignals::Cpu, // move to cpu thread
StartUpSignalFlag = Signal << ActorSignals::StartUp,
MessageSignalFlag = Signal << ActorSignals::Message,
PopSignalFlag = Signal << ActorSignals::Pop,
PauseSignalFlag = Signal << ActorSignals::Pause,
SignalMask = WakeupSignalFlag | AlarmSignalFlag | KillSignalFlag | IoSignalFlag | CpuSignalFlag |
StartUpSignalFlag | MessageSignalFlag | PopSignalFlag | PauseSignalFlag
};
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,59 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/thread_local.h"
namespace td {
namespace actor {
namespace core {
template <class Impl>
class Context {
public:
static Impl *get() {
return context_;
}
class Guard {
public:
explicit Guard(Impl *new_context) {
old_context_ = context_;
context_ = new_context;
}
~Guard() {
context_ = old_context_;
}
Guard(const Guard &) = delete;
Guard &operator=(const Guard &) = delete;
Guard(Guard &&) = delete;
Guard &operator=(Guard &&) = delete;
private:
Impl *old_context_;
};
private:
static TD_THREAD_LOCAL Impl *context_;
};
template <class Impl>
TD_THREAD_LOCAL Impl *Context<Impl>::context_;
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,47 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/actor/core/CpuWorker.h"
#include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/SchedulerContext.h"
namespace td {
namespace actor {
namespace core {
void CpuWorker::run() {
auto thread_id = get_thread_id();
auto &dispatcher = *SchedulerContext::get();
int yields = 0;
while (true) {
SchedulerMessage message;
if (queue_.try_pop(message, thread_id)) {
if (!message) {
return;
}
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue());
yields = waiter_.stop_wait(yields, thread_id);
} else {
yields = waiter_.wait(yields, thread_id);
}
}
}
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,41 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/SchedulerMessage.h"
#include "td/utils/MpmcQueue.h"
#include "td/utils/MpmcWaiter.h"
namespace td {
namespace actor {
namespace core {
class CpuWorker {
public:
CpuWorker(MpmcQueue<SchedulerMessage> &queue, MpmcWaiter &waiter) : queue_(queue), waiter_(waiter) {
}
void run();
private:
MpmcQueue<SchedulerMessage> &queue_;
MpmcWaiter &waiter_;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,100 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/actor/core/IoWorker.h"
#include "td/actor/core/ActorExecutor.h"
namespace td {
namespace actor {
namespace core {
void IoWorker::start_up() {
#if TD_PORT_POSIX
auto &poll = SchedulerContext::get()->get_poll();
poll.subscribe(queue_.reader_get_event_fd().get_poll_info().extract_pollable_fd(nullptr), PollFlags::Read());
#endif
}
void IoWorker::tear_down() {
#if TD_PORT_POSIX
auto &poll = SchedulerContext::get()->get_poll();
poll.unsubscribe(queue_.reader_get_event_fd().get_poll_info().get_pollable_fd_ref());
#endif
}
bool IoWorker::run_once(double timeout) {
auto &dispatcher = *SchedulerContext::get();
#if TD_PORT_POSIX
auto &poll = SchedulerContext::get()->get_poll();
#endif
auto &heap = SchedulerContext::get()->get_heap();
auto now = Time::now(); // update Time::now_cached()
while (!heap.empty() && heap.top_key() <= now) {
auto *heap_node = heap.pop();
auto *actor_info = ActorInfo::from_heap_node(heap_node);
auto id = actor_info->unpin();
ActorExecutor executor(*actor_info, dispatcher, ActorExecutor::Options().with_has_poll(true));
if (executor.can_send_immediate()) {
executor.send_immediate(ActorSignals::one(ActorSignals::Alarm));
} else {
executor.send(ActorSignals::one(ActorSignals::Alarm));
}
}
const int size = queue_.reader_wait_nonblock();
for (int i = 0; i < size; i++) {
auto message = queue_.reader_get_unsafe();
if (!message) {
return false;
}
if (message->state().get_flags_unsafe().is_shared()) {
// should check actors timeout
dispatcher.set_alarm_timestamp(message);
continue;
}
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue().with_has_poll(true));
}
queue_.reader_flush();
bool can_sleep = size == 0 && timeout != 0;
int32 timeout_ms = 0;
if (can_sleep) {
auto wakeup_timestamp = Timestamp::in(timeout);
if (!heap.empty()) {
wakeup_timestamp.relax(Timestamp::at(heap.top_key()));
}
timeout_ms = static_cast<int>(wakeup_timestamp.in() * 1000) + 1;
if (timeout_ms < 0) {
timeout_ms = 0;
}
//const int thirty_seconds = 30 * 1000;
//if (timeout_ms > thirty_seconds) {
//timeout_ms = thirty_seconds;
//}
}
#if TD_PORT_POSIX
poll.run(timeout_ms);
#elif TD_PORT_WINDOWS
queue_.reader_get_event_fd().wait(timeout_ms);
#endif
return true;
}
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,43 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/SchedulerContext.h"
#include "td/actor/core/SchedulerMessage.h"
#include "td/utils/MpscPollableQueue.h"
namespace td {
namespace actor {
namespace core {
class IoWorker {
public:
explicit IoWorker(MpscPollableQueue<SchedulerMessage> &queue) : queue_(queue) {
}
void start_up();
void tear_down();
bool run_once(double timeout);
private:
MpscPollableQueue<SchedulerMessage> &queue_;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,288 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/actor/core/Scheduler.h"
#include "td/actor/core/CpuWorker.h"
#include "td/actor/core/IoWorker.h"
namespace td {
namespace actor {
namespace core {
Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count)
: scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) {
scheduler_group_info_->active_scheduler_count++;
info_ = &scheduler_group_info_->schedulers.at(id.value());
info_->id = id;
if (cpu_threads_count != 0) {
info_->cpu_threads_count = cpu_threads_count;
info_->cpu_queue = std::make_unique<MpmcQueue<SchedulerMessage>>(1024, max_thread_count());
info_->cpu_queue_waiter = std::make_unique<MpmcWaiter>();
}
info_->io_queue = std::make_unique<MpscPollableQueue<SchedulerMessage>>();
info_->io_queue->init();
info_->cpu_workers.resize(cpu_threads_count);
for (auto &worker : info_->cpu_workers) {
worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Cpu, true);
}
info_->io_worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Io, !info_->cpu_workers.empty());
poll_.init();
io_worker_ = std::make_unique<IoWorker>(*info_->io_queue);
#if TD_PORT_WINDOWS
if (info_->id.value() == 0) {
scheduler_group_info_->iocp.init();
}
#endif
}
Scheduler::~Scheduler() {
// should stop
stop();
do_stop();
}
void Scheduler::start() {
for (size_t i = 0; i < cpu_threads_.size(); i++) {
cpu_threads_[i] = td::thread([this, i] {
this->run_in_context_impl(*this->info_->cpu_workers[i],
[this] { CpuWorker(*info_->cpu_queue, *info_->cpu_queue_waiter).run(); });
});
cpu_threads_[i].set_name(PSLICE() << "#" << info_->id.value() << ":cpu#" << i);
}
#if TD_PORT_WINDOWS
// FIXME: use scheduler_id
if (info_->id.value() == 0) {
scheduler_group_info_->iocp_thread = td::thread([this] {
WorkerInfo info;
info.type = WorkerInfo::Type::Cpu;
this->run_in_context_impl(info, [this] { scheduler_group_info_->iocp.loop(); });
});
}
#endif
this->run_in_context([this] { this->io_worker_->start_up(); });
}
bool Scheduler::run(double timeout) {
bool res;
run_in_context_impl(*info_->io_worker, [this, timeout, &res] {
if (SchedulerContext::get()->is_stop_requested()) {
res = false;
} else {
res = io_worker_->run_once(timeout);
}
if (!res) {
if (!is_stopped_) {
io_worker_->tear_down();
}
}
});
if (!res) {
do_stop();
}
return res;
}
void Scheduler::do_stop() {
if (is_stopped_) {
return;
}
// wait other threads to finish
for (auto &thread : cpu_threads_) {
thread.join();
}
// Can't do anything else, other schedulers may send queries to this one.
// Must wait till every scheduler is stopped first..
is_stopped_ = true;
io_worker_.reset();
poll_.clear();
heap_.for_each([](auto &key, auto &node) { ActorInfo::from_heap_node(node)->unpin(); });
std::unique_lock<std::mutex> lock(scheduler_group_info_->active_scheduler_count_mutex);
scheduler_group_info_->active_scheduler_count--;
scheduler_group_info_->active_scheduler_count_condition_variable.notify_all();
}
Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap)
: creator_(creator), scheduler_id_(scheduler_id), scheduler_group_(scheduler_group), poll_(poll), heap_(heap) {
}
SchedulerId Scheduler::ContextImpl::get_scheduler_id() const {
return scheduler_id_;
}
void Scheduler::ContextImpl::add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) {
if (!scheduler_id.is_valid()) {
scheduler_id = get_scheduler_id();
}
auto &info = scheduler_group()->schedulers.at(scheduler_id.value());
if (need_poll || !info.cpu_queue) {
info.io_queue->writer_put(std::move(actor_info_ptr));
} else {
info.cpu_queue->push(std::move(actor_info_ptr), get_thread_id());
info.cpu_queue_waiter->notify();
}
}
ActorInfoCreator &Scheduler::ContextImpl::get_actor_info_creator() {
return *creator_;
}
bool Scheduler::ContextImpl::has_poll() {
return poll_ != nullptr;
}
Poll &Scheduler::ContextImpl::get_poll() {
CHECK(has_poll());
return *poll_;
}
bool Scheduler::ContextImpl::has_heap() {
return heap_ != nullptr;
}
KHeap<double> &Scheduler::ContextImpl::get_heap() {
CHECK(has_heap());
return *heap_;
}
void Scheduler::ContextImpl::set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) {
// Ideas for optimization
// 1. Several cpu actors with separate heaps. They ask io worker to update timeout only when it has been changed
// 2. Update timeout only when it has increased
// 3. Use signal-like logic to combile multiple timeout updates into one
if (!has_heap()) {
add_to_queue(actor_info_ptr, {}, true);
return;
}
// we are in PollWorker
CHECK(has_heap());
auto &heap = get_heap();
auto *heap_node = actor_info_ptr->as_heap_node();
auto timestamp = actor_info_ptr->get_alarm_timestamp();
if (timestamp) {
if (heap_node->in_heap()) {
heap.fix(timestamp.at(), heap_node);
} else {
actor_info_ptr->pin(actor_info_ptr);
heap.insert(timestamp.at(), heap_node);
}
} else {
if (heap_node->in_heap()) {
actor_info_ptr->unpin();
heap.erase(heap_node);
}
}
}
bool Scheduler::ContextImpl::is_stop_requested() {
return scheduler_group()->is_stop_requested;
}
void Scheduler::ContextImpl::stop() {
bool expect_false = false;
// Trying to set close_flag_ to true with CAS
auto &group = *scheduler_group();
if (!group.is_stop_requested.compare_exchange_strong(expect_false, true)) {
return;
}
// Notify all workers of all schedulers
for (auto &scheduler_info : group.schedulers) {
scheduler_info.io_queue->writer_put({});
for (size_t i = 0; i < scheduler_info.cpu_threads_count; i++) {
scheduler_info.cpu_queue->push({}, get_thread_id());
scheduler_info.cpu_queue_waiter->notify();
}
}
}
void Scheduler::close_scheduler_group(SchedulerGroupInfo &group_info) {
//LOG(DEBUG) << "close scheduler group";
// Cannot close scheduler group before somebody asked to stop them
CHECK(group_info.is_stop_requested);
{
std::unique_lock<std::mutex> lock(group_info.active_scheduler_count_mutex);
group_info.active_scheduler_count_condition_variable.wait(lock,
[&] { return group_info.active_scheduler_count == 0; });
}
//FIXME
//ContextImpl context(&group_info.schedulers[0].io_worker->actor_info_creator, SchedulerId{0}, &group_info, nullptr,
// nullptr);
//SchedulerContext::Guard guard(&context);
#if TD_PORT_WINDOWS
detail::Iocp::Guard iocp_guard(&group_info.iocp);
group_info.iocp.interrupt_loop();
group_info.iocp_thread.join();
#endif
// Drain all queues
int it = 0;
for (bool queues_are_empty = false; !queues_are_empty;) {
queues_are_empty = true;
for (auto &scheduler_info : group_info.schedulers) {
// Drain io queue
auto &io_queue = *scheduler_info.io_queue;
while (true) {
int n = io_queue.reader_wait_nonblock();
if (n == 0) {
break;
}
while (n-- > 0) {
auto message = io_queue.reader_get_unsafe();
// message's destructor is called
queues_are_empty = false;
}
}
// Drain cpu queue
if (scheduler_info.cpu_queue) {
auto &cpu_queue = *scheduler_info.cpu_queue;
while (true) {
SchedulerMessage message;
if (!cpu_queue.try_pop(message, get_thread_id())) {
break;
}
// message's destructor is called
queues_are_empty = false;
}
}
}
if (++it > 100) {
LOG(FATAL) << "Failed to drain all queues";
}
}
LOG_IF(ERROR, it > 2) << "It took more than one iteration to drain queues";
// Just to destroy all elements should be ok.
for (auto &scheduler_info : group_info.schedulers) {
scheduler_info.io_queue.reset();
scheduler_info.cpu_queue.reset();
// Do not destroy worker infos. run_in_context will crash if they are empty
scheduler_info.io_worker->actor_info_creator.clear();
for (auto &worker : scheduler_info.cpu_workers) {
worker->actor_info_creator.clear();
}
}
}
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,223 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorExecuteContext.h"
#include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/Actor.h"
#include "td/actor/core/ActorInfo.h"
#include "td/actor/core/ActorInfoCreator.h"
#include "td/actor/core/ActorLocker.h"
#include "td/actor/core/ActorMailbox.h"
#include "td/actor/core/ActorMessage.h"
#include "td/actor/core/Context.h"
#include "td/actor/core/SchedulerContext.h"
#include "td/actor/core/SchedulerId.h"
#include "td/actor/core/SchedulerMessage.h"
#include "td/utils/Closure.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/Heap.h"
#include "td/utils/List.h"
#include "td/utils/logging.h"
#include "td/utils/MpmcQueue.h"
#include "td/utils/MpmcWaiter.h"
#include "td/utils/MpscLinkQueue.h"
#include "td/utils/MpscPollableQueue.h"
#include "td/utils/port/Poll.h"
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/port/thread.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include "td/utils/Time.h"
#include "td/utils/type_traits.h"
#include <atomic>
#include <condition_variable>
#include <limits>
#include <memory>
#include <mutex>
#include <type_traits>
#include <utility>
namespace td {
namespace actor {
namespace core {
class IoWorker;
struct WorkerInfo {
enum class Type { Io, Cpu } type{Type::Io};
WorkerInfo() = default;
explicit WorkerInfo(Type type, bool allow_shared) : type(type), actor_info_creator(allow_shared) {
}
ActorInfoCreator actor_info_creator;
};
struct SchedulerInfo {
SchedulerId id;
// will be read by all workers is any thread
std::unique_ptr<MpmcQueue<SchedulerMessage>> cpu_queue;
std::unique_ptr<MpmcWaiter> cpu_queue_waiter;
// only scheduler itself may read from io_queue_
std::unique_ptr<MpscPollableQueue<SchedulerMessage>> io_queue;
size_t cpu_threads_count{0};
std::unique_ptr<WorkerInfo> io_worker;
std::vector<std::unique_ptr<WorkerInfo>> cpu_workers;
};
struct SchedulerGroupInfo {
explicit SchedulerGroupInfo(size_t n) : schedulers(n) {
}
std::atomic<bool> is_stop_requested{false};
int active_scheduler_count{0};
std::mutex active_scheduler_count_mutex;
std::condition_variable active_scheduler_count_condition_variable;
#if TD_PORT_WINDOWS
td::detail::Iocp iocp;
td::thread iocp_thread;
#endif
std::vector<SchedulerInfo> schedulers;
};
class Scheduler {
public:
static constexpr int32 max_thread_count() {
return 256;
}
static int32 get_thread_id() {
auto thread_id = ::td::get_thread_id();
CHECK(thread_id < max_thread_count());
return thread_id;
}
Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count);
Scheduler(const Scheduler &) = delete;
Scheduler &operator=(const Scheduler &) = delete;
Scheduler(Scheduler &&other) = delete;
Scheduler &operator=(Scheduler &&other) = delete;
~Scheduler();
void start();
template <class F>
void run_in_context(F &&f) {
run_in_context_impl(*info_->io_worker, std::forward<F>(f));
}
template <class F>
void run_in_context_external(F &&f) {
WorkerInfo info;
info.type = WorkerInfo::Type::Cpu;
run_in_context_impl(*info_->io_worker, std::forward<F>(f));
}
bool run(double timeout);
// Just syntactic sugar
void stop() {
run_in_context([] { SchedulerContext::get()->stop(); });
}
SchedulerId get_scheduler_id() const {
return info_->id;
}
private:
std::shared_ptr<SchedulerGroupInfo> scheduler_group_info_;
SchedulerInfo *info_;
std::vector<td::thread> cpu_threads_;
bool is_stopped_{false};
Poll poll_;
KHeap<double> heap_;
std::unique_ptr<IoWorker> io_worker_;
class ContextImpl : public SchedulerContext {
public:
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, SchedulerGroupInfo *scheduler_group, Poll *poll,
KHeap<double> *heap);
SchedulerId get_scheduler_id() const override;
void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override;
ActorInfoCreator &get_actor_info_creator() override;
bool has_poll() override;
Poll &get_poll() override;
bool has_heap() override;
KHeap<double> &get_heap() override;
void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) override;
bool is_stop_requested() override;
void stop() override;
private:
SchedulerGroupInfo *scheduler_group() const {
return scheduler_group_;
}
ActorInfoCreator *creator_;
SchedulerId scheduler_id_;
SchedulerGroupInfo *scheduler_group_;
Poll *poll_;
KHeap<double> *heap_;
};
template <class F>
void run_in_context_impl(WorkerInfo &worker_info, F &&f) {
#if TD_PORT_WINDOWS
td::detail::Iocp::Guard iocp_guard(&scheduler_group_info_->iocp);
#endif
bool is_io_worker = worker_info.type == WorkerInfo::Type::Io;
ContextImpl context(&worker_info.actor_info_creator, info_->id, scheduler_group_info_.get(),
is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr);
SchedulerContext::Guard guard(&context);
f();
}
void do_stop();
public:
static void close_scheduler_group(SchedulerGroupInfo &group_info);
};
// Actor messages
class ActorMessageHangup : public core::ActorMessageImpl {
public:
void run() override {
ActorExecuteContext::get()->actor().hangup();
}
};
class ActorMessageHangupShared : public core::ActorMessageImpl {
public:
void run() override {
ActorExecuteContext::get()->actor().hangup_shared();
}
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,61 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/Context.h"
#include "td/actor/core/SchedulerId.h"
#include "td/actor/core/ActorInfo.h"
#include "td/actor/core/ActorInfoCreator.h"
#include "td/utils/port/Poll.h"
#include "td/utils/Heap.h"
namespace td {
namespace actor {
namespace core {
class SchedulerDispatcher {
public:
virtual ~SchedulerDispatcher() = default;
virtual SchedulerId get_scheduler_id() const = 0;
virtual void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) = 0;
virtual void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) = 0;
};
class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispatcher {
public:
virtual ~SchedulerContext() = default;
// ActorCreator Interface
virtual ActorInfoCreator &get_actor_info_creator() = 0;
// Poll interface
virtual bool has_poll() = 0;
virtual Poll &get_poll() = 0;
// Timeout interface
virtual bool has_heap() = 0;
virtual KHeap<double> &get_heap() = 0;
// Stop all schedulers
virtual bool is_stop_requested() = 0;
virtual void stop() = 0;
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,49 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
namespace td {
namespace actor {
namespace core {
class SchedulerId {
public:
SchedulerId() : id_(-1) {
}
explicit SchedulerId(uint8 id) : id_(id) {
}
bool is_valid() const {
return id_ >= 0;
}
uint8 value() const {
CHECK(is_valid());
return static_cast<uint8>(id_);
}
bool operator==(SchedulerId scheduler_id) const {
return id_ == scheduler_id.id_;
}
private:
int32 id_{0};
};
} // namespace core
} // namespace actor
} // namespace td

View file

@ -0,0 +1,29 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/core/ActorInfo.h"
namespace td {
namespace actor {
namespace core {
using SchedulerMessage = ActorInfoPtr;
} // namespace core
} // namespace actor
} // namespace td